diff --git a/.circleci/config.yml b/.circleci/config.yml
index 749402c7f..92afe9183 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -3,10 +3,10 @@ version: 2.1
 executors:
   node-browsers:
     docker:
-      - image: circleci/node:14-browsers
+      - image: circleci/node:16-browsers
   node-browsers-medium-plus:
     docker:
-      - image: circleci/node:14-browsers
+      - image: circleci/node:16-browsers
     resource_class: medium+
     environment:
       NODE_OPTIONS: --max_old_space_size=2048
diff --git a/.circleci/scripts/chrome-install.sh b/.circleci/scripts/chrome-install.sh
index f97d0486d..dcd122fb8 100755
--- a/.circleci/scripts/chrome-install.sh
+++ b/.circleci/scripts/chrome-install.sh
@@ -5,12 +5,12 @@ set -u
 set -o pipefail
 
 # To get the latest version, see <https://www.ubuntuupdates.org/ppa/google_chrome?dist=stable>
-CHROME_VERSION='102.0.5005.61-1'
+CHROME_VERSION='103.0.5060.53-1'
 CHROME_BINARY="google-chrome-stable_${CHROME_VERSION}_amd64.deb"
 CHROME_BINARY_URL="https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/${CHROME_BINARY}"
 
 # To retrieve this checksum, run the `wget` and `shasum` commands below
-CHROME_BINARY_SHA512SUM='dd701b99febf7d927657f38716d90f3a0b967ae75dac5f6e8fbf9df632c8a531ccb9f37ee09340ad730b4fe40d0564c1b64201121d2d3e4e503f3f167ca632cd'
+CHROME_BINARY_SHA512SUM='36f4e79f46cb71c1431dccf1489f5f8e89d35204a717a4618c7f6f638123ddc2b37bd5cbd00498be8f84c7713149f2faa447cb6da3518be1cb9703e99d110e1a'
 
 wget -O "${CHROME_BINARY}" -t 5 "${CHROME_BINARY_URL}"
 
diff --git a/.circleci/scripts/firefox-install.sh b/.circleci/scripts/firefox-install.sh
index 0c3512114..f2f9f284d 100755
--- a/.circleci/scripts/firefox-install.sh
+++ b/.circleci/scripts/firefox-install.sh
@@ -4,7 +4,7 @@ set -e
 set -u
 set -o pipefail
 
-FIREFOX_VERSION='83.0'
+FIREFOX_VERSION='102.0'
 FIREFOX_BINARY="firefox-${FIREFOX_VERSION}.tar.bz2"
 FIREFOX_BINARY_URL="https://ftp.mozilla.org/pub/firefox/releases/${FIREFOX_VERSION}/linux-x86_64/en-US/${FIREFOX_BINARY}"
 FIREFOX_PATH='/opt/firefox'
diff --git a/.metamaskrc.dist b/.metamaskrc.dist
index d3bff3b46..8ab56bf00 100644
--- a/.metamaskrc.dist
+++ b/.metamaskrc.dist
@@ -6,6 +6,7 @@ ONBOARDING_V2=
 SWAPS_USE_DEV_APIS=
 COLLECTIBLES_V1=
 TOKEN_DETECTION_V2=
+ADD_POPULAR_NETWORKS=
 
 ; Set this to test changes to the phishing warning page.
 PHISHING_WARNING_PAGE_URL=
diff --git a/.nvmrc b/.nvmrc
index 958b5a36e..6f7f377bf 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-v14
+v16
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7895a826..06e058fbf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+## [10.18.0]
+### Added
+- Add setApprovalForAll confirmation view so granted permissions are displayed in a digested manner, instead of a simple contract interaction([#15010](https://github.com/MetaMask/metamask-extension/pull/15010)) 
+- Add warning when performing a Send directly to a token contract([#13588](https://github.com/MetaMask/metamask-extension/pull/13588))
+
+### Changed
+- Update Optimism ChainID from Kovan to Goerli ([#15119](https://github.com/MetaMask/metamask-extension/pull/15119))
+
+### Fixed
+- Fix one of the possible causes for "Sending to a random cached address", by removing the global transaction state from the Send flow ([#14777](https://github.com/MetaMask/metamask-extension/pull/14777))
+- Fix Chinese translation for the message of Importing repeated tokens ([#14994](https://github.com/MetaMask/metamask-extension/pull/14994))
+- Fix Japanese translation for the word Sign ([#15078](https://github.com/MetaMask/metamask-extension/pull/15078))
+- Fix partially the error "Seedphrase is invalid" by disabling Seedphrase Import button after switching the Seedphrase length ([#15139](https://github.com/MetaMask/metamask-extension/pull/15139))
+- Fix Edit Transaction flow by ensuring that changing a tx from a Transfer to a Send resets data and updates tx type ([#15248](https://github.com/MetaMask/metamask-extension/pull/15248))
+- Fix UI on Import Seedphrase page by disabling Import button, if any of the characters of the Seedphrase is in uppercase ([#15186](https://github.com/MetaMask/metamask-extension/pull/15186))
+
 ## [10.17.0]
 ### Added
 - Add cost estimation for canceling a Smart Transaction on Awaiting Swap page ([#15011](https://github.com/MetaMask/metamask-extension/pull/15011))
@@ -3068,7 +3084,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Uncategorized
 - Added the ability to restore accounts from seed words.
 
-[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.17.0...HEAD
+[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.18.0...HEAD
+[10.18.0]: https://github.com/MetaMask/metamask-extension/compare/v10.17.0...v10.18.0
 [10.17.0]: https://github.com/MetaMask/metamask-extension/compare/v10.16.2...v10.17.0
 [10.16.2]: https://github.com/MetaMask/metamask-extension/compare/v10.16.1...v10.16.2
 [10.16.1]: https://github.com/MetaMask/metamask-extension/compare/v10.16.0...v10.16.1
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 0f6747b4b..4e3a7ec46 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -157,6 +157,9 @@
   "addMemo": {
     "message": "Add memo"
   },
+  "addMoreNetworks": {
+    "message": "add more networks manually"
+  },
   "addNetwork": {
     "message": "Add Network"
   },
@@ -227,6 +230,10 @@
   "alerts": {
     "message": "Alerts"
   },
+  "allOfYour": {
+    "message": "All of your $1",
+    "description": "$1 is the symbol or name of the token that the user is approving spending"
+  },
   "allowExternalExtensionTo": {
     "message": "Allow this external extension to:"
   },
@@ -263,6 +270,10 @@
   "approve": {
     "message": "Approve spend limit"
   },
+  "approveAllTokensTitle": {
+    "message": "Give permission to access all of your $1?",
+    "description": "$1 is the symbol of the token for which the user is granting approval"
+  },
   "approveAndInstall": {
     "message": "Approve & Install"
   },
@@ -1284,6 +1295,9 @@
   "functionApprove": {
     "message": "Function: Approve"
   },
+  "functionSetApprovalForAll": {
+    "message": "Function: SetApprovalForAll"
+  },
   "functionType": {
     "message": "Function Type"
   },
@@ -1954,6 +1968,9 @@
   "network": {
     "message": "Network:"
   },
+  "networkAddedSuccessfully": {
+    "message": "Network added successfully!"
+  },
   "networkDetails": {
     "message": "Network Details"
   },
@@ -2690,6 +2707,14 @@
   "revealTheSeedPhrase": {
     "message": "Reveal seed phrase"
   },
+  "revokeAllTokensTitle": {
+    "message": "Revoke permission to access all of your $1?",
+    "description": "$1 is the symbol of the token for which the user is revoking approval"
+  },
+  "revokeApproveForAllDescription": {
+    "message": "By revoking permission, the following $1 will no longer be able to access your $2",
+    "description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name"
+  },
   "rinkeby": {
     "message": "Rinkeby Test Network"
   },
@@ -2866,12 +2891,23 @@
     "message": "Sending $1",
     "description": "$1 represents the native currency symbol for the current network (e.g. ETH or BNB)"
   },
+  "sendingToTokenContractWarning": {
+    "message": "Warning: you are about to send to a token contract which could result in a loss of funds. $1",
+    "description": "$1 is a clickable link with text defined by the 'learnMoreUpperCase' key. The link will open to a support article regarding the known contract address warning"
+  },
   "setAdvancedPrivacySettings": {
     "message": "Set advanced privacy settings"
   },
   "setAdvancedPrivacySettingsDetails": {
     "message": "MetaMask uses these trusted third-party services to enhance product usability and safety."
   },
+  "setApprovalForAll": {
+    "message": "Set Approval for All"
+  },
+  "setApprovalForAllTitle": {
+    "message": "Approve $1 with no spend limit",
+    "description": "The token symbol that is being approved"
+  },
   "settings": {
     "message": "Settings"
   },
@@ -2891,6 +2927,12 @@
   "showAdvancedGasInlineDescription": {
     "message": "Select this to show gas price and limit controls directly on the send and confirm screens."
   },
+  "showCustomNetworkList": {
+    "message": "Show Custom Network List"
+  },
+  "showCustomNetworkListDescription": {
+    "message": "Select this to show a list of networks with prefilled details when adding a new network."
+  },
   "showFiatConversionInTestnets": {
     "message": "Show Conversion on test networks"
   },
@@ -3000,6 +3042,9 @@
   "snapsToggle": {
     "message": "A snap will only run if it is enabled"
   },
+  "someNetworksMayPoseSecurity": {
+    "message": "Some networks may pose security and/or privacy risks. Understand the risks before adding & using a network."
+  },
   "somethingWentWrong": {
     "message": "Oops! Something went wrong."
   },
@@ -3562,6 +3607,10 @@
   "switchNetworks": {
     "message": "Switch Networks"
   },
+  "switchToNetwork": {
+    "message": "Switch to $1",
+    "description": "$1 represents the custom network that has previously been added"
+  },
   "switchToThisAccount": {
     "message": "Switch to this account"
   },
@@ -3865,6 +3914,9 @@
   "unknownCameraErrorTitle": {
     "message": "Ooops! Something went wrong...."
   },
+  "unknownCollection": {
+    "message": "Unnamed collection"
+  },
   "unknownNetwork": {
     "message": "Unknown Private Network"
   },
@@ -4005,6 +4057,9 @@
   "walletCreationSuccessTitle": {
     "message": "Wallet creation successful"
   },
+  "wantToAddThisNetwork": {
+    "message": "Want to add this network?"
+  },
   "warning": {
     "message": "Warning"
   },
@@ -4067,6 +4122,10 @@
   "yesLetsTry": {
     "message": "Yes, let's try"
   },
+  "youHaveAddedAll": {
+    "message": "You've added all the popular networks. You can discover more networks $1 Or you can $2",
+    "description": "$1 is a link with the text 'here' and $2 is a button with the text 'add more networks manually'"
+  },
   "youNeedToAllowCameraAccess": {
     "message": "You need to allow camera access to use this feature."
   },
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index b4a6108af..33386655d 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -4061,7 +4061,7 @@
     "message": "この機能を使用するには、カメラへのアクセスを許可する必要があります。"
   },
   "youSign": {
-    "message": "著名しています"
+    "message": "署名しています"
   },
   "yourPrivateSeedPhrase": {
     "message": "秘密のシークレットリカバリーフレーズ"
diff --git a/app/_locales/zh/messages.json b/app/_locales/zh/messages.json
index 83080c849..f1c97fe44 100644
--- a/app/_locales/zh/messages.json
+++ b/app/_locales/zh/messages.json
@@ -1646,7 +1646,7 @@
     "message": "已知合约地址。"
   },
   "knownTokenWarning": {
-    "message": "此操作将编辑已经在您的钱包中列出的代币,有肯能被用来欺骗您。只有确定要更改这些代币的内容时,才通过此操作。了解更多关于 $1"
+    "message": "此操作将编辑已经在您的钱包中列出的代币,有可能被用来欺骗您。只有确定要更改这些代币的内容时,才通过此操作。了解更多关于 $1"
   },
   "kovan": {
     "message": "Kovan 测试网络"
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index f1bcc95b7..1252d6a14 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -1377,7 +1377,7 @@
     "message": "已知接收方地址。"
   },
   "knownTokenWarning": {
-    "message": "此操作将编辑已经在您的钱包中列出的代币,有肯能被用来欺骗您。只有确定要更改这些代币的内容时,才通过此操作。了解更多关于 $1"
+    "message": "此操作将编辑已经在您的钱包中列出的代币,有可能被用来欺骗您。只有确定要更改这些代币的内容时,才通过此操作。了解更多关于 $1"
   },
   "kovan": {
     "message": "Kovan 测试网络"
diff --git a/app/images/fantom-opera.svg b/app/images/fantom-opera.svg
new file mode 100644
index 000000000..02297ee3a
--- /dev/null
+++ b/app/images/fantom-opera.svg
@@ -0,0 +1 @@
+<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle fill="#1969FF" cx="512" cy="512" r="512"/><path d="M480.953 162.82c17.25-9.093 43.496-9.093 60.746 0l176.016 92.795c10.39 5.477 16.095 13.638 17.117 22.063H735V744.11c-.23 9.19-5.988 18.32-17.285 24.275L541.7 861.18c-17.25 9.093-43.497 9.093-60.746 0l-176.017-92.795c-11.249-5.93-16.647-15.123-16.914-24.275a32.372 32.372 0 0 1-.001-2.35V280.82a24 24 0 0 1 0-1.937v-1.204h.08c.781-8.518 6.228-16.47 16.835-22.063l176.017-92.795ZM707 537l-165.355 87.46c-17.225 9.111-43.433 9.111-60.658 0L316 537.195v205.474l164.987 86.802c9.75 5.217 19.888 10.3 29.76 10.521l.569.008c9.852.032 19.418-4.978 29.117-9.72L707 741.92V537ZM260.424 734c0 17.88 2.06 29.633 6.15 37.912 3.389 6.863 8.475 12.107 17.761 18.489l.53.362c2.038 1.387 4.283 2.839 7.016 4.545l3.223 1.992 9.896 6.025L290.806 827l-11.076-6.75-1.862-1.153c-3.202-1.995-5.857-3.707-8.333-5.392-26.467-18.003-36.337-37.63-36.532-78.461L233 734h27.424ZM498 413c-1.28.44-2.481.951-3.575 1.53l-175.748 93.094c-.185.097-.36.194-.528.29L318 508l.276.159.4.217 175.749 93.094c1.094.579 2.294 1.09 3.575 1.53V413Zm28 0v190a25.085 25.085 0 0 0 3.576-1.53l175.747-93.094c.184-.097.36-.194.528-.29L706 508l-.276-.159-.401-.217-175.747-93.094A25.085 25.085 0 0 0 526 413Zm181-102-158 83 158 83V311Zm-391 0v166l158-83-158-83Zm213.422-123.373c-9.147-4.836-25.697-4.836-34.844 0l-175.9 92.997c-.185.098-.362.194-.529.29L318 281l.276.158.401.218 175.9 92.996c9.148 4.837 25.698 4.837 34.845 0l175.9-92.996c.185-.098.361-.194.528-.29L706 281l-.276-.158-.402-.218-175.9-92.997ZM733.194 197l11.076 6.75 1.862 1.152c3.202 1.995 5.857 3.709 8.333 5.393 26.467 18.003 36.337 37.63 36.532 78.461L791 290h-27.424c0-17.882-2.06-29.633-6.15-37.913-3.388-6.862-8.474-12.107-17.76-18.488l-.531-.362a212.559 212.559 0 0 0-7.016-4.545l-3.223-1.992-9.896-6.025L733.194 197Z" fill="#FFF" fill-rule="nonzero"/></g></svg>
\ No newline at end of file
diff --git a/app/images/harmony-one.svg b/app/images/harmony-one.svg
new file mode 100644
index 000000000..e8466d96d
--- /dev/null
+++ b/app/images/harmony-one.svg
@@ -0,0 +1 @@
+<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300"><defs><linearGradient id="a" x1="71.37" y1="228.63" x2="228.63" y2="71.37" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#00aee9"/><stop offset="1" stop-color="#69fabd"/></linearGradient></defs><path d="M201.17 60a38.81 38.81 0 0 0-38.84 38.71v42.92c-4 .27-8.09.44-12.33.44s-8.31.17-12.33.41V98.71a38.84 38.84 0 0 0-77.67 0v102.58a38.84 38.84 0 0 0 77.67 0v-42.92c4-.27 8.09-.44 12.33-.44s8.31-.17 12.33-.41v43.77a38.84 38.84 0 0 0 77.67 0V98.71A38.81 38.81 0 0 0 201.17 60ZM98.83 75.86a22.91 22.91 0 0 1 22.92 22.85v45.45a130.64 130.64 0 0 0-33 9.33 60 60 0 0 0-12.8 7.64V98.71a22.91 22.91 0 0 1 22.88-22.85Zm22.92 125.43a22.92 22.92 0 0 1-45.84 0V191c0-9.09 7.2-17.7 19.27-23.06a113 113 0 0 1 26.57-7.77Zm79.42 22.85a22.91 22.91 0 0 1-22.92-22.85v-45.45a130.64 130.64 0 0 0 33-9.33 60 60 0 0 0 12.8-7.64v62.42a22.91 22.91 0 0 1-22.88 22.85Zm3.65-92.14a113 113 0 0 1-26.57 7.77V98.71a22.92 22.92 0 0 1 45.84 0V109c0 9.05-7.2 17.66-19.27 23Z" style="fill:url(#a)"/><path style="fill:none" d="M0 0h300v300H0z"/></svg>
\ No newline at end of file
diff --git a/app/images/info-fox.svg b/app/images/info-fox.svg
new file mode 100644
index 000000000..57660c1fe
--- /dev/null
+++ b/app/images/info-fox.svg
@@ -0,0 +1 @@
+<svg width="60" height="45" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path fill="url(#a)" d="M0 0h60v45H0z"/><defs><pattern id="a" patternContentUnits="objectBoundingBox" width="1" height="1"><use xlink:href="#b" transform="matrix(.00101 0 0 .00135 -.001 0)"/></pattern><image id="b" width="990" height="741" xlink:href="data:image/png;base64,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"/></defs></svg>
\ No newline at end of file
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 706260b24..9b9c16d11 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -69,6 +69,7 @@ export default class PreferencesController {
         ? LEDGER_TRANSPORT_TYPES.WEBHID
         : LEDGER_TRANSPORT_TYPES.U2F,
       theme: 'light',
+      customNetworkListEnabled: false,
       ...opts.initState,
     };
 
@@ -179,6 +180,17 @@ export default class PreferencesController {
     this.store.updateState({ theme: val });
   }
 
+  /**
+   * Setter for the `customNetworkListEnabled` property
+   *
+   * @param customNetworkListEnabled
+   */
+  setCustomNetworkListEnabled(customNetworkListEnabled) {
+    this.store.updateState({
+      customNetworkListEnabled,
+    });
+  }
+
   /**
    * Add new methodData to state, to avoid requesting this information again through Infura
    *
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index 0818fc894..87d56c714 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -462,7 +462,10 @@ export default class TransactionController extends EventEmitter {
     };
 
     // only update what is defined
-    editableParams.txParams = pickBy(editableParams.txParams);
+    editableParams.txParams = pickBy(
+      editableParams.txParams,
+      (prop) => prop !== undefined,
+    );
 
     // update transaction type in case it has changes
     const transactionBeforeEdit = this._getTransaction(txId);
diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js
index 422aaadd7..c8adf4191 100644
--- a/app/scripts/controllers/transactions/index.test.js
+++ b/app/scripts/controllers/transactions/index.test.js
@@ -2274,6 +2274,7 @@ describe('Transaction Controller', function () {
     });
 
     it('updates editible params when type changes from simple send to token transfer', async function () {
+      providerResultStub.eth_getCode = '0xab';
       // test update gasFees
       await txController.updateEditableParams('1', {
         data:
diff --git a/app/scripts/lib/ComposableObservableStore.test.js b/app/scripts/lib/ComposableObservableStore.test.js
index 0beeacdfb..9d03eb98d 100644
--- a/app/scripts/lib/ComposableObservableStore.test.js
+++ b/app/scripts/lib/ComposableObservableStore.test.js
@@ -165,7 +165,7 @@ describe('ComposableObservableStore', () => {
             Example: exampleController,
           },
         }),
-    ).toThrow(`Cannot read property 'subscribe' of undefined`);
+    ).toThrow(`Cannot read properties of undefined (reading 'subscribe')`);
   });
 
   it('should throw if the controller messenger is omitted and updateStructure called with a BaseControllerV2 controller', () => {
@@ -175,7 +175,7 @@ describe('ComposableObservableStore', () => {
     });
     const store = new ComposableObservableStore({});
     expect(() => store.updateStructure({ Example: exampleController })).toThrow(
-      `Cannot read property 'subscribe' of undefined`,
+      `Cannot read properties of undefined (reading 'subscribe')`,
     );
   });
 
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index d8f79b6a7..d01d277e7 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -1572,7 +1572,8 @@ export default class MetamaskController extends EventEmitter {
       setCustomRpc: this.setCustomRpc.bind(this),
       updateAndSetCustomRpc: this.updateAndSetCustomRpc.bind(this),
       delCustomRpc: this.delCustomRpc.bind(this),
-
+      addCustomNetwork: this.addCustomNetwork.bind(this),
+      requestUserApproval: this.requestUserApproval.bind(this),
       // PreferencesController
       setSelectedAddress: preferencesController.setSelectedAddress.bind(
         preferencesController,
@@ -1609,7 +1610,9 @@ export default class MetamaskController extends EventEmitter {
         preferencesController,
       ),
       setTheme: preferencesController.setTheme.bind(preferencesController),
-
+      setCustomNetworkListEnabled: preferencesController.setCustomNetworkListEnabled.bind(
+        preferencesController,
+      ),
       // AssetsContractController
       getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
 
@@ -2026,6 +2029,43 @@ export default class MetamaskController extends EventEmitter {
     }
   }
 
+  async requestUserApproval(customRpc, originIsMetaMask) {
+    try {
+      await this.approvalController.addAndShowApprovalRequest({
+        origin: 'metamask',
+        type: 'wallet_addEthereumChain',
+        requestData: {
+          chainId: customRpc.chainId,
+          blockExplorerUrl: customRpc.rpcPrefs.blockExplorerUrl,
+          chainName: customRpc.nickname,
+          rpcUrl: customRpc.rpcUrl,
+          ticker: customRpc.ticker,
+          imageUrl: customRpc.rpcPrefs.imageUrl,
+        },
+      });
+    } catch (error) {
+      if (
+        !(originIsMetaMask && error.message === 'User rejected the request.')
+      ) {
+        throw error;
+      }
+    }
+  }
+
+  async addCustomNetwork(customRpc) {
+    const { chainId, chainName, rpcUrl, ticker, blockExplorerUrl } = customRpc;
+
+    await this.preferencesController.addToFrequentRpcList(
+      rpcUrl,
+      chainId,
+      ticker,
+      chainName,
+      {
+        blockExplorerUrl,
+      },
+    );
+  }
+
   /**
    * Create a new Vault and restore an existent keyring.
    *
diff --git a/development/build/scripts.js b/development/build/scripts.js
index 70db2a694..95833b6ed 100644
--- a/development/build/scripts.js
+++ b/development/build/scripts.js
@@ -36,6 +36,7 @@ const metamaskrc = require('rc')('metamask', {
   COLLECTIBLES_V1: process.env.COLLECTIBLES_V1,
   PHISHING_WARNING_PAGE_URL: process.env.PHISHING_WARNING_PAGE_URL,
   TOKEN_DETECTION_V2: process.env.TOKEN_DETECTION_V2,
+  ADD_POPULAR_NETWORKS: process.env.ADD_POPULAR_NETWORKS,
   SEGMENT_HOST: process.env.SEGMENT_HOST,
   SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,
   SEGMENT_BETA_WRITE_KEY: process.env.SEGMENT_BETA_WRITE_KEY,
@@ -940,6 +941,7 @@ function getEnvironmentVariables({ buildType, devMode, testing, version }) {
     ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1',
     COLLECTIBLES_V1: metamaskrc.COLLECTIBLES_V1 === '1',
     TOKEN_DETECTION_V2: metamaskrc.TOKEN_DETECTION_V2 === '1',
+    ADD_POPULAR_NETWORKS: metamaskrc.ADD_POPULAR_NETWORKS === '1',
   };
 }
 
diff --git a/development/build/transforms/utils.test.js b/development/build/transforms/utils.test.js
index ba273a15b..303b954d3 100644
--- a/development/build/transforms/utils.test.js
+++ b/development/build/transforms/utils.test.js
@@ -26,7 +26,7 @@ describe('transform utils', () => {
       // This error is an artifact of how we're mocking the ESLint singleton,
       // and won't actually occur in production.
       await expect(() => lintTransformedFile()).rejects.toThrow(
-        `Cannot read property '0' of undefined`,
+        `Cannot read properties of undefined (reading '0')`,
       );
       expect(mockESLint).toBeDefined();
     });
diff --git a/package.json b/package.json
index 318365230..828853ae2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "metamask-crx",
-  "version": "10.17.0",
+  "version": "10.18.0",
   "private": true,
   "repository": {
     "type": "git",
@@ -282,7 +282,7 @@
     "browser-util-inspect": "^0.2.0",
     "browserify": "^16.5.1",
     "chalk": "^3.0.0",
-    "chromedriver": "^102.0.0",
+    "chromedriver": "^103.0.0",
     "concurrently": "^5.2.0",
     "copy-webpack-plugin": "^6.0.3",
     "cross-spawn": "^7.0.3",
@@ -381,7 +381,7 @@
     "yarn-deduplicate": "^3.1.0"
   },
   "engines": {
-    "node": "^14.15.1",
+    "node": "^16.0.0",
     "yarn": "^1.16.0"
   },
   "lavamoat": {
diff --git a/shared/constants/network.js b/shared/constants/network.js
index 11f7803a6..ba43e13de 100644
--- a/shared/constants/network.js
+++ b/shared/constants/network.js
@@ -23,11 +23,14 @@ export const KOVAN_CHAIN_ID = '0x2a';
 export const LOCALHOST_CHAIN_ID = '0x539';
 export const BSC_CHAIN_ID = '0x38';
 export const OPTIMISM_CHAIN_ID = '0xa';
-export const OPTIMISM_TESTNET_CHAIN_ID = '0x45';
+export const OPTIMISM_TESTNET_CHAIN_ID = '0x1a4';
 export const POLYGON_CHAIN_ID = '0x89';
 export const AVALANCHE_CHAIN_ID = '0xa86a';
 export const FANTOM_CHAIN_ID = '0xfa';
 export const CELO_CHAIN_ID = '0xa4ec';
+export const ARBITRUM_CHAIN_ID = '0xa4b1';
+export const HARMONY_CHAIN_ID = '0x63564c40';
+export const PALM_CHAIN_ID = '0x2a15c308d';
 
 /**
  * The largest possible chain ID we can handle.
@@ -43,7 +46,14 @@ export const GOERLI_DISPLAY_NAME = 'Goerli';
 export const LOCALHOST_DISPLAY_NAME = 'Localhost 8545';
 export const BSC_DISPLAY_NAME = 'Binance Smart Chain';
 export const POLYGON_DISPLAY_NAME = 'Polygon';
-export const AVALANCHE_DISPLAY_NAME = 'Avalanche';
+export const AVALANCHE_DISPLAY_NAME = 'Avalanche Network C-Chain';
+export const ARBITRUM_DISPLAY_NAME = 'Arbitrum One';
+export const BNB_DISPLAY_NAME =
+  'BNB Smart Chain (previously Binance Smart Chain Mainnet)';
+export const OPTIMISM_DISPLAY_NAME = 'Optimism';
+export const FANTOM_DISPLAY_NAME = 'Fantom Opera';
+export const HARMONY_DISPLAY_NAME = 'Harmony Mainnet Shard 0';
+export const PALM_DISPLAY_NAME = 'Palm';
 
 const infuraProjectId = process.env.INFURA_PROJECT_ID;
 export const getRpcUrl = ({ network, excludeProjectId = false }) =>
@@ -64,12 +74,20 @@ export const MATIC_SYMBOL = 'MATIC';
 export const AVALANCHE_SYMBOL = 'AVAX';
 export const FANTOM_SYMBOL = 'FTM';
 export const CELO_SYMBOL = 'CELO';
+export const ARBITRUM_SYMBOL = 'AETH';
+export const HARMONY_SYMBOL = 'ONE';
+export const PALM_SYMBOL = 'PALM';
 
 export const ETH_TOKEN_IMAGE_URL = './images/eth_logo.svg';
 export const TEST_ETH_TOKEN_IMAGE_URL = './images/black-eth-logo.svg';
 export const BNB_TOKEN_IMAGE_URL = './images/bnb.png';
 export const MATIC_TOKEN_IMAGE_URL = './images/matic-token.png';
 export const AVAX_TOKEN_IMAGE_URL = './images/avax-token.png';
+export const AETH_TOKEN_IMAGE_URL = './images/arbitrum.svg';
+export const FTM_TOKEN_IMAGE_URL = './images/fantom-opera.svg';
+export const HARMONY_ONE_TOKEN_IMAGE_URL = './images/harmony-one.svg';
+export const OPTIMISM_TOKEN_IMAGE_URL = './images/optimism.svg';
+export const PALM_TOKEN_IMAGE_URL = './images/palm.svg';
 
 export const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET, GOERLI];
 
@@ -166,6 +184,12 @@ export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = {
   [AVALANCHE_CHAIN_ID]: AVAX_TOKEN_IMAGE_URL,
   [BSC_CHAIN_ID]: BNB_TOKEN_IMAGE_URL,
   [POLYGON_CHAIN_ID]: MATIC_TOKEN_IMAGE_URL,
+  [ARBITRUM_CHAIN_ID]: AETH_TOKEN_IMAGE_URL,
+  [BSC_CHAIN_ID]: BNB_TOKEN_IMAGE_URL,
+  [FANTOM_CHAIN_ID]: FTM_TOKEN_IMAGE_URL,
+  [HARMONY_CHAIN_ID]: HARMONY_ONE_TOKEN_IMAGE_URL,
+  [OPTIMISM_CHAIN_ID]: OPTIMISM_TOKEN_IMAGE_URL,
+  [PALM_CHAIN_ID]: PALM_TOKEN_IMAGE_URL,
 };
 
 export const CHAIN_ID_TO_NETWORK_ID_MAP = Object.values(
@@ -309,3 +333,86 @@ export const BUYABLE_CHAINS_MAP = {
     },
   },
 };
+
+export const FEATURED_RPCS = [
+  {
+    chainId: ARBITRUM_CHAIN_ID,
+    nickname: ARBITRUM_DISPLAY_NAME,
+    rpcUrl: `https://arbitrum-mainnet.infura.io/v3/${infuraProjectId}`,
+    ticker: ARBITRUM_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://explorer.arbitrum.io',
+      imageUrl: AETH_TOKEN_IMAGE_URL,
+    },
+  },
+  {
+    chainId: AVALANCHE_CHAIN_ID,
+    nickname: AVALANCHE_DISPLAY_NAME,
+    rpcUrl: 'https://api.avax.network/ext/bc/C/rpc',
+    ticker: AVALANCHE_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://snowtrace.io/',
+      imageUrl: AVAX_TOKEN_IMAGE_URL,
+    },
+  },
+  {
+    chainId: BSC_CHAIN_ID,
+    nickname: BNB_DISPLAY_NAME,
+    rpcUrl: 'https://bsc-dataseed.binance.org/',
+    ticker: BNB_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://bscscan.com/',
+      imageUrl: BNB_TOKEN_IMAGE_URL,
+    },
+  },
+  {
+    chainId: FANTOM_CHAIN_ID,
+    nickname: FANTOM_DISPLAY_NAME,
+    rpcUrl: 'https://rpc.ftm.tools/',
+    ticker: FANTOM_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://ftmscan.com/',
+      imageUrl: FTM_TOKEN_IMAGE_URL,
+    },
+  },
+  {
+    chainId: HARMONY_CHAIN_ID,
+    nickname: HARMONY_DISPLAY_NAME,
+    rpcUrl: 'https://api.harmony.one/',
+    ticker: HARMONY_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://explorer.harmony.one/',
+      imageUrl: HARMONY_ONE_TOKEN_IMAGE_URL,
+    },
+  },
+  {
+    chainId: OPTIMISM_CHAIN_ID,
+    nickname: OPTIMISM_DISPLAY_NAME,
+    rpcUrl: `https://optimism-mainnet.infura.io/v3/${infuraProjectId}`,
+    ticker: ETH_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://optimistic.etherscan.io/',
+      imageUrl: OPTIMISM_TOKEN_IMAGE_URL,
+    },
+  },
+  {
+    chainId: PALM_CHAIN_ID,
+    nickname: PALM_DISPLAY_NAME,
+    rpcUrl: `https://palm-mainnet.infura.io/v3/${infuraProjectId}`,
+    ticker: PALM_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://explorer.palm.io/',
+      imageUrl: PALM_TOKEN_IMAGE_URL,
+    },
+  },
+  {
+    chainId: POLYGON_CHAIN_ID,
+    nickname: `${POLYGON_DISPLAY_NAME} ${capitalize(MAINNET)}`,
+    rpcUrl: `https://polygon-mainnet.infura.io/v3/${infuraProjectId}`,
+    ticker: MATIC_SYMBOL,
+    rpcPrefs: {
+      blockExplorerUrl: 'https://polygonscan.com/',
+      imageUrl: MATIC_TOKEN_IMAGE_URL,
+    },
+  },
+];
diff --git a/shared/constants/tokens.js b/shared/constants/tokens.js
index c4e52317a..2a878f3df 100644
--- a/shared/constants/tokens.js
+++ b/shared/constants/tokens.js
@@ -8,3 +8,16 @@ import contractMap from '@metamask/contract-metadata';
 export const LISTED_CONTRACT_ADDRESSES = Object.keys(
   contractMap,
 ).map((address) => address.toLowerCase());
+
+/**
+ * @typedef {Object} TokenDetails
+ * @property {string} address - The address of the selected 'TOKEN' or
+ *  'COLLECTIBLE' contract.
+ * @property {string} [symbol] - The symbol of the token.
+ * @property {number} [decimals] - The number of decimals of the selected
+ *  'ERC20' asset.
+ * @property {number} [tokenId] - The id of the selected 'COLLECTIBLE' asset.
+ * @property {TokenStandardStrings} [standard] - The standard of the selected
+ *  asset.
+ * @property {boolean} [isERC721] - True when the asset is a ERC721 token.
+ */
diff --git a/shared/constants/transaction.js b/shared/constants/transaction.js
index 8cb365f79..d2a54ae35 100644
--- a/shared/constants/transaction.js
+++ b/shared/constants/transaction.js
@@ -15,6 +15,8 @@ import { MESSAGE_TYPE } from './app';
  *  to ensure that the receiver is an address capable of handling with the token being sent.
  * @property {'approve'} TOKEN_METHOD_APPROVE - A token transaction requesting an
  *  allowance of the token to spend on behalf of the user
+ * @property {'setapprovalforall'} TOKEN_METHOD_SET_APPROVAL_FOR_ALL - A token transaction requesting an
+ *  allowance of all of a user's token to spend on behalf of the user
  * @property {'incoming'} INCOMING - An incoming (deposit) transaction
  * @property {'simpleSend'} SIMPLE_SEND - A transaction sending a network's native asset to a recipient
  * @property {'contractInteraction'} CONTRACT_INTERACTION - A transaction that is
@@ -66,6 +68,7 @@ export const TRANSACTION_TYPES = {
   TOKEN_METHOD_SAFE_TRANSFER_FROM: 'safetransferfrom',
   TOKEN_METHOD_TRANSFER: 'transfer',
   TOKEN_METHOD_TRANSFER_FROM: 'transferfrom',
+  TOKEN_METHOD_SET_APPROVAL_FOR_ALL: 'setapprovalforall',
 };
 
 /**
diff --git a/shared/modules/transaction.utils.js b/shared/modules/transaction.utils.js
index 08adb46e3..688548b8e 100644
--- a/shared/modules/transaction.utils.js
+++ b/shared/modules/transaction.utils.js
@@ -8,7 +8,7 @@ import { readAddressAsContract } from './contract-utils';
 import { isEqualCaseInsensitive } from './string-utils';
 
 /**
- * @typedef { 'transfer' | 'approve' | 'transferfrom' | 'contractInteraction'| 'simpleSend' } InferrableTransactionTypes
+ * @typedef { 'transfer' | 'approve' | 'setapprovalforall' | 'transferfrom' | 'contractInteraction'| 'simpleSend' } InferrableTransactionTypes
  */
 
 /**
@@ -148,32 +148,35 @@ export async function determineTransactionType(txParams, query) {
     log.debug('Failed to parse transaction data.', error, data);
   }
 
-  const tokenMethodName = [
-    TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
-    TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
-    TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
-    TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM,
-  ].find((methodName) => isEqualCaseInsensitive(methodName, name));
-
   let result;
-  if (data && tokenMethodName) {
-    result = tokenMethodName;
-  } else if (data && !to) {
-    result = TRANSACTION_TYPES.DEPLOY_CONTRACT;
-  }
-
   let contractCode;
 
-  if (!result) {
+  if (data && !to) {
+    result = TRANSACTION_TYPES.DEPLOY_CONTRACT;
+  } else {
     const {
       contractCode: resultCode,
       isContractAddress,
     } = await readAddressAsContract(query, to);
 
     contractCode = resultCode;
-    result = isContractAddress
-      ? TRANSACTION_TYPES.CONTRACT_INTERACTION
-      : TRANSACTION_TYPES.SIMPLE_SEND;
+
+    if (isContractAddress) {
+      const tokenMethodName = [
+        TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
+        TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL,
+        TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
+        TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
+        TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM,
+      ].find((methodName) => isEqualCaseInsensitive(methodName, name));
+
+      result =
+        data && tokenMethodName
+          ? tokenMethodName
+          : TRANSACTION_TYPES.CONTRACT_INTERACTION;
+    } else {
+      result = TRANSACTION_TYPES.SIMPLE_SEND;
+    }
   }
 
   return { type: result, getCodeResponse: contractCode };
@@ -181,6 +184,7 @@ export async function determineTransactionType(txParams, query) {
 
 const INFERRABLE_TRANSACTION_TYPES = [
   TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
+  TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL,
   TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
   TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
   TRANSACTION_TYPES.CONTRACT_INTERACTION,
@@ -220,6 +224,7 @@ export async function determineTransactionAssetType(
   // method to get the asset type.
   const isTokenMethod = [
     TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
+    TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL,
     TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
     TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
   ].find((methodName) => methodName === inferrableType);
diff --git a/shared/modules/transaction.utils.test.js b/shared/modules/transaction.utils.test.js
index fba8c7827..6998022e4 100644
--- a/shared/modules/transaction.utils.test.js
+++ b/shared/modules/transaction.utils.test.js
@@ -111,13 +111,23 @@ describe('Transaction.utils', function () {
     const genericProvider = createTestProviderTools().provider;
     const query = new EthQuery(genericProvider);
 
-    it('should return a simple send type when to is truthy but data is falsy', async function () {
+    it('should return a simple send type when to is truthy and is not a contract address', async function () {
+      const _providerResultStub = {
+        // 1 gwei
+        eth_gasPrice: '0x0de0b6b3a7640000',
+        // by default, all accounts are external accounts (not contracts)
+        eth_getCode: '0x',
+      };
+      const _provider = createTestProviderTools({
+        scaffold: _providerResultStub,
+      }).provider;
+
       const result = await determineTransactionType(
         {
           to: '0xabc',
           data: '',
         },
-        query,
+        new EthQuery(_provider),
       );
       expect(result).toMatchObject({
         type: TRANSACTION_TYPES.SIMPLE_SEND,
@@ -125,33 +135,78 @@ describe('Transaction.utils', function () {
       });
     });
 
-    it('should return a token transfer type when data is for the respective method call', async function () {
+    it('should return a token transfer type when the recipient is a contract and data is for the respective method call', async function () {
+      const _providerResultStub = {
+        // 1 gwei
+        eth_gasPrice: '0x0de0b6b3a7640000',
+        // by default, all accounts are external accounts (not contracts)
+        eth_getCode: '0xab',
+      };
+      const _provider = createTestProviderTools({
+        scaffold: _providerResultStub,
+      }).provider;
+
       const result = await determineTransactionType(
         {
-          to: '0xabc',
+          to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
           data:
             '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
         },
-        query,
+        new EthQuery(_provider),
       );
       expect(result).toMatchObject({
         type: TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
-        getCodeResponse: undefined,
+        getCodeResponse: '0xab',
       });
     });
 
-    it('should return a token approve type when data is for the respective method call', async function () {
+    it('should NOT return a token transfer type when the recipient is not a contract but the data matches the respective method call', async function () {
+      const _providerResultStub = {
+        // 1 gwei
+        eth_gasPrice: '0x0de0b6b3a7640000',
+        // by default, all accounts are external accounts (not contracts)
+        eth_getCode: '0x',
+      };
+      const _provider = createTestProviderTools({
+        scaffold: _providerResultStub,
+      }).provider;
+
       const result = await determineTransactionType(
         {
-          to: '0xabc',
+          to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
+          data:
+            '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
+        },
+        new EthQuery(_provider),
+      );
+      expect(result).toMatchObject({
+        type: TRANSACTION_TYPES.SIMPLE_SEND,
+        getCodeResponse: '0x',
+      });
+    });
+
+    it('should return a token approve type when when the recipient is a contract and data is for the respective method call', async function () {
+      const _providerResultStub = {
+        // 1 gwei
+        eth_gasPrice: '0x0de0b6b3a7640000',
+        // by default, all accounts are external accounts (not contracts)
+        eth_getCode: '0xab',
+      };
+      const _provider = createTestProviderTools({
+        scaffold: _providerResultStub,
+      }).provider;
+
+      const result = await determineTransactionType(
+        {
+          to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
           data:
             '0x095ea7b30000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C9700000000000000000000000000000000000000000000000000000000000000005',
         },
-        query,
+        new EthQuery(_provider),
       );
       expect(result).toMatchObject({
         type: TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
-        getCodeResponse: undefined,
+        getCodeResponse: '0xab',
       });
     });
 
@@ -184,12 +239,22 @@ describe('Transaction.utils', function () {
     });
 
     it('should return a simple send type with a null getCodeResponse when to is truthy and there is data and but getCode returns an error', async function () {
+      const _providerResultStub = {
+        // 1 gwei
+        eth_gasPrice: '0x0de0b6b3a7640000',
+        // by default, all accounts are external accounts (not contracts)
+        eth_getCode: null,
+      };
+      const _provider = createTestProviderTools({
+        scaffold: _providerResultStub,
+      }).provider;
+
       const result = await determineTransactionType(
         {
-          to: '0xabc',
+          to: '0x9e673399f795D01116e9A8B2dD2F156705131ee9',
           data: '0xabd',
         },
-        query,
+        new EthQuery(_provider),
       );
       expect(result).toMatchObject({
         type: TRANSACTION_TYPES.SIMPLE_SEND,
diff --git a/test/e2e/fixtures/special-settings/state.json b/test/e2e/fixtures/special-settings/state.json
new file mode 100644
index 000000000..6163d7621
--- /dev/null
+++ b/test/e2e/fixtures/special-settings/state.json
@@ -0,0 +1,146 @@
+{
+  "data": {
+    "AppStateController": {
+      "mkrMigrationReminderTimestamp": null
+    },
+    "CachedBalancesController": {
+      "cachedBalances": {
+        "4": {}
+      }
+    },
+    "CurrencyController": {
+      "conversionDate": 1575697244.188,
+      "conversionRate": 149.61,
+      "currentCurrency": "usd",
+      "nativeCurrency": "ETH"
+    },
+    "IncomingTransactionsController": {
+      "incomingTransactions": {},
+      "incomingTxLastFetchedBlocksByNetwork": {
+        "goerli": null,
+        "kovan": null,
+        "mainnet": null,
+        "rinkeby": 5570536
+      }
+    },
+    "KeyringController": {
+      "vault": "{\"data\":\"s6TpYjlUNsn7ifhEFTkuDGBUM1GyOlPrim7JSjtfIxgTt8/6MiXgiR/CtFfR4dWW2xhq85/NGIBYEeWrZThGdKGarBzeIqBfLFhw9n509jprzJ0zc2Rf+9HVFGLw+xxC4xPxgCS0IIWeAJQ+XtGcHmn0UZXriXm8Ja4kdlow6SWinB7sr/WM3R0+frYs4WgllkwggDf2/Tv6VHygvLnhtzp6hIJFyTjh+l/KnyJTyZW1TkZhDaNDzX3SCOHT\",\"iv\":\"FbeHDAW5afeWNORfNJBR0Q==\",\"salt\":\"TxZ+WbCW6891C9LK/hbMAoUsSEW1E8pyGLVBU6x5KR8=\"}"
+    },
+    "NetworkController": {
+      "network": "1337",
+      "provider": {
+        "nickname": "Localhost 8545",
+        "rpcUrl": "http://localhost:8545",
+        "chainId": "0x539",
+        "ticker": "ETH",
+        "type": "rpc"
+      }
+    },
+    "NotificationController": {
+      "notifications": {
+        "1": {
+          "isShown": true
+        },
+        "3": {
+          "isShown": true
+        },
+        "5": {
+          "isShown": true
+        },
+        "6": {
+          "isShown": true
+        },
+        "8": {
+          "isShown": true
+        },
+        "12": {
+          "isShown": true
+        }
+      }
+    },
+    "OnboardingController": {
+      "onboardingTabs": {},
+      "seedPhraseBackedUp": false
+    },
+    "PermissionsMetadata": {
+      "domainMetadata": {
+        "metamask.github.io": {
+          "icon": null,
+          "name": "M E T A M A S K M E S H T E S T"
+        }
+      },
+      "permissionsHistory": {},
+      "permissionsLog": [
+        {
+          "id": 746677923,
+          "method": "eth_accounts",
+          "methodType": "restricted",
+          "origin": "metamask.github.io",
+          "request": {
+            "id": 746677923,
+            "jsonrpc": "2.0",
+            "method": "eth_accounts",
+            "origin": "metamask.github.io",
+            "params": []
+          },
+          "requestTime": 1575697241368,
+          "response": {
+            "id": 746677923,
+            "jsonrpc": "2.0",
+            "result": []
+          },
+          "responseTime": 1575697241370,
+          "success": true
+        }
+      ]
+    },
+    "PreferencesController": {
+      "accountTokens": {
+        "0x5cfe73b6021e818b776b421b1c4db2474086a7e1": {
+          "rinkeby": [],
+          "ropsten": []
+        }
+      },
+      "assetImages": {},
+      "completedOnboarding": true,
+      "dismissSeedBackUpReminder": true,
+      "currentLocale": "en",
+      "featureFlags": {
+        "showIncomingTransactions": true,
+        "transactionTime": false,
+        "sendHexData": true
+      },
+      "firstTimeFlowType": "create",
+      "forgottenPassword": false,
+      "frequentRpcListDetail": [],
+      "identities": {
+        "0x5cfe73b6021e818b776b421b1c4db2474086a7e1": {
+          "address": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1",
+          "name": "Account 1"
+        }
+      },
+      "knownMethodData": {},
+      "lostIdentities": {},
+      "metaMetricsId": null,
+      "participateInMetaMetrics": false,
+      "preferences": {
+        "useNativeCurrencyAsPrimaryCurrency": true
+      },
+      "selectedAddress": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1",
+      "suggestedTokens": {},
+      "tokens": [],
+      "useBlockie": false,
+      "useNonceField": false,
+      "usePhishDetect": true,
+      "useTokenDetection": true
+    },
+    "config": {},
+    "firstTimeInfo": {
+      "date": 1575697234195,
+      "version": "7.7.0"
+    }
+  },
+  "meta": {
+    "version": 40
+  }
+}
diff --git a/test/e2e/mock-e2e.js b/test/e2e/mock-e2e.js
index a6d5d6518..99f5c6e57 100644
--- a/test/e2e/mock-e2e.js
+++ b/test/e2e/mock-e2e.js
@@ -25,6 +25,28 @@ async function setupMocking(server, testSpecificMock) {
     };
   });
 
+  await server
+    .forGet('https://www.4byte.directory/api/v1/signatures/')
+    .thenCallback(() => {
+      return {
+        statusCode: 200,
+        json: {
+          count: 1,
+          next: null,
+          previous: null,
+          results: [
+            {
+              id: 1,
+              created_at: null,
+              text_signature: 'deposit()',
+              hex_signature: null,
+              bytes_signature: null,
+            },
+          ],
+        },
+      };
+    });
+
   await server
     .forGet('https://gas-api.metaswap.codefi.network/networks/1/gasPrices')
     .thenCallback(() => {
diff --git a/test/e2e/snaps/enums.js b/test/e2e/snaps/enums.js
index 086ebdd7d..5787056e8 100644
--- a/test/e2e/snaps/enums.js
+++ b/test/e2e/snaps/enums.js
@@ -1,3 +1,3 @@
 module.exports = {
-  TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/test-snaps/1.0.0',
+  TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/test-snaps/2.0.0',
 };
diff --git a/test/e2e/snaps/test-snap-bip-44.spec.js b/test/e2e/snaps/test-snap-bip-44.spec.js
index e1529fd28..763d6a3a7 100644
--- a/test/e2e/snaps/test-snap-bip-44.spec.js
+++ b/test/e2e/snaps/test-snap-bip-44.spec.js
@@ -31,11 +31,16 @@ describe('Test Snap bip-44', function () {
 
         // navigate to test snaps page and connect
         await driver.driver.get(TEST_SNAPS_WEBSITE_URL);
-        await driver.fill('.snapId3', 'npm:@metamask/test-snap-bip44');
-        await driver.clickElement({
-          text: 'Connect BIP-44 Snap',
-          tag: 'button',
-        });
+        await driver.delay(1000);
+        await driver.fill('#snapId3', 'npm:@metamask/test-snap-bip44');
+
+        // reveal snapId3 by finding and scrolling to #snapId4
+        const snapButton = await driver.findElement('#snapId4');
+        await driver.scrollToElement(snapButton);
+        await driver.delay(500);
+
+        // connect the snap
+        await driver.clickElement('#connectBip44');
 
         // switch to metamask extension and click connect
         await driver.waitUntilXWindowHandles(2, 5000, 10000);
@@ -77,14 +82,11 @@ describe('Test Snap bip-44', function () {
         await driver.waitUntilXWindowHandles(1, 5000, 10000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.clickElement({
-          text: 'Send Test to BIP-44 Snap',
-          tag: 'button',
-        });
+        await driver.clickElement('#sendBip44');
 
         // check the results of the public key test
         await driver.delay(2000);
-        const bip44Result = await driver.findElement('.bip44Result');
+        const bip44Result = await driver.findElement('#bip44Result');
         assert.equal(
           await bip44Result.getText(),
           'Public key: "0x86debb44fb3a984d93f326131d4c1db0bc39644f1a67b673b3ab45941a1cea6a385981755185ac4594b6521e4d1e8d1"',
diff --git a/test/e2e/snaps/test-snap-confirm.spec.js b/test/e2e/snaps/test-snap-confirm.spec.js
index ba5212e85..49caa9e89 100644
--- a/test/e2e/snaps/test-snap-confirm.spec.js
+++ b/test/e2e/snaps/test-snap-confirm.spec.js
@@ -31,11 +31,8 @@ describe('Test Snap Confirm', function () {
 
         // navigate to test snaps page and connect
         await driver.driver.get(TEST_SNAPS_WEBSITE_URL);
-        await driver.fill('.snapId1', 'npm:@metamask/test-snap-confirm');
-        await driver.clickElement({
-          text: 'Connect To Confirm Snap',
-          tag: 'button',
-        });
+        await driver.fill('#snapId1', 'npm:@metamask/test-snap-confirm');
+        await driver.clickElement('#connectHello');
 
         // switch to metamask extension and click connect
         await driver.waitUntilXWindowHandles(2, 5000, 10000);
@@ -70,7 +67,7 @@ describe('Test Snap Confirm', function () {
         await driver.waitUntilXWindowHandles(1, 5000, 10000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.clickElement('.sendConfirmButton');
+        await driver.clickElement('#sendConfirmButton');
 
         // hit 'approve' on the custom confirm
         await driver.waitUntilXWindowHandles(2, 5000, 10000);
@@ -88,7 +85,7 @@ describe('Test Snap Confirm', function () {
         await driver.waitUntilXWindowHandles(1, 5000, 10000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        const confirmResult = await driver.findElement('.confirmResult');
+        const confirmResult = await driver.findElement('#confirmResult');
         assert.equal(await confirmResult.getText(), 'true');
       },
     );
diff --git a/test/e2e/snaps/test-snap-error.spec.js b/test/e2e/snaps/test-snap-error.spec.js
index e8fcb49c1..734520600 100644
--- a/test/e2e/snaps/test-snap-error.spec.js
+++ b/test/e2e/snaps/test-snap-error.spec.js
@@ -30,11 +30,8 @@ describe('Test Snap Error', function () {
 
         // navigate to test snaps page and connect
         await driver.driver.get(TEST_SNAPS_WEBSITE_URL);
-        await driver.fill('.snapId2', 'npm:@metamask/test-snap-error');
-        await driver.clickElement({
-          text: 'Connect Error Snap',
-          tag: 'button',
-        });
+        await driver.fill('#snapId2', 'npm:@metamask/test-snap-error');
+        await driver.clickElement('#connectError');
 
         // switch to metamask extension and click connect
         await driver.waitUntilXWindowHandles(2, 5000, 10000);
@@ -68,10 +65,7 @@ describe('Test Snap Error', function () {
         await driver.waitUntilXWindowHandles(1, 5000, 10000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.clickElement({
-          text: 'Send Test to Error Snap',
-          tag: 'button',
-        });
+        await driver.clickElement('#sendError');
 
         await driver.navigate(PAGES.HOME);
 
diff --git a/test/e2e/snaps/test-snap-managestate.spec.js b/test/e2e/snaps/test-snap-managestate.spec.js
index fbf2ec76c..f22e2fce1 100644
--- a/test/e2e/snaps/test-snap-managestate.spec.js
+++ b/test/e2e/snaps/test-snap-managestate.spec.js
@@ -13,6 +13,7 @@ describe('Test Snap manageState', function () {
         },
       ],
     };
+
     await withFixtures(
       {
         fixtures: 'imported-account',
@@ -29,13 +30,18 @@ describe('Test Snap manageState', function () {
         await driver.fill('#password', 'correct horse battery staple');
         await driver.press('#password', driver.Key.ENTER);
 
-        // navigate to test snaps page and connect
+        // navigate to test snaps page, then fill in the snapId
         await driver.driver.get(TEST_SNAPS_WEBSITE_URL);
-        await driver.fill('.snapId3', 'npm:@metamask/test-snap-managestate');
-        await driver.clickElement({
-          text: 'Connect manageState Snap',
-          tag: 'button',
-        });
+        await driver.delay(1000);
+        await driver.fill('#snapId4', 'npm:@metamask/test-snap-managestate');
+
+        // find and scroll to the rest of the card
+        const snapButton = await driver.findElement('#snapId4');
+        await driver.scrollToElement(snapButton);
+        await driver.delay(500);
+
+        // connect the snap
+        await driver.clickElement('#connectManageState');
 
         // switch to metamask extension and click connect
         await driver.waitUntilXWindowHandles(2, 5000, 10000);
@@ -51,7 +57,6 @@ describe('Test Snap manageState', function () {
           },
           10000,
         );
-
         await driver.delay(2000);
 
         // approve install of snap
@@ -70,32 +75,23 @@ describe('Test Snap manageState', function () {
         await driver.waitUntilXWindowHandles(1, 5000, 10000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.fill('.dataManageState', '23');
-        await driver.clickElement({
-          text: 'Send data to manageState Snap',
-          tag: 'button',
-        });
+        await driver.fill('#dataManageState', '23');
+        await driver.clickElement('#sendManageState');
 
         // check the results of the public key test
-        await driver.delay(2000);
+        await driver.delay(500);
         const manageStateResult = await driver.findElement(
-          '.sendManageStateResult',
+          '#sendManageStateResult',
         );
         assert.equal(await manageStateResult.getText(), 'true');
 
         // click get results
-        await driver.waitUntilXWindowHandles(1, 5000, 10000);
-        windowHandles = await driver.getAllWindowHandles();
-        await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.clickElement({
-          text: 'Get data from manageState Snap',
-          tag: 'button',
-        });
+        await driver.clickElement('#retrieveManageState');
 
         // check the results
-        await driver.delay(2000);
+        await driver.delay(500);
         const retrieveManageStateResult = await driver.findElement(
-          '.retrieveManageStateResult',
+          '#retrieveManageStateResult',
         );
         assert.equal(
           await retrieveManageStateResult.getText(),
@@ -103,34 +99,22 @@ describe('Test Snap manageState', function () {
         );
 
         // click clear results
-        await driver.waitUntilXWindowHandles(1, 5000, 10000);
-        windowHandles = await driver.getAllWindowHandles();
-        await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.clickElement({
-          text: 'Clear data of manageState Snap',
-          tag: 'button',
-        });
+        await driver.clickElement('#clearManageState');
 
         // check if true
-        await driver.delay(2000);
+        await driver.delay(500);
         const clearManageStateResult = await driver.findElement(
-          '.clearManageStateResult',
+          '#clearManageStateResult',
         );
         assert.equal(await clearManageStateResult.getText(), 'true');
 
         // click get results again
-        await driver.waitUntilXWindowHandles(1, 5000, 10000);
-        windowHandles = await driver.getAllWindowHandles();
-        await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.clickElement({
-          text: 'Get data from manageState Snap',
-          tag: 'button',
-        });
+        await driver.clickElement('#retrieveManageState');
 
         // check result array is empty
-        await driver.delay(2000);
+        await driver.delay(500);
         const retrieveManageStateResult2 = await driver.findElement(
-          '.retrieveManageStateResult',
+          '#retrieveManageStateResult',
         );
         assert.equal(
           await retrieveManageStateResult2.getText(),
diff --git a/test/e2e/snaps/test-snap-notification.spec.js b/test/e2e/snaps/test-snap-notification.spec.js
index 01359cf84..85ab18267 100644
--- a/test/e2e/snaps/test-snap-notification.spec.js
+++ b/test/e2e/snaps/test-snap-notification.spec.js
@@ -30,13 +30,18 @@ describe('Test Snap Notification', function () {
         await driver.fill('#password', 'correct horse battery staple');
         await driver.press('#password', driver.Key.ENTER);
 
-        // navigate to test snaps page and connect
+        // navigate to test snaps page
         await driver.driver.get(TEST_SNAPS_WEBSITE_URL);
-        await driver.fill('.snapId5', 'npm:@metamask/test-snap-notification');
-        await driver.clickElement({
-          text: 'Connect Notification Snap',
-          tag: 'button',
-        });
+        await driver.delay(1000);
+
+        // find and scroll down to snapId5
+        const snapButton = await driver.findElement('#snapId5');
+        await driver.scrollToElement(snapButton);
+        await driver.delay(500);
+        await driver.fill('#snapId5', 'npm:@metamask/test-snap-notification');
+
+        // connect the snap
+        await driver.clickElement('#connectNotification');
 
         // switch to metamask extension and click connect
         await driver.waitUntilXWindowHandles(2, 5000, 10000);
@@ -70,10 +75,7 @@ describe('Test Snap Notification', function () {
         await driver.waitUntilXWindowHandles(1, 5000, 10000);
         windowHandles = await driver.getAllWindowHandles();
         await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
-        await driver.clickElement({
-          text: 'Send InApp Notification',
-          tag: 'button',
-        });
+        await driver.clickElement('#sendInAppNotification');
 
         // try to go to the MM pages
         await driver.navigate(PAGES.HOME);
diff --git a/test/e2e/tests/contract-interactions.spec.js b/test/e2e/tests/contract-interactions.spec.js
index db70f0845..ad0ce7fd6 100644
--- a/test/e2e/tests/contract-interactions.spec.js
+++ b/test/e2e/tests/contract-interactions.spec.js
@@ -103,7 +103,7 @@ describe('Deploy contract and call contract methods', function () {
         );
         await driver.waitForSelector({
           css: '.confirm-page-container-summary__action__name',
-          text: 'Withdraw',
+          text: 'Deposit',
         });
         await driver.clickElement({ text: 'Confirm', tag: 'button' });
         await driver.waitUntilXWindowHandles(2);
diff --git a/test/e2e/tests/send-eth.spec.js b/test/e2e/tests/send-eth.spec.js
index e34ab5731..f14bb1bc5 100644
--- a/test/e2e/tests/send-eth.spec.js
+++ b/test/e2e/tests/send-eth.spec.js
@@ -92,6 +92,54 @@ describe('Send ETH from inside MetaMask using default gas', function () {
   });
 });
 
+describe('Send ETH non-contract address with data that matches ERC20 transfer data signature', function () {
+  const ganacheOptions = {
+    accounts: [
+      {
+        secretKey:
+          '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
+        balance: convertToHexValue(25000000000000000000),
+      },
+    ],
+  };
+  it('renders the correct recipient on the confirmation screen', async function () {
+    await withFixtures(
+      {
+        fixtures: 'special-settings',
+        ganacheOptions,
+        title: this.test.title,
+      },
+      async ({ driver }) => {
+        await driver.navigate();
+        await driver.fill('#password', 'correct horse battery staple');
+        await driver.press('#password', driver.Key.ENTER);
+
+        await driver.clickElement('[data-testid="eth-overview-send"]');
+
+        await driver.fill(
+          'input[placeholder="Search, public address (0x), or ENS"]',
+          '0xc427D562164062a23a5cFf596A4a3208e72Acd28',
+        );
+
+        await driver.fill(
+          'textarea[placeholder="Optional',
+          '0xa9059cbb0000000000000000000000002f318C334780961FB129D2a6c30D0763d9a5C970000000000000000000000000000000000000000000000000000000000000000a',
+        );
+
+        await driver.clickElement({ text: 'Next', tag: 'button' });
+
+        await driver.clickElement({ text: '0xc42...cd28' });
+
+        const recipientAddress = await driver.findElements({
+          text: '0xc427D562164062a23a5cFf596A4a3208e72Acd28',
+        });
+
+        assert.equal(recipientAddress.length, 1);
+      },
+    );
+  });
+});
+
 /* eslint-disable-next-line mocha/max-top-level-suites */
 describe('Send ETH from inside MetaMask using advanced gas modal', function () {
   const ganacheOptions = {
diff --git a/test/e2e/tests/send-hex-address.spec.js b/test/e2e/tests/send-hex-address.spec.js
new file mode 100644
index 000000000..ed89901db
--- /dev/null
+++ b/test/e2e/tests/send-hex-address.spec.js
@@ -0,0 +1,329 @@
+const { strict: assert } = require('assert');
+const { convertToHexValue, withFixtures } = require('../helpers');
+
+const hexPrefixedAddress = '0x2f318C334780961FB129D2a6c30D0763d9a5C970';
+const nonHexPrefixedAddress = hexPrefixedAddress.substring(2);
+
+describe('Send ETH to a 40 character hexadecimal address', function () {
+  const ganacheOptions = {
+    accounts: [
+      {
+        secretKey:
+          '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
+        balance: convertToHexValue(25000000000000000000),
+      },
+    ],
+  };
+  it('should ensure the address is prefixed with 0x when pasted and should send ETH to a valid hexadecimal address', async function () {
+    await withFixtures(
+      {
+        fixtures: 'imported-account',
+        ganacheOptions,
+        title: this.test.title,
+        failOnConsoleError: false,
+      },
+      async ({ driver }) => {
+        await driver.navigate();
+        await driver.fill('#password', 'correct horse battery staple');
+        await driver.press('#password', driver.Key.ENTER);
+
+        // Send ETH
+        await driver.clickElement('[data-testid="eth-overview-send"]');
+
+        // Paste address without hex prefix
+        await driver.pasteIntoField(
+          'input[placeholder="Search, public address (0x), or ENS"]',
+          nonHexPrefixedAddress,
+        );
+        await driver.waitForSelector({
+          css: '.ens-input__selected-input__title',
+          text: hexPrefixedAddress,
+        });
+        await driver.wait(async () => {
+          const sendDialogMsgs = await driver.findElements(
+            '.send-v2__form div.dialog',
+          );
+          return sendDialogMsgs.length === 1;
+        }, 10000);
+        await driver.clickElement({ text: 'Next', tag: 'button' });
+
+        // Confirm transaction
+        await driver.clickElement({ text: 'Confirm', tag: 'button' });
+        await driver.clickElement('[data-testid="home__activity-tab"]');
+        const sendTransactionListItem = await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item',
+        );
+        await sendTransactionListItem.click();
+        await driver.clickElement({ text: 'Activity log', tag: 'summary' });
+        await driver.clickElement('.sender-to-recipient__name:nth-of-type(2)');
+
+        // Verify address in activity log
+        const publicAddress = await driver.findElement(
+          '.nickname-popover__public-address',
+        );
+        assert.equal(await publicAddress.getText(), hexPrefixedAddress);
+      },
+    );
+  });
+  it('should ensure the address is prefixed with 0x when typed and should send ETH to a valid hexadecimal address', async function () {
+    await withFixtures(
+      {
+        fixtures: 'imported-account',
+        ganacheOptions,
+        title: this.test.title,
+        failOnConsoleError: false,
+      },
+      async ({ driver }) => {
+        await driver.navigate();
+        await driver.fill('#password', 'correct horse battery staple');
+        await driver.press('#password', driver.Key.ENTER);
+
+        // Send ETH
+        await driver.clickElement('[data-testid="eth-overview-send"]');
+
+        // Type address without hex prefix
+        await driver.fill(
+          'input[placeholder="Search, public address (0x), or ENS"]',
+          nonHexPrefixedAddress,
+        );
+        await driver.waitForSelector({
+          css: '.ens-input__selected-input__title',
+          text: hexPrefixedAddress,
+        });
+        await driver.wait(async () => {
+          const sendDialogMsgs = await driver.findElements(
+            '.send-v2__form div.dialog',
+          );
+          return sendDialogMsgs.length === 1;
+        }, 10000);
+        await driver.clickElement({ text: 'Next', tag: 'button' });
+
+        // Confirm transaction
+        await driver.clickElement({ text: 'Confirm', tag: 'button' });
+        await driver.clickElement('[data-testid="home__activity-tab"]');
+        const sendTransactionListItem = await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item',
+        );
+        await sendTransactionListItem.click();
+        await driver.clickElement({ text: 'Activity log', tag: 'summary' });
+        await driver.clickElement('.sender-to-recipient__name:nth-of-type(2)');
+
+        // Verify address in activity log
+        const publicAddress = await driver.findElement(
+          '.nickname-popover__public-address',
+        );
+        assert.equal(await publicAddress.getText(), hexPrefixedAddress);
+      },
+    );
+  });
+});
+
+describe('Send ERC20 to a 40 character hexadecimal address', function () {
+  const ganacheOptions = {
+    accounts: [
+      {
+        secretKey:
+          '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
+        balance: convertToHexValue(25000000000000000000),
+      },
+    ],
+  };
+  it('should ensure the address is prefixed with 0x when pasted and should send TST to a valid hexadecimal address', async function () {
+    await withFixtures(
+      {
+        dapp: true,
+        fixtures: 'connected-state',
+        ganacheOptions,
+        title: this.test.title,
+        failOnConsoleError: false,
+      },
+      async ({ driver }) => {
+        await driver.navigate();
+        await driver.fill('#password', 'correct horse battery staple');
+        await driver.press('#password', driver.Key.ENTER);
+
+        // Create TST
+        await driver.openNewPage('http://127.0.0.1:8080/');
+        await driver.clickElement('#createToken');
+        await driver.waitUntilXWindowHandles(3);
+        let windowHandles = await driver.getAllWindowHandles();
+        const extension = windowHandles[0];
+        const dapp = await driver.switchToWindowWithTitle(
+          'E2E Test Dapp',
+          windowHandles,
+        );
+        await driver.switchToWindowWithTitle(
+          'MetaMask Notification',
+          windowHandles,
+        );
+        await driver.clickElement({ text: 'Confirm', tag: 'button' });
+        await driver.waitUntilXWindowHandles(2);
+        await driver.switchToWindow(extension);
+        await driver.clickElement('[data-testid="home__activity-tab"]');
+        await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(1)',
+          { timeout: 10000 },
+        );
+
+        // Add token
+        await driver.switchToWindow(dapp);
+        await driver.clickElement('#watchAsset');
+        await driver.waitUntilXWindowHandles(3);
+        windowHandles = await driver.getAllWindowHandles();
+        await driver.switchToWindowWithTitle(
+          'MetaMask Notification',
+          windowHandles,
+        );
+        await driver.clickElement({ text: 'Add Token', tag: 'button' });
+        await driver.waitUntilXWindowHandles(2);
+        await driver.switchToWindow(extension);
+
+        // Send TST
+        await driver.clickElement('[data-testid="home__asset-tab"]');
+        await driver.clickElement('.token-cell');
+        await driver.clickElement('[data-testid="eth-overview-send"]');
+
+        // Paste address without hex prefix
+        await driver.pasteIntoField(
+          'input[placeholder="Search, public address (0x), or ENS"]',
+          nonHexPrefixedAddress,
+        );
+        await driver.waitForSelector({
+          css: '.ens-input__selected-input__title',
+          text: hexPrefixedAddress,
+        });
+        await driver.wait(async () => {
+          const sendDialogMsgs = await driver.findElements(
+            '.send-v2__form div.dialog',
+          );
+          return sendDialogMsgs.length === 1;
+        }, 10000);
+        await driver.delay(2000);
+        await driver.clickElement({ text: 'Next', tag: 'button' });
+
+        // Confirm transaction
+        await driver.waitForSelector({
+          css: '.confirm-page-container-summary__title',
+          text: '0 TST',
+        });
+        await driver.clickElement({ text: 'Confirm', tag: 'button' });
+        await driver.clickElement('[data-testid="home__activity-tab"]');
+        await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(2)',
+          { timeout: 10000 },
+        );
+        const sendTransactionListItem = await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(1)',
+        );
+        await sendTransactionListItem.click();
+        await driver.clickElement({ text: 'Activity log', tag: 'summary' });
+        await driver.clickElement('.sender-to-recipient__name:nth-of-type(2)');
+
+        // Verify address in activity log
+        const publicAddress = await driver.findElement(
+          '.nickname-popover__public-address',
+        );
+        assert.equal(await publicAddress.getText(), hexPrefixedAddress);
+      },
+    );
+  });
+  it('should ensure the address is prefixed with 0x when typed and should send TST to a valid hexadecimal address', async function () {
+    await withFixtures(
+      {
+        dapp: true,
+        fixtures: 'connected-state',
+        ganacheOptions,
+        title: this.test.title,
+        failOnConsoleError: false,
+      },
+      async ({ driver }) => {
+        await driver.navigate();
+        await driver.fill('#password', 'correct horse battery staple');
+        await driver.press('#password', driver.Key.ENTER);
+
+        // Create TST
+        await driver.openNewPage('http://127.0.0.1:8080/');
+        await driver.clickElement('#createToken');
+        await driver.waitUntilXWindowHandles(3);
+        let windowHandles = await driver.getAllWindowHandles();
+        const extension = windowHandles[0];
+        const dapp = await driver.switchToWindowWithTitle(
+          'E2E Test Dapp',
+          windowHandles,
+        );
+        await driver.switchToWindowWithTitle(
+          'MetaMask Notification',
+          windowHandles,
+        );
+        await driver.clickElement({ text: 'Confirm', tag: 'button' });
+        await driver.waitUntilXWindowHandles(2);
+        await driver.switchToWindow(extension);
+        await driver.clickElement('[data-testid="home__activity-tab"]');
+        await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(1)',
+          { timeout: 10000 },
+        );
+
+        // Add token
+        await driver.switchToWindow(dapp);
+        await driver.clickElement('#watchAsset');
+        await driver.waitUntilXWindowHandles(3);
+        windowHandles = await driver.getAllWindowHandles();
+        await driver.switchToWindowWithTitle(
+          'MetaMask Notification',
+          windowHandles,
+        );
+        await driver.clickElement({ text: 'Add Token', tag: 'button' });
+        await driver.waitUntilXWindowHandles(2);
+        await driver.switchToWindow(extension);
+
+        // Send TST
+        await driver.clickElement('[data-testid="home__asset-tab"]');
+        await driver.clickElement('.token-cell');
+        await driver.clickElement('[data-testid="eth-overview-send"]');
+
+        // Type address without hex prefix
+        await driver.fill(
+          'input[placeholder="Search, public address (0x), or ENS"]',
+          nonHexPrefixedAddress,
+        );
+        await driver.waitForSelector({
+          css: '.ens-input__selected-input__title',
+          text: hexPrefixedAddress,
+        });
+        await driver.wait(async () => {
+          const sendDialogMsgs = await driver.findElements(
+            '.send-v2__form div.dialog',
+          );
+          return sendDialogMsgs.length === 1;
+        }, 10000);
+        await driver.delay(2000);
+        await driver.clickElement({ text: 'Next', tag: 'button' });
+
+        // Confirm transaction
+        await driver.waitForSelector({
+          css: '.confirm-page-container-summary__title',
+          text: '0 TST',
+        });
+        await driver.clickElement({ text: 'Confirm', tag: 'button' });
+        await driver.clickElement('[data-testid="home__activity-tab"]');
+        await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(2)',
+          { timeout: 10000 },
+        );
+        const sendTransactionListItem = await driver.waitForSelector(
+          '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(1)',
+        );
+        await sendTransactionListItem.click();
+        await driver.clickElement({ text: 'Activity log', tag: 'summary' });
+        await driver.clickElement('.sender-to-recipient__name:nth-of-type(2)');
+
+        // Verify address in activity log
+        const publicAddress = await driver.findElement(
+          '.nickname-popover__public-address',
+        );
+        assert.equal(await publicAddress.getText(), hexPrefixedAddress);
+      },
+    );
+  });
+});
diff --git a/test/e2e/tests/state-logs.spec.js b/test/e2e/tests/state-logs.spec.js
new file mode 100644
index 000000000..6152ff01a
--- /dev/null
+++ b/test/e2e/tests/state-logs.spec.js
@@ -0,0 +1,65 @@
+const { strict: assert } = require('assert');
+const { promises: fs } = require('fs');
+const { convertToHexValue, withFixtures } = require('../helpers');
+
+const downloadsFolder = `${process.cwd()}/test-artifacts/downloads`;
+
+const createDownloadFolder = async () => {
+  await fs.rm(downloadsFolder, { recursive: true, force: true });
+  await fs.mkdir(downloadsFolder, { recursive: true });
+};
+
+const stateLogsExist = async () => {
+  try {
+    const stateLogs = `${downloadsFolder}/MetaMask State Logs.json`;
+    await fs.access(stateLogs);
+    return true;
+  } catch (e) {
+    return false;
+  }
+};
+
+describe('State logs', function () {
+  const ganacheOptions = {
+    accounts: [
+      {
+        secretKey:
+          '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
+        balance: convertToHexValue(25000000000000000000),
+      },
+    ],
+  };
+  it('should download state logs for the account', async function () {
+    await withFixtures(
+      {
+        fixtures: 'imported-account',
+        ganacheOptions,
+        title: this.test.title,
+        failOnConsoleError: false,
+      },
+      async ({ driver }) => {
+        await createDownloadFolder();
+        await driver.navigate();
+        await driver.fill('#password', 'correct horse battery staple');
+        await driver.press('#password', driver.Key.ENTER);
+
+        // Download State Logs
+        await driver.clickElement('.account-menu__icon');
+        await driver.clickElement({ text: 'Settings', tag: 'div' });
+        await driver.clickElement({ text: 'Advanced', tag: 'div' });
+        await driver.clickElement({
+          text: 'Download State Logs',
+          tag: 'button',
+        });
+
+        // Verify download
+        let fileExists;
+        await driver.wait(async () => {
+          fileExists = await stateLogsExist();
+          return fileExists === true;
+        }, 10000);
+        assert.equal(fileExists, true);
+      },
+    );
+  });
+});
diff --git a/test/e2e/webdriver/chrome.js b/test/e2e/webdriver/chrome.js
index 13ff00f62..a9df3e646 100644
--- a/test/e2e/webdriver/chrome.js
+++ b/test/e2e/webdriver/chrome.js
@@ -22,6 +22,9 @@ class ChromeDriver {
     const options = new chrome.Options().addArguments(args);
     options.setProxy(proxy.manual({ https: HTTPS_PROXY_HOST }));
     options.setAcceptInsecureCerts(true);
+    options.setUserPreferences({
+      'download.default_directory': `${process.cwd()}/test-artifacts/downloads`,
+    });
     const builder = new Builder()
       .forBrowser('chrome')
       .setChromeOptions(options);
diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js
index 630bd6683..73c029bfe 100644
--- a/test/e2e/webdriver/driver.js
+++ b/test/e2e/webdriver/driver.js
@@ -266,21 +266,21 @@ class Driver {
   /**
    * Paste a string into a field.
    *
-   * @param {string} element - The element locator.
+   * @param {string} rawLocator - The element locator.
    * @param {string} contentToPaste - The content to paste.
    */
-  async pasteIntoField(element, contentToPaste) {
+  async pasteIntoField(rawLocator, contentToPaste) {
     // Throw if double-quote is present in content to paste
     // so that we don't have to worry about escaping double-quotes
     if (contentToPaste.includes('"')) {
       throw new Error('Cannot paste content with double-quote');
     }
     // Click to focus the field
-    await this.clickElement(element);
+    await this.clickElement(rawLocator);
     await this.executeScript(
       `navigator.clipboard.writeText("${contentToPaste}")`,
     );
-    await this.fill(element, Key.chord(this.Key.MODIFIER, 'v'));
+    await this.fill(rawLocator, Key.chord(this.Key.MODIFIER, 'v'));
   }
 
   // Navigation
diff --git a/test/e2e/webdriver/firefox.js b/test/e2e/webdriver/firefox.js
index 71111df58..3e3dd84cd 100644
--- a/test/e2e/webdriver/firefox.js
+++ b/test/e2e/webdriver/firefox.js
@@ -40,6 +40,11 @@ class FirefoxDriver {
     const options = new firefox.Options().setProfile(templateProfile);
     options.setProxy(proxy.manual({ https: HTTPS_PROXY_HOST }));
     options.setAcceptInsecureCerts(true);
+    options.setPreference('browser.download.folderList', 2);
+    options.setPreference(
+      'browser.download.dir',
+      `${process.cwd()}/test-artifacts/downloads`,
+    );
     const builder = new Builder()
       .forBrowser('firefox')
       .setFirefoxOptions(options);
diff --git a/test/jest/mocks.js b/test/jest/mocks.js
index 6e257adaa..47c8a1f99 100644
--- a/test/jest/mocks.js
+++ b/test/jest/mocks.js
@@ -1,3 +1,8 @@
+import {
+  draftTransactionInitialState,
+  initialState,
+} from '../../ui/ducks/send';
+
 export const TOP_ASSETS_GET_RESPONSE = [
   {
     symbol: 'LINK',
@@ -103,3 +108,42 @@ export const createGasFeeEstimatesForFeeMarket = () => {
     estimatedBaseFee: '50',
   };
 };
+
+export const INITIAL_SEND_STATE_FOR_EXISTING_DRAFT = {
+  ...initialState,
+  currentTransactionUUID: 'test-uuid',
+  draftTransactions: {
+    'test-uuid': {
+      ...draftTransactionInitialState,
+    },
+  },
+};
+
+export const getInitialSendStateWithExistingTxState = (draftTxState) => ({
+  ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+  draftTransactions: {
+    'test-uuid': {
+      ...draftTransactionInitialState,
+      ...draftTxState,
+      amount: {
+        ...draftTransactionInitialState.amount,
+        ...draftTxState.amount,
+      },
+      asset: {
+        ...draftTransactionInitialState.asset,
+        ...draftTxState.asset,
+      },
+      gas: {
+        ...draftTransactionInitialState.gas,
+        ...draftTxState.gas,
+      },
+      recipient: {
+        ...draftTransactionInitialState.recipient,
+        ...draftTxState.recipient,
+      },
+      history: draftTxState.history ?? [],
+      // Use this key if you want to console.log inside the send.js file.
+      test: draftTxState.test ?? 'yo',
+    },
+  },
+});
diff --git a/ui/components/app/add-network/add-network.js b/ui/components/app/add-network/add-network.js
index c453eedf5..3ebf2d293 100644
--- a/ui/components/app/add-network/add-network.js
+++ b/ui/components/app/add-network/add-network.js
@@ -1,168 +1,286 @@
-import React, { useContext } from 'react';
-import { useSelector } from 'react-redux';
-import PropTypes from 'prop-types';
+import React, { useContext, useEffect, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useHistory } from 'react-router-dom';
 import { I18nContext } from '../../../contexts/i18n';
 import Box from '../../ui/box';
 import Typography from '../../ui/typography';
 import {
   ALIGN_ITEMS,
-  BLOCK_SIZES,
   COLORS,
   DISPLAY,
   FLEX_DIRECTION,
   FONT_WEIGHT,
   TYPOGRAPHY,
   JUSTIFY_CONTENT,
+  SIZES,
 } from '../../../helpers/constants/design-system';
 import Button from '../../ui/button';
-import IconCaretLeft from '../../ui/icon/icon-caret-left';
 import Tooltip from '../../ui/tooltip';
 import IconWithFallback from '../../ui/icon-with-fallback';
 import IconBorder from '../../ui/icon-border';
-import { getTheme } from '../../../selectors';
-import { THEME_TYPE } from '../../../pages/settings/experimental-tab/experimental-tab.constant';
+import {
+  getFrequentRpcListDetail,
+  getUnapprovedConfirmations,
+} from '../../../selectors';
 
-const AddNetwork = ({
-  onBackClick,
-  onAddNetworkClick,
-  onAddNetworkManuallyClick,
-  featuredRPCS,
-}) => {
+import {
+  ENVIRONMENT_TYPE_FULLSCREEN,
+  ENVIRONMENT_TYPE_POPUP,
+  MESSAGE_TYPE,
+} from '../../../../shared/constants/app';
+import { requestUserApproval } from '../../../store/actions';
+import Popover from '../../ui/popover';
+import ConfirmationPage from '../../../pages/confirmation/confirmation';
+import { FEATURED_RPCS } from '../../../../shared/constants/network';
+import { ADD_NETWORK_ROUTE } from '../../../helpers/constants/routes';
+import { getEnvironmentType } from '../../../../app/scripts/lib/util';
+
+const AddNetwork = () => {
   const t = useContext(I18nContext);
-  const theme = useSelector(getTheme);
+  const dispatch = useDispatch();
+  const history = useHistory();
+  const frequentRpcList = useSelector(getFrequentRpcListDetail);
+
+  const frequentRpcListChainIds = Object.values(frequentRpcList).map(
+    (net) => net.chainId,
+  );
 
   const infuraRegex = /infura.io/u;
 
-  const nets = featuredRPCS
-    .sort((a, b) => (a.ticker > b.ticker ? 1 : -1))
-    .slice(0, 8);
+  const nets = FEATURED_RPCS.sort((a, b) =>
+    a.ticker > b.ticker ? 1 : -1,
+  ).slice(0, FEATURED_RPCS.length);
+
+  const notFrequentRpcNetworks = nets.filter(
+    (net) => frequentRpcListChainIds.indexOf(net.chainId) === -1,
+  );
+  const unapprovedConfirmations = useSelector(getUnapprovedConfirmations);
+  const [showPopover, setShowPopover] = useState(false);
+
+  useEffect(() => {
+    const anAddNetworkConfirmationFromMetaMaskExists = unapprovedConfirmations?.find(
+      (confirmation) => {
+        return (
+          confirmation.origin === 'metamask' &&
+          confirmation.type === MESSAGE_TYPE.ADD_ETHEREUM_CHAIN
+        );
+      },
+    );
+    if (!showPopover && anAddNetworkConfirmationFromMetaMaskExists) {
+      setShowPopover(true);
+    }
+
+    if (showPopover && !anAddNetworkConfirmationFromMetaMaskExists) {
+      setShowPopover(false);
+    }
+  }, [unapprovedConfirmations, showPopover]);
 
   return (
-    <Box>
-      <Box
-        height={BLOCK_SIZES.TWO_TWELFTHS}
-        padding={[4, 0, 4, 0]}
-        display={DISPLAY.FLEX}
-        alignItems={ALIGN_ITEMS.CENTER}
-        flexDirection={FLEX_DIRECTION.ROW}
-        className="add-network__header"
-      >
-        <IconCaretLeft
-          aria-label={t('back')}
-          onClick={onBackClick}
-          className="add-network__header__back-icon"
-        />
-        <Typography variant={TYPOGRAPHY.H3} color={COLORS.TEXT_DEFAULT}>
-          {t('addNetwork')}
-        </Typography>
-      </Box>
-      <Box
-        height={BLOCK_SIZES.FOUR_FIFTHS}
-        width={BLOCK_SIZES.TEN_TWELFTHS}
-        margin={[0, 6, 0, 6]}
-      >
-        <Typography
-          variant={TYPOGRAPHY.H6}
-          color={COLORS.TEXT_ALTERNATIVE}
-          margin={[4, 0, 0, 0]}
+    <>
+      {Object.keys(notFrequentRpcNetworks).length === 0 ? (
+        <Box
+          className="add-network__edge-case-box"
+          borderRadius={SIZES.MD}
+          padding={4}
+          margin={[4, 6, 0, 6]}
+          display={DISPLAY.FLEX}
+          flexDirection={FLEX_DIRECTION.ROW}
+          backgroundColor={COLORS.BACKGROUND_ALTERNATIVE}
         >
-          {t('addFromAListOfPopularNetworks')}
-        </Typography>
-        <Typography
-          variant={TYPOGRAPHY.H7}
-          color={COLORS.TEXT_MUTED}
-          margin={[4, 0, 3, 0]}
-        >
-          {t('popularCustomNetworks')}
-        </Typography>
-        {nets.map((item, index) => (
-          <Box
-            key={index}
-            display={DISPLAY.FLEX}
-            alignItems={ALIGN_ITEMS.CENTER}
-            justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
-            marginBottom={6}
-          >
-            <Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
-              <IconBorder size={24}>
-                <IconWithFallback
-                  icon={item.rpcPrefs.imageUrl}
-                  name={item.nickname}
-                  size={24}
-                />
-              </IconBorder>
-              <Typography
-                variant={TYPOGRAPHY.H7}
-                color={COLORS.TEXT_DEFAULT}
-                fontWeight={FONT_WEIGHT.BOLD}
-                boxProps={{ marginLeft: 2 }}
-              >
-                {item.nickname}
+          <Box marginRight={4}>
+            <img src="images/info-fox.svg" />
+          </Box>
+          <Box>
+            <Typography variant={TYPOGRAPHY.H7}>
+              {t('youHaveAddedAll', [
+                <a
+                  key="link"
+                  className="add-network__edge-case-box__link"
+                  href="https://chainlist.wtf/"
+                  target="_blank"
+                  rel="noreferrer"
+                >
+                  {t('here')}.
+                </a>,
+                <Button
+                  key="button"
+                  type="inline"
+                  onClick={(event) => {
+                    event.preventDefault();
+                    getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
+                      ? global.platform.openExtensionInBrowser(
+                          ADD_NETWORK_ROUTE,
+                        )
+                      : history.push(ADD_NETWORK_ROUTE);
+                  }}
+                >
+                  <Typography
+                    variant={TYPOGRAPHY.H7}
+                    color={COLORS.INFO_DEFAULT}
+                  >
+                    {t('addMoreNetworks')}.
+                  </Typography>
+                </Button>,
+              ])}
+            </Typography>
+          </Box>
+        </Box>
+      ) : (
+        <Box className="add-network__networks-container">
+          {getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN && (
+            <Box
+              display={DISPLAY.FLEX}
+              alignItems={ALIGN_ITEMS.CENTER}
+              flexDirection={FLEX_DIRECTION.ROW}
+              marginTop={7}
+              marginBottom={4}
+              paddingBottom={2}
+              className="add-network__header"
+            >
+              <Typography variant={TYPOGRAPHY.H4} color={COLORS.TEXT_MUTED}>
+                {t('networks')}
+              </Typography>
+              <span className="add-network__header__subtitle">{'  >  '}</span>
+              <Typography variant={TYPOGRAPHY.H4} color={COLORS.TEXT_DEFAULT}>
+                {t('addANetwork')}
               </Typography>
             </Box>
-            <Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
-              {
-                // Warning for the networks that doesn't use infura.io as the RPC
-                !infuraRegex.test(item.rpcUrl) && (
-                  <Tooltip
-                    className="add-network__warning-tooltip"
-                    position="top"
-                    interactive
-                    html={
-                      <Box margin={3} className="add-network__warning-tooltip">
-                        {t('addNetworkTooltipWarning', [
-                          <a
-                            key="zendesk_page_link"
-                            href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
-                            rel="noreferrer"
-                            target="_blank"
-                          >
-                            {t('learnMoreUpperCase')}
-                          </a>,
-                        ])}
-                      </Box>
-                    }
-                    trigger="mouseenter"
-                    theme={theme === THEME_TYPE.DEFAULT ? 'light' : 'dark'}
-                  >
-                    <i
-                      className="fa fa-exclamation-triangle add-network__warning-icon"
-                      title={t('warning')}
-                    />
-                  </Tooltip>
-                )
-              }
-              <Button
-                type="inline"
-                className="add-network__add-button"
-                onClick={onAddNetworkClick}
+          )}
+          <Box
+            margin={
+              getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
+                ? [0, 0, 1, 0]
+                : [4, 0, 1, 0]
+            }
+            className="add-network__main-container"
+          >
+            <Typography
+              variant={TYPOGRAPHY.H6}
+              color={COLORS.TEXT_ALTERNATIVE}
+              margin={[4, 0, 0, 0]}
+            >
+              {t('addFromAListOfPopularNetworks')}
+            </Typography>
+            <Typography
+              variant={TYPOGRAPHY.H7}
+              color={COLORS.TEXT_MUTED}
+              margin={[4, 0, 3, 0]}
+            >
+              {t('popularCustomNetworks')}
+            </Typography>
+            {notFrequentRpcNetworks.map((item, index) => (
+              <Box
+                key={index}
+                display={DISPLAY.FLEX}
+                alignItems={ALIGN_ITEMS.CENTER}
+                justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN}
+                marginBottom={6}
+                className="add-network__list-of-networks"
               >
-                {t('add')}
-              </Button>
-            </Box>
+                <Box display={DISPLAY.FLEX} alignItems={ALIGN_ITEMS.CENTER}>
+                  <Box>
+                    <IconBorder size={24}>
+                      <IconWithFallback
+                        icon={item.rpcPrefs.imageUrl}
+                        name={item.nickname}
+                        size={24}
+                      />
+                    </IconBorder>
+                  </Box>
+                  <Box marginLeft={2}>
+                    <Typography
+                      variant={TYPOGRAPHY.H7}
+                      color={COLORS.TEXT_DEFAULT}
+                      fontWeight={FONT_WEIGHT.BOLD}
+                    >
+                      {item.nickname}
+                    </Typography>
+                  </Box>
+                </Box>
+                <Box
+                  display={DISPLAY.FLEX}
+                  alignItems={ALIGN_ITEMS.CENTER}
+                  marginLeft={1}
+                >
+                  {
+                    // Warning for the networks that doesn't use infura.io as the RPC
+                    !infuraRegex.test(item.rpcUrl) && (
+                      <Tooltip
+                        position="top"
+                        interactive
+                        html={
+                          <Box
+                            margin={3}
+                            className="add-network__warning-tooltip"
+                          >
+                            {t('addNetworkTooltipWarning', [
+                              <a
+                                key="zendesk_page_link"
+                                href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
+                                rel="noreferrer"
+                                target="_blank"
+                              >
+                                {t('learnMoreUpperCase')}
+                              </a>,
+                            ])}
+                          </Box>
+                        }
+                        trigger="mouseenter"
+                      >
+                        <i
+                          className="fa fa-exclamation-triangle add-network__warning-icon"
+                          title={t('warning')}
+                        />
+                      </Tooltip>
+                    )
+                  }
+                  <Button
+                    type="inline"
+                    className="add-network__add-button"
+                    onClick={async () => {
+                      await dispatch(requestUserApproval(item, true));
+                    }}
+                  >
+                    {t('add')}
+                  </Button>
+                </Box>
+              </Box>
+            ))}
           </Box>
-        ))}
-      </Box>
-      <Box
-        height={BLOCK_SIZES.ONE_TWELFTH}
-        padding={[4, 4, 4, 4]}
-        className="add-network__footer"
-      >
-        <Button type="link" onClick={onAddNetworkManuallyClick}>
-          <Typography variant={TYPOGRAPHY.H6} color={COLORS.PRIMARY_DEFAULT}>
-            {t('addANetworkManually')}
-          </Typography>
-        </Button>
-      </Box>
-    </Box>
+          <Box
+            padding={
+              getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
+                ? [2, 0, 2, 6]
+                : [2, 0, 2, 0]
+            }
+            className="add-network__footer"
+          >
+            <Button
+              type="link"
+              onClick={(event) => {
+                event.preventDefault();
+                getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
+                  ? global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE)
+                  : history.push(ADD_NETWORK_ROUTE);
+              }}
+            >
+              <Typography
+                variant={TYPOGRAPHY.H6}
+                color={COLORS.PRIMARY_DEFAULT}
+              >
+                {t('addANetworkManually')}
+              </Typography>
+            </Button>
+          </Box>
+        </Box>
+      )}
+      {showPopover && (
+        <Popover>
+          <ConfirmationPage />
+        </Popover>
+      )}
+    </>
   );
 };
 
-AddNetwork.propTypes = {
-  onBackClick: PropTypes.func,
-  onAddNetworkClick: PropTypes.func,
-  onAddNetworkManuallyClick: PropTypes.func,
-  featuredRPCS: PropTypes.array,
-};
-
 export default AddNetwork;
diff --git a/ui/components/app/add-network/add-network.test.js b/ui/components/app/add-network/add-network.test.js
new file mode 100644
index 000000000..d0272608f
--- /dev/null
+++ b/ui/components/app/add-network/add-network.test.js
@@ -0,0 +1,60 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { renderWithProvider } from '../../../../test/jest';
+import configureStore from '../../../store/store';
+import mockState from '../../../../test/data/mock-state.json';
+import AddNetwork from './add-network';
+
+jest.mock('../../../selectors', () => ({
+  getFrequentRpcListDetail: () => ({
+    frequentRpcList: [
+      {
+        chainId: '0x539',
+        nickname: 'Localhost 8545',
+        rpcPrefs: {},
+        rpcUrl: 'http://localhost:8545',
+        ticker: 'ETH',
+      },
+      {
+        chainId: '0xA4B1',
+        nickname: 'Arbitrum One',
+        rpcPrefs: { blockExplorerUrl: 'https://explorer.arbitrum.io' },
+        rpcUrl:
+          'https://arbitrum-mainnet.infura.io/v3/7e127583378c4732a858df2550aff333',
+        ticker: 'AETH',
+      },
+    ],
+  }),
+  getUnapprovedConfirmations: jest.fn(),
+  getTheme: () => 'light',
+}));
+
+const render = () => {
+  const store = configureStore({
+    metamask: {
+      ...mockState.metamask,
+    },
+  });
+  return renderWithProvider(<AddNetwork />, store);
+};
+
+describe('AddNetwork', () => {
+  it('should show Add from a list.. text', () => {
+    render();
+    expect(
+      screen.getByText(
+        'Add from a list of popular networks or add a network manually. Only interact with the entities you trust.',
+      ),
+    ).toBeInTheDocument();
+  });
+
+  it('should show Popular custom networks text', () => {
+    render();
+    expect(screen.getByText('Popular custom networks')).toBeInTheDocument();
+  });
+
+  it('should show Arbitrum One network nickname', () => {
+    render();
+    expect(screen.getByText('Arbitrum One')).toBeInTheDocument();
+  });
+});
diff --git a/ui/components/app/add-network/index.scss b/ui/components/app/add-network/index.scss
index 6da064bf5..4c7d9f909 100644
--- a/ui/components/app/add-network/index.scss
+++ b/ui/components/app/add-network/index.scss
@@ -1,10 +1,36 @@
 .add-network {
+  &__networks-container {
+    padding-inline-end: 24px;
+
+    @media screen and (max-width: $break-small) {
+      padding: 0;
+    }
+  }
+
   &__header {
     border-bottom: 1px solid var(--color-border-default);
 
-    &__back-icon {
-      margin-left: 24px;
-      margin-right: 16px;
+    @media screen and (max-width: 575px) {
+      padding-inline-start: 24px;
+      padding-inline-end: 24px;
+    }
+
+    &__subtitle {
+      margin-inline-start: 10px;
+      margin-inline-end: 10px;
+    }
+  }
+
+  &__main-container {
+    @media screen and (max-width: 575px) {
+      padding-inline-start: 24px;
+      padding-inline-end: 24px;
+    }
+  }
+
+  &__list-of-networks {
+    @media screen and (min-width: $break-large) {
+      width: 75%;
     }
   }
 
@@ -23,19 +49,25 @@
 
   &__add-icon {
     color: var(--color-text-alternative);
-    margin-left: auto;
-    margin-right: 0;
+    margin-inline-start: auto;
+    margin-inline-end: 0;
     cursor: pointer;
   }
 
   &__add-button.button {
     color: var(--color-primary-default);
     font-size: $font-size-h7;
-    margin-left: 24px;
+    margin-inline-start: 24px;
   }
 
   &__footer {
     border-top: 1px solid var(--color-border-muted);
+    width: 100%;
+    padding-bottom: 8px;
+
+    @media screen and (max-width: 575px) {
+      padding-inline-start: 24px !important;
+    }
 
     & .btn-link {
       display: initial;
@@ -51,6 +83,14 @@
       color: var(--color-text-alternative);
     }
   }
+
+  &__edge-case-box {
+    border: 1px solid var(--color-border-muted);
+
+    &__link {
+      color: var(--color-info-default);
+      display: inline;
+      padding: 0;
+    }
+  }
 }
-
-
diff --git a/ui/components/app/app-components.scss b/ui/components/app/app-components.scss
index 3920e5372..6fd148adc 100644
--- a/ui/components/app/app-components.scss
+++ b/ui/components/app/app-components.scss
@@ -14,6 +14,7 @@
 @import 'collectibles-items/index';
 @import 'collectibles-tab/index';
 @import 'collectible-details/index';
+@import 'collectible-default-image/index';
 @import 'collectible-options/index';
 @import 'collectibles-detection-notice/index';
 @import 'connected-accounts-list/index';
diff --git a/ui/components/app/asset-list-item/asset-list-item.js b/ui/components/app/asset-list-item/asset-list-item.js
index 77ab7f53d..bc19aff87 100644
--- a/ui/components/app/asset-list-item/asset-list-item.js
+++ b/ui/components/app/asset-list-item/asset-list-item.js
@@ -9,7 +9,7 @@ import Tooltip from '../../ui/tooltip';
 import InfoIcon from '../../ui/icon/info-icon.component';
 import Button from '../../ui/button';
 import { useI18nContext } from '../../../hooks/useI18nContext';
-import { updateSendAsset } from '../../../ducks/send';
+import { startNewDraftTransaction } from '../../../ducks/send';
 import { SEND_ROUTE } from '../../../helpers/constants/routes';
 import { SEVERITIES } from '../../../helpers/constants/design-system';
 import { INVALID_ASSET_TYPE } from '../../../helpers/constants/error-keys';
@@ -74,7 +74,7 @@ const AssetListItem = ({
           });
           try {
             await dispatch(
-              updateSendAsset({
+              startNewDraftTransaction({
                 type: ASSET_TYPES.TOKEN,
                 details: {
                   address: tokenAddress,
diff --git a/ui/components/app/collectible-default-image/collectible-default-image.js b/ui/components/app/collectible-default-image/collectible-default-image.js
new file mode 100644
index 000000000..c301c2c2c
--- /dev/null
+++ b/ui/components/app/collectible-default-image/collectible-default-image.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import Typography from '../../ui/typography';
+import { TYPOGRAPHY } from '../../../helpers/constants/design-system';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+
+export default function CollectibleDefaultImage({
+  name,
+  tokenId,
+  handleImageClick,
+}) {
+  const t = useI18nContext();
+  return (
+    <div
+      className={classnames('collectible-default', {
+        'collectible-default--clickable': handleImageClick,
+      })}
+      onClick={handleImageClick}
+    >
+      <Typography variant={TYPOGRAPHY.H6} className="collectible-default__text">
+        {name ?? t('unknownCollection')} <br /> #{tokenId}
+      </Typography>
+    </div>
+  );
+}
+
+CollectibleDefaultImage.propTypes = {
+  /**
+   * The name of the collectible collection if not supplied will default to "Unnamed collection"
+   */
+  name: PropTypes.string,
+  /**
+   * The token id of the collectible
+   */
+  tokenId: PropTypes.string,
+  /**
+   * The click handler for the collectible default image
+   */
+  handleImageClick: PropTypes.func,
+};
diff --git a/ui/components/app/collectible-default-image/collectible-default-image.stories.js b/ui/components/app/collectible-default-image/collectible-default-image.stories.js
new file mode 100644
index 000000000..d4b7a2a69
--- /dev/null
+++ b/ui/components/app/collectible-default-image/collectible-default-image.stories.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import CollectibleDefaultImage from '.';
+
+export default {
+  title: 'Components/App/CollectibleDefaultImage',
+  id: __filename,
+  argTypes: {
+    name: {
+      control: 'text',
+    },
+    tokenId: {
+      control: 'text',
+    },
+    handleImageClick: {
+      action: 'handleImageClick',
+    },
+  },
+  args: {
+    name: null,
+    tokenId: '12345',
+    handleImageClick: null,
+  },
+};
+
+export const DefaultStory = (args) => (
+  <div style={{ width: 200, height: 200 }}>
+    <CollectibleDefaultImage {...args} />
+  </div>
+);
+
+DefaultStory.storyName = 'Default';
+
+export const handleImageClick = (args) => (
+  <div style={{ width: 200, height: 200 }}>
+    <CollectibleDefaultImage {...args} />
+  </div>
+);
+
+handleImageClick.args = {
+  // eslint-disable-next-line no-alert
+  handleImageClick: () => window.alert('CollectibleDefaultImage clicked!'),
+};
diff --git a/ui/components/app/collectible-default-image/index.js b/ui/components/app/collectible-default-image/index.js
new file mode 100644
index 000000000..76ab58746
--- /dev/null
+++ b/ui/components/app/collectible-default-image/index.js
@@ -0,0 +1 @@
+export { default } from './collectible-default-image';
diff --git a/ui/components/app/collectible-default-image/index.scss b/ui/components/app/collectible-default-image/index.scss
new file mode 100644
index 000000000..ff2ba60e1
--- /dev/null
+++ b/ui/components/app/collectible-default-image/index.scss
@@ -0,0 +1,22 @@
+.collectible-default {
+  background-color: var(--color-background-alternative);
+  padding-top: 100%; // retains 1:1 aspect ratio
+  position: relative;
+  width: 100%;
+
+  &__text {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    text-align: center;
+    position: absolute;
+    white-space: nowrap;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    width: calc(100% - 32px);
+  }
+
+  &--clickable {
+    cursor: pointer;
+  }
+}
diff --git a/ui/components/app/collectible-details/collectible-details.js b/ui/components/app/collectible-details/collectible-details.js
index 5c8bf8e3a..df991634e 100644
--- a/ui/components/app/collectible-details/collectible-details.js
+++ b/ui/components/app/collectible-details/collectible-details.js
@@ -45,13 +45,14 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util';
 import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
 import CollectibleOptions from '../collectible-options/collectible-options';
 import Button from '../../ui/button';
-import { updateSendAsset } from '../../../ducks/send';
+import { startNewDraftTransaction } from '../../../ducks/send';
 import InfoTooltip from '../../ui/info-tooltip';
 import { ERC721 } from '../../../helpers/constants/common';
 import { usePrevious } from '../../../hooks/usePrevious';
 import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
 import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
 import { ASSET_TYPES } from '../../../../shared/constants/transaction';
+import CollectibleDefaultImage from '../collectible-default-image';
 
 export default function CollectibleDetails({ collectible }) {
   const {
@@ -119,7 +120,7 @@ export default function CollectibleDetails({ collectible }) {
 
   const onSend = async () => {
     await dispatch(
-      updateSendAsset({
+      startNewDraftTransaction({
         type: ASSET_TYPES.COLLECTIBLE,
         details: collectible,
       }),
@@ -176,7 +177,11 @@ export default function CollectibleDetails({ collectible }) {
             justifyContent={JUSTIFY_CONTENT.CENTER}
             className="collectible-details__card"
           >
-            <img className="collectible-details__image" src={image} />
+            {image ? (
+              <img className="collectible-details__image" src={image} />
+            ) : (
+              <CollectibleDefaultImage name={name} tokenId={tokenId} />
+            )}
           </Card>
           <Box
             flexDirection={FLEX_DIRECTION.COLUMN}
@@ -215,6 +220,7 @@ export default function CollectibleDetails({ collectible }) {
                 <Typography
                   color={COLORS.TEXT_ALTERNATIVE}
                   variant={TYPOGRAPHY.H6}
+                  overflowWrap={OVERFLOW_WRAP.BREAK_WORD}
                   boxProps={{ margin: 0, marginBottom: 4 }}
                 >
                   {description}
diff --git a/ui/components/app/collectible-details/collectible-details.stories.js b/ui/components/app/collectible-details/collectible-details.stories.js
index bbd18bac0..a40e81c02 100644
--- a/ui/components/app/collectible-details/collectible-details.stories.js
+++ b/ui/components/app/collectible-details/collectible-details.stories.js
@@ -1,16 +1,6 @@
 import React from 'react';
 import CollectibleDetails from './collectible-details';
 
-export default {
-  title: 'Components/App/CollectiblesDetail',
-  id: __filename,
-  argTypes: {
-    collectible: {
-      control: 'object',
-    },
-  },
-};
-
 const collectible = {
   name: 'Catnip Spicywright',
   tokenId: '1124157',
@@ -20,12 +10,32 @@ const collectible = {
     "Good day. My name is Catnip Spicywight, which got me teased a lot in high school. If I want to put low fat mayo all over my hamburgers, I shouldn't have to answer to anyone about it, am I right? One time I beat Arlene in an arm wrestle.",
 };
 
-export const DefaultStory = () => {
-  return <CollectibleDetails collectible={collectible} />;
+export default {
+  title: 'Components/App/CollectiblesDetail',
+  id: __filename,
+  argTypes: {
+    collectible: {
+      control: 'object',
+    },
+  },
+  args: {
+    collectible,
+  },
+};
+
+export const DefaultStory = (args) => {
+  return <CollectibleDetails {...args} />;
 };
 
 DefaultStory.storyName = 'Default';
 
-DefaultStory.args = {
-  collectible,
+export const NoImage = (args) => {
+  return <CollectibleDetails {...args} />;
+};
+
+NoImage.args = {
+  collectible: {
+    ...collectible,
+    image: undefined,
+  },
 };
diff --git a/ui/components/app/collectibles-items/collectibles-items.js b/ui/components/app/collectibles-items/collectibles-items.js
index 15e45b434..07f5d2eae 100644
--- a/ui/components/app/collectibles-items/collectibles-items.js
+++ b/ui/components/app/collectibles-items/collectibles-items.js
@@ -28,6 +28,8 @@ import { getAssetImageURL } from '../../../helpers/utils/util';
 import { updateCollectibleDropDownState } from '../../../store/actions';
 import { usePrevious } from '../../../hooks/usePrevious';
 import { getCollectiblesDropdownState } from '../../../ducks/metamask/metamask';
+import { useI18nContext } from '../../../hooks/useI18nContext';
+import CollectibleDefaultImage from '../collectible-default-image';
 
 const width =
   getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
@@ -46,6 +48,7 @@ export default function CollectiblesItems({
   const previousCollectionKeys = usePrevious(collectionsKeys);
   const selectedAddress = useSelector(getSelectedAddress);
   const chainId = useSelector(getCurrentChainId);
+  const t = useI18nContext();
 
   useEffect(() => {
     if (
@@ -101,7 +104,7 @@ export default function CollectiblesItems({
     }
     return (
       <div className="collectibles-items__collection-image-alt">
-        {collectionName[0]}
+        {collectionName?.[0]?.toUpperCase() ?? null}
       </div>
     );
   };
@@ -164,7 +167,9 @@ export default function CollectiblesItems({
                 variant={TYPOGRAPHY.H5}
                 margin={[0, 0, 0, 2]}
               >
-                {`${collectionName} (${collectibles.length})`}
+                {`${collectionName ?? t('unknownCollection')} (${
+                  collectibles.length
+                })`}
               </Typography>
             </Box>
             <Box alignItems={ALIGN_ITEMS.FLEX_END}>
@@ -180,29 +185,48 @@ export default function CollectiblesItems({
         {isExpanded ? (
           <Box display={DISPLAY.FLEX} flexWrap={FLEX_WRAP.WRAP} gap={4}>
             {collectibles.map((collectible, i) => {
-              const { image, address, tokenId, backgroundColor } = collectible;
+              const {
+                image,
+                address,
+                tokenId,
+                backgroundColor,
+                name,
+              } = collectible;
               const collectibleImage = getAssetImageURL(image, ipfsGateway);
+              const handleImageClick = () =>
+                history.push(`${ASSET_ROUTE}/${address}/${tokenId}`);
+
               return (
                 <Box
                   width={width}
                   key={`collectible-${i}`}
-                  className="collectibles-items__collection-item-wrapper"
+                  className="collectibles-items__item-wrapper"
                 >
-                  <Card padding={0} justifyContent={JUSTIFY_CONTENT.CENTER}>
-                    <div
-                      className="collectibles-items__collection-item"
-                      style={{
-                        backgroundColor,
-                      }}
-                    >
-                      <img
-                        onClick={() =>
-                          history.push(`${ASSET_ROUTE}/${address}/${tokenId}`)
-                        }
-                        className="collectibles-items__collection-item-image"
-                        src={collectibleImage}
+                  <Card
+                    padding={0}
+                    justifyContent={JUSTIFY_CONTENT.CENTER}
+                    className="collectibles-items__item-wrapper__card"
+                  >
+                    {collectibleImage ? (
+                      <div
+                        className="collectibles-items__item"
+                        style={{
+                          backgroundColor,
+                        }}
+                      >
+                        <img
+                          onClick={handleImageClick}
+                          className="collectibles-items__item-image"
+                          src={collectibleImage}
+                        />
+                      </div>
+                    ) : (
+                      <CollectibleDefaultImage
+                        name={name}
+                        tokenId={tokenId}
+                        handleImageClick={handleImageClick}
                       />
-                    </div>
+                    )}
                   </Card>
                 </Box>
               );
diff --git a/ui/components/app/collectibles-items/index.scss b/ui/components/app/collectibles-items/index.scss
index 7087ac481..545528fde 100644
--- a/ui/components/app/collectibles-items/index.scss
+++ b/ui/components/app/collectibles-items/index.scss
@@ -27,29 +27,33 @@
       color: var(--color-overlay-inverse);
       text-align: center;
     }
+  }
 
-    &-item-wrapper {
-      align-self: center;
+  &__item-wrapper {
+    align-self: center;
+
+    &__card {
+      overflow: hidden;
     }
+  }
 
-    &-item {
-      border-radius: 4px;
-      width: 100%;
-      display: flex;
-      justify-content: center;
-      cursor: pointer;
-      align-self: center;
-    }
+  &__item {
+    border-radius: 4px;
+    width: 100%;
+    display: flex;
+    justify-content: center;
+    cursor: pointer;
+    align-self: center;
 
-    &-item-image {
+    &-image {
       border-radius: 4px;
       width: 100%;
       height: 100%;
       cursor: pointer;
     }
+  }
 
-    &__icon-chevron {
-      color: var(--color-icon-default);
-    }
+  &__icon-chevron {
+    color: var(--color-icon-default);
   }
 }
diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js
index 2d5fb8193..e31e81bce 100644
--- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js
+++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js
@@ -53,7 +53,8 @@ const ConfirmPageContainerSummary = (props) => {
     contractAddress =
       transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER ||
       transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM ||
-      transactionType === TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM
+      transactionType === TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM ||
+      transactionType === TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL
         ? tokenAddress
         : toAddress;
   }
diff --git a/ui/components/app/dropdowns/network-dropdown.js b/ui/components/app/dropdowns/network-dropdown.js
index 33ac6af44..54af45b12 100644
--- a/ui/components/app/dropdowns/network-dropdown.js
+++ b/ui/components/app/dropdowns/network-dropdown.js
@@ -20,6 +20,7 @@ import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
 import { EVENT } from '../../../../shared/constants/metametrics';
 import {
   ADD_NETWORK_ROUTE,
+  ADD_POPULAR_CUSTOM_NETWORK,
   ADVANCED_ROUTE,
 } from '../../../helpers/constants/routes';
 import IconCheck from '../../ui/icon/icon-check';
@@ -49,6 +50,7 @@ function mapStateToProps(state) {
     frequentRpcListDetail: state.metamask.frequentRpcListDetail || [],
     networkDropdownOpen: state.appState.networkDropdownOpen,
     showTestnetMessageInDropdown: state.metamask.showTestnetMessageInDropdown,
+    addPopularNetworkFeatureToggledOn: state.metamask.customNetworkListEnabled,
   };
 }
 
@@ -101,6 +103,7 @@ class NetworkDropdown extends Component {
     showTestnetMessageInDropdown: PropTypes.bool.isRequired,
     hideTestNetMessage: PropTypes.func.isRequired,
     history: PropTypes.object,
+    addPopularNetworkFeatureToggledOn: PropTypes.bool,
   };
 
   handleClick(newProviderType) {
@@ -129,10 +132,12 @@ class NetworkDropdown extends Component {
         <Button
           type="secondary"
           onClick={() => {
-            if (getEnvironmentType() === ENVIRONMENT_TYPE_POPUP) {
-              global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE);
+            if (this.props.addPopularNetworkFeatureToggledOn) {
+              this.props.history.push(ADD_POPULAR_CUSTOM_NETWORK);
             } else {
-              this.props.history.push(ADD_NETWORK_ROUTE);
+              getEnvironmentType() === ENVIRONMENT_TYPE_POPUP
+                ? global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE)
+                : this.props.history.push(ADD_NETWORK_ROUTE);
             }
             this.props.hideNetworkDropdown();
           }}
diff --git a/ui/components/app/metamask-template-renderer/metamask-template-renderer.js b/ui/components/app/metamask-template-renderer/metamask-template-renderer.js
index 6aaca546b..ad15d03cb 100644
--- a/ui/components/app/metamask-template-renderer/metamask-template-renderer.js
+++ b/ui/components/app/metamask-template-renderer/metamask-template-renderer.js
@@ -43,6 +43,9 @@ const MetaMaskTemplateRenderer = ({ sections }) => {
   return (
     <>
       {sections.reduce((allChildren, child) => {
+        if (child?.hide === true) {
+          return allChildren;
+        }
         if (typeof child === 'string') {
           // React can render strings directly, so push them into the accumulator
           allChildren.push(child);
diff --git a/ui/components/app/metamask-template-renderer/safe-component-list.js b/ui/components/app/metamask-template-renderer/safe-component-list.js
index 29ded06ac..07aad3630 100644
--- a/ui/components/app/metamask-template-renderer/safe-component-list.js
+++ b/ui/components/app/metamask-template-renderer/safe-component-list.js
@@ -9,6 +9,8 @@ import MetaMaskTranslation from '../metamask-translation';
 import NetworkDisplay from '../network-display';
 import TextArea from '../../ui/textarea/textarea';
 import ConfirmationNetworkSwitch from '../../../pages/confirmation/components/confirmation-network-switch';
+import UrlIcon from '../../ui/url-icon';
+import Tooltip from '../../ui/tooltip/tooltip';
 
 export const safeComponentList = {
   MetaMaskTranslation,
@@ -27,4 +29,7 @@ export const safeComponentList = {
   NetworkDisplay,
   TextArea,
   ConfirmationNetworkSwitch,
+  UrlIcon,
+  Tooltip,
+  i: 'i',
 };
diff --git a/ui/components/app/srp-input/srp-input.js b/ui/components/app/srp-input/srp-input.js
index 1a3eb5810..d8b0a77de 100644
--- a/ui/components/app/srp-input/srp-input.js
+++ b/ui/components/app/srp-input/srp-input.js
@@ -35,7 +35,7 @@ export default function SrpInput({ onChange, srpText }) {
   const onSrpChange = useCallback(
     (newDraftSrp) => {
       let newSrpError = '';
-      const joinedDraftSrp = newDraftSrp.join(' ');
+      const joinedDraftSrp = newDraftSrp.join(' ').trim();
 
       if (newDraftSrp.some((word) => word !== '')) {
         if (newDraftSrp.some((word) => word === '')) {
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 331885364..f65c82f63 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
@@ -251,7 +251,10 @@ export default class TransactionListItemDetails extends PureComponent {
             <div className="transaction-list-item-details__cards-container">
               <TransactionBreakdown
                 nonce={transactionGroup.initialTransaction.txParams.nonce}
-                isTokenApprove={type === TRANSACTION_TYPES.TOKEN_METHOD_APPROVE}
+                isTokenApprove={
+                  type === TRANSACTION_TYPES.TOKEN_METHOD_APPROVE ||
+                  type === TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL
+                }
                 transaction={transaction}
                 primaryCurrency={primaryCurrency}
                 className="transaction-list-item-details__transaction-breakdown"
diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js
index 4b9e22a41..26b0019b1 100644
--- a/ui/components/app/wallet-overview/eth-overview.js
+++ b/ui/components/app/wallet-overview/eth-overview.js
@@ -33,6 +33,8 @@ import { isHardwareKeyring } from '../../../helpers/utils/hardware';
 import { MetaMetricsContext } from '../../../contexts/metametrics';
 import { EVENT } from '../../../../shared/constants/metametrics';
 import Spinner from '../../ui/spinner';
+import { startNewDraftTransaction } from '../../../ducks/send';
+import { ASSET_TYPES } from '../../../../shared/constants/transaction';
 import WalletOverview from './wallet-overview';
 
 const EthOverview = ({ className }) => {
@@ -131,7 +133,11 @@ const EthOverview = ({ className }) => {
                   legacy_event: true,
                 },
               });
-              history.push(SEND_ROUTE);
+              dispatch(
+                startNewDraftTransaction({ type: ASSET_TYPES.NATIVE }),
+              ).then(() => {
+                history.push(SEND_ROUTE);
+              });
             }}
           />
           <IconButton
diff --git a/ui/components/app/wallet-overview/token-overview.js b/ui/components/app/wallet-overview/token-overview.js
index 23d8997d8..a02f0263e 100644
--- a/ui/components/app/wallet-overview/token-overview.js
+++ b/ui/components/app/wallet-overview/token-overview.js
@@ -14,7 +14,7 @@ import {
 } from '../../../helpers/constants/routes';
 import { useTokenTracker } from '../../../hooks/useTokenTracker';
 import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
-import { updateSendAsset } from '../../../ducks/send';
+import { startNewDraftTransaction } from '../../../ducks/send';
 import { setSwapsFromToken } from '../../../ducks/swaps/swaps';
 import {
   getCurrentKeyring,
@@ -93,7 +93,7 @@ const TokenOverview = ({ className, token }) => {
               });
               try {
                 await dispatch(
-                  updateSendAsset({
+                  startNewDraftTransaction({
                     type: ASSET_TYPES.TOKEN,
                     details: token,
                   }),
diff --git a/ui/components/ui/chip/chip.js b/ui/components/ui/chip/chip.js
index 21b4fe13f..fa217e31d 100644
--- a/ui/components/ui/chip/chip.js
+++ b/ui/components/ui/chip/chip.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
 import classnames from 'classnames';
 import { omit } from 'lodash';
 import Typography from '../typography';
+import UrlIcon from '../url-icon';
 import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system';
 
 export default function Chip({
@@ -14,9 +15,11 @@ export default function Chip({
   label,
   labelProps = {},
   leftIcon,
+  leftIconUrl = '',
   rightIcon,
   onClick,
   maxContent = true,
+  displayInlineBlock = false,
 }) {
   const onKeyPress = (event) => {
     if (event.key === 'Enter' && onClick) {
@@ -37,11 +40,17 @@ export default function Chip({
         [`chip--border-color-${borderColor}`]: true,
         [`chip--background-color-${backgroundColor}`]: true,
         'chip--max-content': maxContent,
+        'chip--display-inline-block': displayInlineBlock,
       })}
       role={isInteractive ? 'button' : undefined}
       tabIndex={isInteractive ? 0 : undefined}
     >
-      {leftIcon ? <div className="chip__left-icon">{leftIcon}</div> : null}
+      {leftIcon && !leftIconUrl ? (
+        <div className="chip__left-icon">{leftIcon}</div>
+      ) : null}
+      {leftIconUrl ? (
+        <UrlIcon className="chip__left-url-icon" url={leftIconUrl} />
+      ) : null}
       {children ?? (
         <Typography
           className="chip__label"
@@ -106,4 +115,12 @@ Chip.propTypes = {
    * max-content can overflow the parent's width and break designs
    */
   maxContent: PropTypes.bool,
+  /**
+   * Icon location
+   */
+  leftIconUrl: PropTypes.string,
+  /**
+   * Display or not the inline block
+   */
+  displayInlineBlock: PropTypes.bool,
 };
diff --git a/ui/components/ui/chip/chip.scss b/ui/components/ui/chip/chip.scss
index 7eba44619..b0a430819 100644
--- a/ui/components/ui/chip/chip.scss
+++ b/ui/components/ui/chip/chip.scss
@@ -16,6 +16,10 @@
     align-items: center;
   }
 
+  &__left-url-icon {
+    margin-right: 8px;
+  };
+
   @each $variant, $color in design-system.$color-map {
     &--border-color-#{$variant} {
       border-color: var($color);
@@ -67,4 +71,8 @@
   &--max-content {
     width: max-content;
   }
+
+  &--display-inline-block {
+    display: inline-block;
+  }
 }
diff --git a/ui/components/ui/definition-list/definition-list.js b/ui/components/ui/definition-list/definition-list.js
index 05ca7b912..2d1560814 100644
--- a/ui/components/ui/definition-list/definition-list.js
+++ b/ui/components/ui/definition-list/definition-list.js
@@ -7,6 +7,7 @@ import {
   SIZES,
   TYPOGRAPHY,
   FONT_WEIGHT,
+  OVERFLOW_WRAP,
 } from '../../../helpers/constants/design-system';
 import Tooltip from '../tooltip';
 
@@ -60,6 +61,7 @@ export default function DefinitionList({
               marginBottom: MARGIN_MAP[gapSize],
             }}
             className="definition-list__definition"
+            overflowWrap={OVERFLOW_WRAP.BREAK_WORD}
             tag="dd"
           >
             {definition}
diff --git a/ui/components/ui/token-input/token-input.component.js b/ui/components/ui/token-input/token-input.component.js
index 6de1191dc..5f2cab056 100644
--- a/ui/components/ui/token-input/token-input.component.js
+++ b/ui/components/ui/token-input/token-input.component.js
@@ -118,7 +118,7 @@ export default class TokenInput extends PureComponent {
       isEqualCaseInsensitive(address, token.address),
     );
 
-    const tokenExchangeRate = tokenExchangeRates?.[existingToken.address] || 0;
+    const tokenExchangeRate = tokenExchangeRates?.[existingToken?.address] ?? 0;
     let currency, numberOfDecimals;
 
     if (hideConversion) {
diff --git a/ui/ducks/app/app.js b/ui/ducks/app/app.js
index 93267bd27..94c3bb6f2 100644
--- a/ui/ducks/app/app.js
+++ b/ui/ducks/app/app.js
@@ -60,6 +60,7 @@ export default function reduceApp(state = {}, action) {
     newCollectibleAddedMessage: '',
     sendInputCurrencySwitched: false,
     newTokensImported: '',
+    newCustomNetworkAdded: {},
     ...state,
   };
 
@@ -393,6 +394,11 @@ export default function reduceApp(state = {}, action) {
         ...appState,
         sendInputCurrencySwitched: !appState.sendInputCurrencySwitched,
       };
+    case actionConstants.SET_NEW_CUSTOM_NETWORK_ADDED:
+      return {
+        ...appState,
+        newCustomNetworkAdded: action.value,
+      };
     default:
       return appState;
   }
@@ -444,3 +450,7 @@ export function getLedgerTransportStatus(state) {
 export function toggleCurrencySwitch() {
   return { type: actionConstants.TOGGLE_CURRENCY_INPUT_SWITCH };
 }
+
+export function setNewCustomNetworkAdded(value) {
+  return { type: actionConstants.SET_NEW_CUSTOM_NETWORK_ADDED, value };
+}
diff --git a/ui/ducks/send/helpers.js b/ui/ducks/send/helpers.js
new file mode 100644
index 000000000..f1233a9c1
--- /dev/null
+++ b/ui/ducks/send/helpers.js
@@ -0,0 +1,295 @@
+import { addHexPrefix } from 'ethereumjs-util';
+import abi from 'human-standard-token-abi';
+import { GAS_LIMITS } from '../../../shared/constants/gas';
+import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network';
+import {
+  ASSET_TYPES,
+  TRANSACTION_ENVELOPE_TYPES,
+} from '../../../shared/constants/transaction';
+import { readAddressAsContract } from '../../../shared/modules/contract-utils';
+import {
+  conversionUtil,
+  multiplyCurrencies,
+} from '../../../shared/modules/conversion.utils';
+import { ETH, GWEI } from '../../helpers/constants/common';
+import { calcTokenAmount } from '../../helpers/utils/token-util';
+import { MIN_GAS_LIMIT_HEX } from '../../pages/send/send.constants';
+import {
+  addGasBuffer,
+  generateERC20TransferData,
+  generateERC721TransferData,
+  getAssetTransferData,
+} from '../../pages/send/send.utils';
+import { getGasPriceInHexWei } from '../../selectors';
+import { estimateGas } from '../../store/actions';
+
+export async function estimateGasLimitForSend({
+  selectedAddress,
+  value,
+  gasPrice,
+  sendToken,
+  to,
+  data,
+  isNonStandardEthChain,
+  chainId,
+  gasLimit,
+  ...options
+}) {
+  let isSimpleSendOnNonStandardNetwork = false;
+
+  // blockGasLimit may be a falsy, but defined, value when we receive it from
+  // state, so we use logical or to fall back to MIN_GAS_LIMIT_HEX. Some
+  // network implementations check the gas parameter supplied to
+  // eth_estimateGas for validity. For this reason, we set token sends
+  // blockGasLimit default to a higher number. Note that the current gasLimit
+  // on a BLOCK is 15,000,000 and will be 30,000,000 on mainnet after London.
+  // Meanwhile, MIN_GAS_LIMIT_HEX is 0x5208.
+  let blockGasLimit = MIN_GAS_LIMIT_HEX;
+  if (options.blockGasLimit) {
+    blockGasLimit = options.blockGasLimit;
+  } else if (sendToken) {
+    blockGasLimit = GAS_LIMITS.BASE_TOKEN_ESTIMATE;
+  }
+
+  // The parameters below will be sent to our background process to estimate
+  // how much gas will be used for a transaction. That background process is
+  // located in tx-gas-utils.js in the transaction controller folder.
+  const paramsForGasEstimate = { from: selectedAddress, value, gasPrice };
+
+  if (sendToken) {
+    if (!to) {
+      // If no to address is provided, we cannot generate the token transfer
+      // hexData. hexData in a transaction largely dictates how much gas will
+      // be consumed by a transaction. We must use our best guess, which is
+      // represented in the gas shared constants.
+      return GAS_LIMITS.BASE_TOKEN_ESTIMATE;
+    }
+    paramsForGasEstimate.value = '0x0';
+
+    // We have to generate the erc20/erc721 contract call to transfer tokens in
+    // order to get a proper estimate for gasLimit.
+    paramsForGasEstimate.data = getAssetTransferData({
+      sendToken,
+      fromAddress: selectedAddress,
+      toAddress: to,
+      amount: value,
+    });
+
+    paramsForGasEstimate.to = sendToken.address;
+  } else {
+    if (!data) {
+      // eth.getCode will return the compiled smart contract code at the
+      // address. If this returns 0x, 0x0 or a nullish value then the address
+      // is an externally owned account (NOT a contract account). For these
+      // types of transactions the gasLimit will always be 21,000 or 0x5208
+      const { isContractAddress } = to
+        ? await readAddressAsContract(global.eth, to)
+        : {};
+      if (!isContractAddress && !isNonStandardEthChain) {
+        return GAS_LIMITS.SIMPLE;
+      } else if (!isContractAddress && isNonStandardEthChain) {
+        isSimpleSendOnNonStandardNetwork = true;
+      }
+    }
+
+    paramsForGasEstimate.data = data;
+
+    if (to) {
+      paramsForGasEstimate.to = to;
+    }
+
+    if (!value || value === '0') {
+      // TODO: Figure out what's going on here. According to eth_estimateGas
+      // docs this value can be zero, or undefined, yet we are setting it to a
+      // value here when the value is undefined or zero. For more context:
+      // https://github.com/MetaMask/metamask-extension/pull/6195
+      paramsForGasEstimate.value = '0xff';
+    }
+  }
+
+  if (!isSimpleSendOnNonStandardNetwork) {
+    // If we do not yet have a gasLimit, we must call into our background
+    // process to get an estimate for gasLimit based on known parameters.
+
+    paramsForGasEstimate.gas = addHexPrefix(
+      multiplyCurrencies(blockGasLimit, 0.95, {
+        multiplicandBase: 16,
+        multiplierBase: 10,
+        roundDown: '0',
+        toNumericBase: 'hex',
+      }),
+    );
+  }
+
+  // The buffer multipler reduces transaction failures by ensuring that the
+  // estimated gas is always sufficient. Without the multiplier, estimates
+  // for contract interactions can become inaccurate over time. This is because
+  // gas estimation is non-deterministic. The gas required for the exact same
+  // transaction call can change based on state of a contract or changes in the
+  // contracts environment (blockchain data or contracts it interacts with).
+  // Applying the 1.5 buffer has proven to be a useful guard against this non-
+  // deterministic behaviour.
+  //
+  // Gas estimation of simple sends should, however, be deterministic. As such
+  // no buffer is needed in those cases.
+  let bufferMultiplier = 1.5;
+  if (isSimpleSendOnNonStandardNetwork) {
+    bufferMultiplier = 1;
+  } else if (CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId]) {
+    bufferMultiplier = CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId];
+  }
+
+  try {
+    // Call into the background process that will simulate transaction
+    // execution on the node and return an estimate of gasLimit
+    const estimatedGasLimit = await estimateGas(paramsForGasEstimate);
+    const estimateWithBuffer = addGasBuffer(
+      estimatedGasLimit,
+      blockGasLimit,
+      bufferMultiplier,
+    );
+    return addHexPrefix(estimateWithBuffer);
+  } catch (error) {
+    const simulationFailed =
+      error.message.includes('Transaction execution error.') ||
+      error.message.includes(
+        'gas required exceeds allowance or always failing transaction',
+      ) ||
+      (CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId] &&
+        error.message.includes('gas required exceeds allowance'));
+    if (simulationFailed) {
+      const estimateWithBuffer = addGasBuffer(
+        paramsForGasEstimate?.gas ?? gasLimit,
+        blockGasLimit,
+        bufferMultiplier,
+      );
+      return addHexPrefix(estimateWithBuffer);
+    }
+    throw error;
+  }
+}
+
+/**
+ * Generates a txParams from the send slice.
+ *
+ * @param {import('.').SendState} sendState - the state of the send slice
+ * @returns {import(
+ *  '../../../shared/constants/transaction'
+ * ).TxParams} A txParams object that can be used to create a transaction or
+ *  update an existing transaction.
+ */
+export function generateTransactionParams(sendState) {
+  const draftTransaction =
+    sendState.draftTransactions[sendState.currentTransactionUUID];
+  const txParams = {
+    // If the fromAccount has been specified we use that, if not we use the
+    // selected account.
+    from:
+      draftTransaction.fromAccount?.address ||
+      sendState.selectedAccount.address,
+    // gasLimit always needs to be set regardless of the asset being sent
+    // or the type of transaction.
+    gas: draftTransaction.gas.gasLimit,
+  };
+  switch (draftTransaction.asset.type) {
+    case ASSET_TYPES.TOKEN:
+      // When sending a token the to address is the contract address of
+      // the token being sent. The value is set to '0x0' and the data
+      // is generated from the recipient address, token being sent and
+      // amount.
+      txParams.to = draftTransaction.asset.details.address;
+      txParams.value = '0x0';
+      txParams.data = generateERC20TransferData({
+        toAddress: draftTransaction.recipient.address,
+        amount: draftTransaction.amount.value,
+        sendToken: draftTransaction.asset.details,
+      });
+      break;
+    case ASSET_TYPES.COLLECTIBLE:
+      // When sending a token the to address is the contract address of
+      // the token being sent. The value is set to '0x0' and the data
+      // is generated from the recipient address, token being sent and
+      // amount.
+      txParams.to = draftTransaction.asset.details.address;
+      txParams.value = '0x0';
+      txParams.data = generateERC721TransferData({
+        toAddress: draftTransaction.recipient.address,
+        fromAddress:
+          draftTransaction.fromAccount?.address ??
+          sendState.selectedAccount.address,
+        tokenId: draftTransaction.asset.details.tokenId,
+      });
+      break;
+    case ASSET_TYPES.NATIVE:
+    default:
+      // When sending native currency the to and value fields use the
+      // recipient and amount values and the data key is either null or
+      // populated with the user input provided in hex field.
+      txParams.to = draftTransaction.recipient.address;
+      txParams.value = draftTransaction.amount.value;
+      txParams.data = draftTransaction.userInputHexData ?? undefined;
+  }
+
+  // We need to make sure that we only include the right gas fee fields
+  // based on the type of transaction the network supports. We will also set
+  // the type param here.
+  if (sendState.eip1559support) {
+    txParams.type = TRANSACTION_ENVELOPE_TYPES.FEE_MARKET;
+
+    txParams.maxFeePerGas = draftTransaction.gas.maxFeePerGas;
+    txParams.maxPriorityFeePerGas = draftTransaction.gas.maxPriorityFeePerGas;
+
+    if (!txParams.maxFeePerGas || txParams.maxFeePerGas === '0x0') {
+      txParams.maxFeePerGas = draftTransaction.gas.gasPrice;
+    }
+
+    if (
+      !txParams.maxPriorityFeePerGas ||
+      txParams.maxPriorityFeePerGas === '0x0'
+    ) {
+      txParams.maxPriorityFeePerGas = txParams.maxFeePerGas;
+    }
+  } else {
+    txParams.gasPrice = draftTransaction.gas.gasPrice;
+    txParams.type = TRANSACTION_ENVELOPE_TYPES.LEGACY;
+  }
+
+  return txParams;
+}
+
+/**
+ * This method is used to keep the original logic from the gas.duck.js file
+ * after receiving a gasPrice from eth_gasPrice. First, the returned gasPrice
+ * was converted to GWEI, then it was converted to a Number, then in the send
+ * duck (here) we would use getGasPriceInHexWei to get back to hexWei. Now that
+ * we receive a GWEI estimate from the controller, we still need to do this
+ * weird conversion to get the proper rounding.
+ *
+ * @param {string} gasPriceEstimate
+ * @returns {string}
+ */
+export function getRoundedGasPrice(gasPriceEstimate) {
+  const gasPriceInDecGwei = conversionUtil(gasPriceEstimate, {
+    numberOfDecimals: 9,
+    toDenomination: GWEI,
+    fromNumericBase: 'dec',
+    toNumericBase: 'dec',
+    fromCurrency: ETH,
+    fromDenomination: GWEI,
+  });
+  const gasPriceAsNumber = Number(gasPriceInDecGwei);
+  return getGasPriceInHexWei(gasPriceAsNumber);
+}
+
+export async function getERC20Balance(token, accountAddress) {
+  const contract = global.eth.contract(abi).at(token.address);
+  const usersToken = (await contract.balanceOf(accountAddress)) ?? null;
+  if (!usersToken) {
+    return '0x0';
+  }
+  const amount = calcTokenAmount(
+    usersToken.balance.toString(),
+    token.decimals,
+  ).toString(16);
+  return addHexPrefix(amount);
+}
diff --git a/ui/ducks/send/helpers.test.js b/ui/ducks/send/helpers.test.js
new file mode 100644
index 000000000..a8ec656a9
--- /dev/null
+++ b/ui/ducks/send/helpers.test.js
@@ -0,0 +1,163 @@
+import { ethers } from 'ethers';
+import { GAS_LIMITS } from '../../../shared/constants/gas';
+import {
+  ASSET_TYPES,
+  TRANSACTION_ENVELOPE_TYPES,
+} from '../../../shared/constants/transaction';
+import { BURN_ADDRESS } from '../../../shared/modules/hexstring-utils';
+import { getInitialSendStateWithExistingTxState } from '../../../test/jest/mocks';
+import { TOKEN_STANDARDS } from '../../helpers/constants/common';
+import {
+  generateERC20TransferData,
+  generateERC721TransferData,
+} from '../../pages/send/send.utils';
+import { generateTransactionParams } from './helpers';
+
+describe('Send Slice Helpers', () => {
+  describe('generateTransactionParams', () => {
+    it('should generate a txParams for a token transfer', () => {
+      const tokenDetails = {
+        address: '0xToken',
+        symbol: 'SYMB',
+        decimals: 18,
+      };
+      const txParams = generateTransactionParams(
+        getInitialSendStateWithExistingTxState({
+          fromAccount: {
+            address: '0x00',
+          },
+          amount: {
+            value: '0x1',
+          },
+          asset: {
+            type: ASSET_TYPES.TOKEN,
+            balance: '0xaf',
+            details: tokenDetails,
+          },
+          recipient: {
+            address: BURN_ADDRESS,
+          },
+        }),
+      );
+      expect(txParams).toStrictEqual({
+        from: '0x00',
+        data: generateERC20TransferData({
+          toAddress: BURN_ADDRESS,
+          amount: '0x1',
+          sendToken: tokenDetails,
+        }),
+        to: '0xToken',
+        type: '0x0',
+        value: '0x0',
+        gas: '0x0',
+        gasPrice: '0x0',
+      });
+    });
+
+    it('should generate a txParams for a collectible transfer', () => {
+      const txParams = generateTransactionParams(
+        getInitialSendStateWithExistingTxState({
+          fromAccount: {
+            address: '0x00',
+          },
+          amount: {
+            value: '0x1',
+          },
+          asset: {
+            type: ASSET_TYPES.COLLECTIBLE,
+            balance: '0xaf',
+            details: {
+              address: '0xToken',
+              standard: TOKEN_STANDARDS.ERC721,
+              tokenId: ethers.BigNumber.from(15000).toString(),
+            },
+          },
+          recipient: {
+            address: BURN_ADDRESS,
+          },
+        }),
+      );
+      expect(txParams).toStrictEqual({
+        from: '0x00',
+        data: generateERC721TransferData({
+          toAddress: BURN_ADDRESS,
+          fromAddress: '0x00',
+          tokenId: ethers.BigNumber.from(15000).toString(),
+        }),
+        to: '0xToken',
+        type: '0x0',
+        value: '0x0',
+        gas: '0x0',
+        gasPrice: '0x0',
+      });
+    });
+
+    it('should generate a txParams for a native legacy transaction', () => {
+      const txParams = generateTransactionParams(
+        getInitialSendStateWithExistingTxState({
+          fromAccount: {
+            address: '0x00',
+          },
+          amount: {
+            value: '0x1',
+          },
+          asset: {
+            type: ASSET_TYPES.NATIVE,
+            balance: '0xaf',
+            details: null,
+          },
+          recipient: {
+            address: BURN_ADDRESS,
+          },
+        }),
+      );
+      expect(txParams).toStrictEqual({
+        from: '0x00',
+        data: undefined,
+        to: BURN_ADDRESS,
+        type: '0x0',
+        value: '0x1',
+        gas: '0x0',
+        gasPrice: '0x0',
+      });
+    });
+
+    it('should generate a txParams for a native fee market transaction', () => {
+      const txParams = generateTransactionParams({
+        ...getInitialSendStateWithExistingTxState({
+          fromAccount: {
+            address: '0x00',
+          },
+          amount: {
+            value: '0x1',
+          },
+          asset: {
+            type: ASSET_TYPES.NATIVE,
+            balance: '0xaf',
+            details: null,
+          },
+          recipient: {
+            address: BURN_ADDRESS,
+          },
+          gas: {
+            maxFeePerGas: '0x2',
+            maxPriorityFeePerGas: '0x1',
+            gasLimit: GAS_LIMITS.SIMPLE,
+          },
+          transactionType: TRANSACTION_ENVELOPE_TYPES.FEE_MARKET,
+        }),
+        eip1559support: true,
+      });
+      expect(txParams).toStrictEqual({
+        from: '0x00',
+        data: undefined,
+        to: BURN_ADDRESS,
+        type: '0x2',
+        value: '0x1',
+        gas: GAS_LIMITS.SIMPLE,
+        maxFeePerGas: '0x2',
+        maxPriorityFeePerGas: '0x1',
+      });
+    });
+  });
+});
diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js
index 2b3d59f12..4ca6b6ca4 100644
--- a/ui/ducks/send/send.js
+++ b/ui/ducks/send/send.js
@@ -1,8 +1,8 @@
 import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
-import abi from 'human-standard-token-abi';
 import BigNumber from 'bignumber.js';
 import { addHexPrefix } from 'ethereumjs-util';
 import { debounce } from 'lodash';
+import { v4 as uuidv4 } from 'uuid';
 import {
   conversionGreaterThan,
   conversionUtil,
@@ -17,26 +17,19 @@ import {
   INVALID_RECIPIENT_ADDRESS_ERROR,
   INVALID_RECIPIENT_ADDRESS_NOT_ETH_NETWORK_ERROR,
   KNOWN_RECIPIENT_ADDRESS_WARNING,
-  MIN_GAS_LIMIT_HEX,
   NEGATIVE_ETH_ERROR,
 } from '../../pages/send/send.constants';
 
 import {
-  addGasBuffer,
   calcGasTotal,
-  generateERC20TransferData,
-  generateERC721TransferData,
-  getAssetTransferData,
   isBalanceSufficient,
   isTokenBalanceSufficient,
 } from '../../pages/send/send.utils';
 import {
-  getAddressBookEntry,
   getAdvancedInlineGasShown,
   getCurrentChainId,
   getGasPriceInHexWei,
   getIsMainnet,
-  getSelectedAddress,
   getTargetAccount,
   getIsNonStandardEthChain,
   checkNetworkAndAccountSupports1559,
@@ -45,11 +38,12 @@ import {
   getAddressBookEntryOrAccountName,
   getIsMultiLayerFeeNetwork,
   getEnsResolutionByAddress,
+  getSelectedAccount,
+  getSelectedAddress,
 } from '../../selectors';
 import {
   disconnectGasFeeEstimatePoller,
   displayWarning,
-  estimateGas,
   getGasFeeEstimatesAndStartPolling,
   hideLoadingIndication,
   showLoadingIndication,
@@ -75,6 +69,7 @@ import {
   calcTokenAmount,
   getTokenAddressParam,
   getTokenValueParam,
+  getTokenMetadata,
 } from '../../helpers/utils/token-util';
 import {
   checkExistingAddresses,
@@ -97,17 +92,21 @@ import {
 import { sumHexes } from '../../helpers/utils/transactions.util';
 import fetchEstimatedL1Fee from '../../helpers/utils/optimism/fetchEstimatedL1Fee';
 
-import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network';
-import { TOKEN_STANDARDS, ETH, GWEI } from '../../helpers/constants/common';
+import { TOKEN_STANDARDS, ETH } from '../../helpers/constants/common';
 import {
   ASSET_TYPES,
   TRANSACTION_ENVELOPE_TYPES,
   TRANSACTION_TYPES,
 } from '../../../shared/constants/transaction';
-import { readAddressAsContract } from '../../../shared/modules/contract-utils';
 import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys';
 import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
 import { getValueFromWeiHex } from '../../helpers/utils/confirm-tx.util';
+import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils';
+import {
+  estimateGasLimitForSend,
+  generateTransactionParams,
+  getRoundedGasPrice,
+} from './helpers';
 // typedef import statements
 /**
  * @typedef {(
@@ -120,6 +119,9 @@ import { getValueFromWeiHex } from '../../helpers/utils/confirm-tx.util';
  *  import( '../../helpers/constants/common').TokenStandardStrings
  * )} TokenStandardStrings
  * @typedef {(
+ *  import( '../../../shared/constants/tokens').TokenDetails
+ * )} TokenDetails
+ * @typedef {(
  *  import('../../../shared/constants/transaction').TransactionTypeString
  * )} TransactionTypeString
  * @typedef {(
@@ -134,14 +136,28 @@ import { getValueFromWeiHex } from '../../helpers/utils/confirm-tx.util';
  * @typedef {(
  *  import('@metamask/controllers').GasEstimateType
  * )} GasEstimateType
+ * @typedef {(
+ *  import('redux').AnyAction
+ * )} AnyAction
  */
 
-const name = 'send';
+/**
+ * @template R - Return type of the async function
+ * @typedef {(
+ *  import('redux-thunk').ThunkAction<R, MetaMaskState, unknown, AnyAction>
+ * )} ThunkAction<R>
+ */
+
+/**
+ * This type will take a typical constant string mapped object and turn it into
+ * a union type of the values.
+ *
+ * @template O - The object to make strings out of
+ * @typedef {O[keyof O]} MapValuesToUnion<O>
+ */
 
 /**
  * @typedef {Object} SendStateStages
- * @property {'INACTIVE'} INACTIVE - The send state is idle, and hasn't yet
- *  fetched required data for gasPrice and gasLimit estimations, etc.
  * @property {'ADD_RECIPIENT'} ADD_RECIPIENT - The user is selecting which
  *  address to send an asset to.
  * @property {'DRAFT'} DRAFT - The send form is shown for a transaction yet to
@@ -150,13 +166,8 @@ const name = 'send';
  *  submitted to the Transaction Controller but not yet confirmed. This happens
  *  when a confirmation is shown for a transaction and the 'edit' button in the
  *  header is clicked.
- */
-
-/**
- * This type will work anywhere you expect a string that can be one of the
- * above Stages
- *
- * @typedef {SendStateStages[keyof SendStateStages]} SendStateStagesStrings
+ * @property {'INACTIVE'} INACTIVE - The send state is idle, and hasn't yet
+ *  fetched required data for gasPrice and gasLimit estimations, etc.
  */
 
 /**
@@ -165,15 +176,14 @@ const name = 'send';
  * @type {SendStateStages}
  */
 export const SEND_STAGES = {
-  INACTIVE: 'INACTIVE',
   ADD_RECIPIENT: 'ADD_RECIPIENT',
   DRAFT: 'DRAFT',
   EDIT: 'EDIT',
+  INACTIVE: 'INACTIVE',
 };
 
 /**
- * @typedef {Object} SendStateStatuses
- * @property {'VALID'} VALID - The transaction is valid and can be submitted.
+ * @typedef {Object} DraftTxStatus
  * @property {'INVALID'} INVALID - The transaction is invalid and cannot be
  *  submitted. There are a number of cases that would result in an invalid
  *  send state:
@@ -184,41 +194,28 @@ export const SEND_STAGES = {
  *  4. The amount of sent asset is greater than the user's *asset* balance
  *  5. Gas price estimates failed to load entirely
  *  6. The gasLimit is less than 21000 (0x5208)
- */
-
-/**
- * This type will work anywhere you expect a string that can be one of the
- * above statuses
- *
- * @typedef {SendStateStatuses[keyof SendStateStatuses]} SendStateStatusStrings
+ * @property {'VALID'} VALID - The transaction is valid and can be submitted.
  */
 
 /**
  * The status of the send slice
  *
- * @type {SendStateStatuses}
+ * @type {DraftTxStatus}
  */
 export const SEND_STATUSES = {
-  VALID: 'VALID',
   INVALID: 'INVALID',
+  VALID: 'VALID',
 };
 
 /**
  * @typedef {Object} SendStateGasModes
  * @property {'BASIC'} BASIC - Shows the basic estimate slow/avg/fast buttons
  *  when on mainnet and the metaswaps API request is successful.
- * @property {'INLINE'} INLINE - Shows inline gasLimit/gasPrice fields when on
- *  any other network or metaswaps API fails and we use eth_gasPrice.
  * @property {'CUSTOM'} CUSTOM - Shows GasFeeDisplay component that is a read
  *  only display of the values the user has set in the advanced gas modal
  *  (stored in the gas duck under the customData key).
- */
-
-/**
- * This type will work anywhere you expect a string that can be one of the
- * above gas modes
- *
- * @typedef {SendStateGasModes[keyof SendStateGasModes]} SendStateGasModeStrings
+ * @property {'INLINE'} INLINE - Shows inline gasLimit/gasPrice fields when on
+ *  any other network or metaswaps API fails and we use eth_gasPrice.
  */
 
 /**
@@ -228,8 +225,8 @@ export const SEND_STATUSES = {
  */
 export const GAS_INPUT_MODES = {
   BASIC: 'BASIC',
-  INLINE: 'INLINE',
   CUSTOM: 'CUSTOM',
+  INLINE: 'INLINE',
 };
 
 /**
@@ -240,13 +237,6 @@ export const GAS_INPUT_MODES = {
  *  calculated based on balance - (amount + gasTotal).
  */
 
-/**
- * This type will work anywhere you expect a string that can be one of the
- * above gas modes
- *
- * @typedef {SendStateAmountModes[keyof SendStateAmountModes]} SendStateAmountModeStrings
- */
-
 /**
  * The modes that the amount field can be set by
  *
@@ -259,17 +249,10 @@ export const AMOUNT_MODES = {
 
 /**
  * @typedef {Object} SendStateRecipientModes
- * @property {'MY_ACCOUNTS'} MY_ACCOUNTS - the user is displayed a list of
- *  their own accounts to send to.
  * @property {'CONTACT_LIST'} CONTACT_LIST - The user is displayed a list of
  *  their contacts and addresses they have recently send to.
- */
-
-/**
- * This type will work anywhere you expect a string that can be one of the
- * above recipient modes
- *
- * @typedef {SendStateRecipientModes[keyof SendStateRecipientModes]} SendStateRecipientModeStrings
+ * @property {'MY_ACCOUNTS'} MY_ACCOUNTS - the user is displayed a list of
+ *  their own accounts to send to.
  */
 
 /**
@@ -278,168 +261,217 @@ export const AMOUNT_MODES = {
  * @type {SendStateRecipientModes}
  */
 export const RECIPIENT_SEARCH_MODES = {
-  MY_ACCOUNTS: 'MY_ACCOUNTS',
   CONTACT_LIST: 'CONTACT_LIST',
+  MY_ACCOUNTS: 'MY_ACCOUNTS',
 };
 
-async function estimateGasLimitForSend({
-  selectedAddress,
-  value,
-  gasPrice,
-  sendToken,
-  to,
-  data,
-  isNonStandardEthChain,
-  chainId,
-  gasLimit,
-  ...options
-}) {
-  let isSimpleSendOnNonStandardNetwork = false;
+/**
+ * @typedef {Object} Account
+ * @property {string} address - The hex address of the account.
+ * @property {string} balance - Hex string representing the native asset
+ *  balance of the account the transaction will be sent from.
+ */
 
-  // blockGasLimit may be a falsy, but defined, value when we receive it from
-  // state, so we use logical or to fall back to MIN_GAS_LIMIT_HEX. Some
-  // network implementations check the gas parameter supplied to
-  // eth_estimateGas for validity. For this reason, we set token sends
-  // blockGasLimit default to a higher number. Note that the current gasLimit
-  // on a BLOCK is 15,000,000 and will be 30,000,000 on mainnet after London.
-  // Meanwhile, MIN_GAS_LIMIT_HEX is 0x5208.
-  let blockGasLimit = MIN_GAS_LIMIT_HEX;
-  if (options.blockGasLimit) {
-    blockGasLimit = options.blockGasLimit;
-  } else if (sendToken) {
-    blockGasLimit = GAS_LIMITS.BASE_TOKEN_ESTIMATE;
-  }
+/**
+ * @typedef {Object} Amount
+ * @property {string} [error] - Error to display for the amount field.
+ * @property {string} value - A hex string representing the amount of the
+ *  selected currency to send.
+ */
 
-  // The parameters below will be sent to our background process to estimate
-  // how much gas will be used for a transaction. That background process is
-  // located in tx-gas-utils.js in the transaction controller folder.
-  const paramsForGasEstimate = { from: selectedAddress, value, gasPrice };
+/**
+ * @typedef {Object} Asset
+ * @property {string} balance - A hex string representing the balance
+ *  that the user holds of the asset that they are attempting to send.
+ * @property {TokenDetails} [details] - An object that describes the
+ *  selected asset in the case that the user is sending a token or collectibe.
+ *  Will be null when asset.type is 'NATIVE'.
+ * @property {string} [error] - Error to display when there is an issue
+ *  with the asset.
+ * @property {AssetTypesString} type - The type of asset that the user
+ *  is attempting to send. Defaults to 'NATIVE' which represents the native
+ *  asset of the chain. Can also be 'TOKEN' or 'COLLECTIBLE'.
+ */
 
-  if (sendToken) {
-    if (!to) {
-      // if no to address is provided, we cannot generate the token transfer
-      // hexData. hexData in a transaction largely dictates how much gas will
-      // be consumed by a transaction. We must use our best guess, which is
-      // represented in the gas shared constants.
-      return GAS_LIMITS.BASE_TOKEN_ESTIMATE;
-    }
-    paramsForGasEstimate.value = '0x0';
+/**
+ * @typedef {Object} GasFees
+ * @property {string} [error] - error to display for gas fields.
+ * @property {string} gasLimit - maximum gas needed for tx.
+ * @property {string} gasPrice - price in wei to pay per gas.
+ * @property {string} gasTotal - maximum total price in wei to pay.
+ * @property {string} maxFeePerGas - Maximum price in wei to pay per gas.
+ * @property {string} maxPriorityFeePerGas - Maximum priority fee in wei to pay
+ *  per gas.
+ */
 
-    // We have to generate the erc20/erc721 contract call to transfer tokens in
-    // order to get a proper estimate for gasLimit.
-    paramsForGasEstimate.data = getAssetTransferData({
-      sendToken,
-      fromAddress: selectedAddress,
-      toAddress: to,
-      amount: value,
-    });
+/**
+ * An object that describes the intended recipient of a transaction.
+ *
+ * @typedef {Object} Recipient
+ * @property {string} address - The fully qualified address of the recipient.
+ *  This is set after the recipient.userInput is validated, the userInput field
+ *  is quickly updated to avoid delay between keystrokes and seeing the input
+ *  field updated. After a debounce the address typed is validated and then the
+ *  address field is updated. The address field is also set when the user
+ *  selects a contact or account from the list, or an ENS resolution when
+ *  typing ENS names.
+ * @property {string} [error] - Error to display on the address field.
+ * @property {string} nickname - The nickname that the user has added to their
+ *  address book for the recipient.address.
+ * @property {string} [warning] - Warning to display on the address field.
+ */
 
-    paramsForGasEstimate.to = sendToken.address;
-  } else {
-    if (!data) {
-      // eth.getCode will return the compiled smart contract code at the
-      // address. If this returns 0x, 0x0 or a nullish value then the address
-      // is an externally owned account (NOT a contract account). For these
-      // types of transactions the gasLimit will always be 21,000 or 0x5208
-      const { isContractAddress } = to
-        ? await readAddressAsContract(global.eth, to)
-        : {};
-      if (!isContractAddress && !isNonStandardEthChain) {
-        return GAS_LIMITS.SIMPLE;
-      } else if (!isContractAddress && isNonStandardEthChain) {
-        isSimpleSendOnNonStandardNetwork = true;
-      }
-    }
+/**
+ * @typedef {Object} DraftTransaction
+ * @property {Amount} amount - An object containing information about the
+ *  amount of currency to send.
+ * @property {Asset} asset - An object that describes the asset that the user
+ *  has selected to send.
+ * @property {Account} [fromAccount] - The send flow is usually only relative to
+ *  the currently selected account. When editing a transaction, however, the
+ *  account may differ. In that case, the details of that account will be
+ *  stored in this object within the draftTransaction.
+ * @property {GasFees} gas - Details about the current gas settings
+ * @property {Array<{event: string, timestamp: number}>} history - An array of
+ *  entries that describe the user's journey through the send flow. This is
+ *  sent to the controller for attaching to state logs for troubleshooting and
+ *  support.
+ * @property {string} [id] - If the transaction has already been added to the
+ *  TransactionController this field will be populated with its id from the
+ *  TransactionController state. This is required to be able to update the
+ *  transaction in the controller.
+ * @property {Recipient} recipient - An object that describes the intended
+ *  recipient of the transaction.
+ * @property {MapValuesToUnion<DraftTxStatus>} status - Describes the
+ *  validity of the draft transaction, which will be either 'VALID' or
+ *  'INVALID', depending on our ability to generate a valid txParams object for
+ *  submission.
+ * @property {string} transactionType - Determines type of transaction being
+ *  sent, defaulted to 0x0 (legacy).
+ * @property {string} [userInputHexData] - When a user has enabled custom hex
+ *  data field in advanced options, they can supply data to the field which is
+ *  stored under this key.
+ */
 
-    paramsForGasEstimate.data = data;
+/**
+ * @type {DraftTransaction}
+ */
+export const draftTransactionInitialState = {
+  amount: {
+    error: null,
+    value: '0x0',
+  },
+  asset: {
+    balance: '0x0',
+    details: null,
+    error: null,
+    type: ASSET_TYPES.NATIVE,
+  },
+  fromAccount: null,
+  gas: {
+    error: null,
+    gasLimit: '0x0',
+    gasPrice: '0x0',
+    gasTotal: '0x0',
+    maxFeePerGas: '0x0',
+    maxPriorityFeePerGas: '0x0',
+  },
+  history: [],
+  id: null,
+  recipient: {
+    address: '',
+    error: null,
+    nickname: '',
+    warning: null,
+    recipientWarningAcknowledged: false,
+  },
+  status: SEND_STATUSES.VALID,
+  transactionType: TRANSACTION_ENVELOPE_TYPES.LEGACY,
+  userInputHexData: null,
+};
 
-    if (to) {
-      paramsForGasEstimate.to = to;
-    }
+/**
+ * Describes the state tree of the send slice
+ *
+ * @typedef {Object} SendState
+ * @property {MapValuesToUnion<SendStateAmountModes>} amountMode - Describe
+ *  whether the user has manually input an amount or if they have selected max
+ *  to send the maximum amount of the selected currency.
+ * @property {string} currentTransactionUUID - The UUID of the transaction
+ *  currently being modified by the send flow. This UUID is generated upon
+ *  initialization of the send flow, any previous UUIDs are discarded at
+ *  clean up AND during initialization. When a transaction is edited a new UUID
+ *  is generated for it and the state of that transaction is copied into a new
+ *  entry in the draftTransactions object.
+ * @property {Object.<string, DraftTransaction>} draftTransactions - An object keyed
+ *  by UUID with draftTransactions as the values.
+ * @property {boolean} eip1559support - tracks whether the current network
+ *  supports EIP 1559 transactions.
+ * @property {boolean} gasEstimateIsLoading - Indicates whether the gas
+ *  estimate is loading.
+ * @property {string} [gasEstimatePollToken] - String token identifying a
+ *  listener for polling on the gasFeeController
+ * @property {boolean} gasIsSetInModal - true if the user set custom gas in the
+ *  custom gas modal
+ * @property {string} gasLimitMinimum - minimum supported gasLimit.
+ * @property {string} gasPriceEstimate - Expected price in wei necessary to
+ *  pay per gas used for a transaction to be included in a reasonable timeframe.
+ *  Comes from the GasFeeController.
+ * @property {string} gasTotalForLayer1 -  Layer 1 gas fee total on multi-layer
+ *  fee networks
+ * @property {string} recipientInput - The user input of the recipient
+ *  which is updated quickly to avoid delays in the UI reflecting manual entry
+ *  of addresses.
+ * @property {MapValuesToUnion<SendStateRecipientModes>} recipientMode -
+ *  Describes which list of recipients the user is shown on the add recipient
+ *  screen. When this key is set to 'MY_ACCOUNTS' the user is shown the list of
+ *  accounts they own. When it is 'CONTACT_LIST' the user is shown the list of
+ *  contacts they have saved in MetaMask and any addresses they have recently
+ *  sent to.
+ * @property {Account} selectedAccount - The currently selected account in
+ *  MetaMask. Native balance and address will be pulled from this account if a
+ *  fromAccount is not specified in the draftTransaction object. During an edit
+ *  the fromAccount is specified.
+ * @property {MapValuesToUnion<SendStateStages>} stage - The stage of the
+ *  send flow that the user has progressed to. Defaults to 'INACTIVE' which
+ *  results in the send screen not being shown.
+ */
 
-    if (!value || value === '0') {
-      // TODO: Figure out what's going on here. According to eth_estimateGas
-      // docs this value can be zero, or undefined, yet we are setting it to a
-      // value here when the value is undefined or zero. For more context:
-      // https://github.com/MetaMask/metamask-extension/pull/6195
-      paramsForGasEstimate.value = '0xff';
-    }
-  }
+/**
+ * @type {SendState}
+ */
+export const initialState = {
+  amountMode: AMOUNT_MODES.INPUT,
+  currentTransactionUUID: null,
+  draftTransactions: {},
+  eip1559support: false,
+  gasEstimateIsLoading: true,
+  gasEstimatePollToken: null,
+  gasIsSetInModal: false,
+  gasPriceEstimate: '0x0',
+  gasLimitMinimum: GAS_LIMITS.SIMPLE,
+  gasTotalForLayer1: '0x0',
+  recipientMode: RECIPIENT_SEARCH_MODES.CONTACT_LIST,
+  recipientInput: '',
+  selectedAccount: {
+    address: null,
+    balance: '0x0',
+  },
+  stage: SEND_STAGES.INACTIVE,
+};
 
-  if (!isSimpleSendOnNonStandardNetwork) {
-    // If we do not yet have a gasLimit, we must call into our background
-    // process to get an estimate for gasLimit based on known parameters.
+/**
+ * TODO: We really need to start creating the metamask state type, and the
+ * entire state tree of redux. Would be *extremely* valuable in future
+ * typescript conversions. The metamask key is typed as an object on purpose
+ * here because I cannot go so far in this work as to type that entire object.
+ *
+ * @typedef {Object} MetaMaskState
+ * @property {SendState} send - The state of the send flow.
+ * @property {Object} metamask - The state of the metamask store.
+ */
 
-    paramsForGasEstimate.gas = addHexPrefix(
-      multiplyCurrencies(blockGasLimit, 0.95, {
-        multiplicandBase: 16,
-        multiplierBase: 10,
-        roundDown: '0',
-        toNumericBase: 'hex',
-      }),
-    );
-  }
-
-  // The buffer multipler reduces transaction failures by ensuring that the
-  // estimated gas is always sufficient. Without the multiplier, estimates
-  // for contract interactions can become inaccurate over time. This is because
-  // gas estimation is non-deterministic. The gas required for the exact same
-  // transaction call can change based on state of a contract or changes in the
-  // contracts environment (blockchain data or contracts it interacts with).
-  // Applying the 1.5 buffer has proven to be a useful guard against this non-
-  // deterministic behaviour.
-  //
-  // Gas estimation of simple sends should, however, be deterministic. As such
-  // no buffer is needed in those cases.
-  let bufferMultiplier = 1.5;
-  if (isSimpleSendOnNonStandardNetwork) {
-    bufferMultiplier = 1;
-  } else if (CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId]) {
-    bufferMultiplier = CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId];
-  }
-
-  try {
-    // call into the background process that will simulate transaction
-    // execution on the node and return an estimate of gasLimit
-    const estimatedGasLimit = await estimateGas(paramsForGasEstimate);
-    const estimateWithBuffer = addGasBuffer(
-      estimatedGasLimit,
-      blockGasLimit,
-      bufferMultiplier,
-    );
-    return addHexPrefix(estimateWithBuffer);
-  } catch (error) {
-    const simulationFailed =
-      error.message.includes('Transaction execution error.') ||
-      error.message.includes(
-        'gas required exceeds allowance or always failing transaction',
-      ) ||
-      (CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId] &&
-        error.message.includes('gas required exceeds allowance'));
-    if (simulationFailed) {
-      const estimateWithBuffer = addGasBuffer(
-        paramsForGasEstimate?.gas ?? gasLimit,
-        blockGasLimit,
-        bufferMultiplier,
-      );
-      return addHexPrefix(estimateWithBuffer);
-    }
-    throw error;
-  }
-}
-
-export async function getERC20Balance(token, accountAddress) {
-  const contract = global.eth.contract(abi).at(token.address);
-  const usersToken = (await contract.balanceOf(accountAddress)) ?? null;
-  if (!usersToken) {
-    return '0x0';
-  }
-  const amount = calcTokenAmount(
-    usersToken.balance.toString(),
-    token.decimals,
-  ).toString(16);
-  return addHexPrefix(amount);
-}
+const name = 'send';
 
 // After modification of specific fields in specific circumstances we must
 // recompute the gasLimit estimate to be as accurate as possible. the cases
@@ -463,25 +495,27 @@ export const computeEstimatedGasLimit = createAsyncThunk(
   async (_, thunkApi) => {
     const state = thunkApi.getState();
     const { send, metamask } = state;
+    const draftTransaction =
+      send.draftTransactions[send.currentTransactionUUID];
     const unapprovedTxs = getUnapprovedTxs(state);
     const isMultiLayerFeeNetwork = getIsMultiLayerFeeNetwork(state);
-    const transaction = unapprovedTxs[send.id];
+    const transaction = unapprovedTxs[draftTransaction.id];
     const isNonStandardEthChain = getIsNonStandardEthChain(state);
     const chainId = getCurrentChainId(state);
 
-    let layer1GasTotal;
+    let gasTotalForLayer1;
     if (isMultiLayerFeeNetwork) {
-      layer1GasTotal = await fetchEstimatedL1Fee(global.eth, {
+      gasTotalForLayer1 = await fetchEstimatedL1Fee(global.eth, {
         txParams: {
-          gasPrice: send.gas.gasPrice,
-          gas: send.gas.gasLimit,
-          to: send.recipient.address?.toLowerCase(),
+          gasPrice: draftTransaction.gas.gasPrice,
+          gas: draftTransaction.gas.gasLimit,
+          to: draftTransaction.recipient.address?.toLowerCase(),
           value:
-            send.amount.mode === 'MAX'
-              ? send.account.balance
+            send.amountMode === AMOUNT_MODES.MAX
+              ? send.selectedAccount.balance
               : send.amount.value,
-          from: send.account.address,
-          data: send.userInputHexData,
+          from: send.selectedAccount.address,
+          data: draftTransaction.userInputHexData,
           type: '0x0',
         },
       });
@@ -493,21 +527,21 @@ export const computeEstimatedGasLimit = createAsyncThunk(
       !transaction.userEditedGasLimit
     ) {
       const gasLimit = await estimateGasLimitForSend({
-        gasPrice: send.gas.gasPrice,
+        gasPrice: draftTransaction.gas.gasPrice,
         blockGasLimit: metamask.currentBlockGasLimit,
         selectedAddress: metamask.selectedAddress,
-        sendToken: send.asset.details,
-        to: send.recipient.address?.toLowerCase(),
-        value: send.amount.value,
-        data: send.userInputHexData,
+        sendToken: draftTransaction.asset.details,
+        to: draftTransaction.recipient.address?.toLowerCase(),
+        value: draftTransaction.amount.value,
+        data: draftTransaction.userInputHexData,
         isNonStandardEthChain,
         chainId,
-        gasLimit: send.gas.gasLimit,
+        gasLimit: draftTransaction.gas.gasLimit,
       });
       await thunkApi.dispatch(setCustomGasLimit(gasLimit));
       return {
         gasLimit,
-        layer1GasTotal,
+        gasTotalForLayer1,
       };
     }
     return null;
@@ -515,28 +549,18 @@ export const computeEstimatedGasLimit = createAsyncThunk(
 );
 
 /**
- * This method is used to keep the original logic from the gas.duck.js file
- * after receiving a gasPrice from eth_gasPrice. First, the returned gasPrice
- * was converted to GWEI, then it was converted to a Number, then in the send
- * duck (here) we would use getGasPriceInHexWei to get back to hexWei. Now that
- * we receive a GWEI estimate from the controller, we still need to do this
- * weird conversion to get the proper rounding.
- *
- * @param {string} gasPriceEstimate
- * @returns {string}
+ * @typedef {Object} Asset
+ * @property {AssetTypesString} type - The type of asset that the user
+ *  is attempting to send. Defaults to 'NATIVE' which represents the native
+ *  asset of the chain. Can also be 'TOKEN' or 'COLLECTIBLE'.
+ * @property {string} balance - A hex string representing the balance
+ *  that the user holds of the asset that they are attempting to send.
+ * @property {TokenDetails} [details] - An object that describes the
+ *  selected asset in the case that the user is sending a token or collectibe.
+ *  Will be null when asset.type is 'NATIVE'.
+ * @property {string} [error] - Error to display when there is an issue
+ *  with the asset.
  */
-function getRoundedGasPrice(gasPriceEstimate) {
-  const gasPriceInDecGwei = conversionUtil(gasPriceEstimate, {
-    numberOfDecimals: 9,
-    toDenomination: GWEI,
-    fromNumericBase: 'dec',
-    toNumericBase: 'dec',
-    fromCurrency: ETH,
-    fromDenomination: GWEI,
-  });
-  const gasPriceAsNumber = Number(gasPriceInDecGwei);
-  return getGasPriceInHexWei(gasPriceAsNumber);
-}
 
 /**
  * Responsible for initializing required state for the send slice.
@@ -550,34 +574,43 @@ function getRoundedGasPrice(gasPriceEstimate) {
  */
 export const initializeSendState = createAsyncThunk(
   'send/initializeSendState',
-  async (_, thunkApi) => {
+  async ({ chainHasChanged = false } = {}, thunkApi) => {
+    /**
+     * @typedef {Object} ReduxState
+     * @property {Object} metamask - Half baked type for the MetaMask object
+     * @property {SendState} send - the send state
+     */
+
+    /**
+     * @type {ReduxState}
+     */
     const state = thunkApi.getState();
     const isNonStandardEthChain = getIsNonStandardEthChain(state);
     const chainId = getCurrentChainId(state);
     const eip1559support = checkNetworkAndAccountSupports1559(state);
-    const {
-      send: { asset, stage, recipient, amount, userInputHexData },
-      metamask,
-    } = state;
+    const account = getSelectedAccount(state);
+    const { send: sendState, metamask } = state;
+    const draftTransaction =
+      sendState.draftTransactions[sendState.currentTransactionUUID];
 
-    // First determine the correct from address. For new sends this is always
-    // the currently selected account and switching accounts switches the from
-    // address. If editing an existing transaction (by clicking 'edit' on the
-    // send page), the fromAddress is always the address from the txParams.
-    const fromAddress =
-      stage === SEND_STAGES.EDIT
-        ? state.send.account.address
-        : metamask.selectedAddress;
-    // We need the account's balance which is calculated from cachedBalances in
-    // the getMetaMaskAccounts selector. getTargetAccount consumes this
-    // selector and returns the account at the specified address.
-    const account = getTargetAccount(state, fromAddress);
+    // If the draft transaction is not present, then this action has been
+    // dispatched out of sync with the intended flow. This is not always a bug.
+    // For instance, in the actions.js file we dispatch this action anytime the
+    // chain changes.
+    if (!draftTransaction) {
+      thunkApi.rejectWithValue(
+        'draftTransaction not found, possibly not on send flow',
+      );
+    }
 
     // Default gasPrice to 1 gwei if all estimation fails, this is only used
     // for gasLimit estimation and won't be set directly in state. Instead, we
     // will return the gasFeeEstimates and gasEstimateType so that the reducer
     // can set the appropriate gas fees in state.
-    let gasPrice = '0x1';
+    let gasPrice =
+      sendState.stage === SEND_STAGES.EDIT
+        ? draftTransaction.gas.gasPrice
+        : '0x1';
     let gasEstimatePollToken = null;
 
     // Instruct the background process that polling for gas prices should begin
@@ -589,43 +622,49 @@ export const initializeSendState = createAsyncThunk(
       metamask: { gasFeeEstimates, gasEstimateType },
     } = thunkApi.getState();
 
-    // Because we are only interested in getting a gasLimit estimation we only
-    // need to worry about gasPrice. So we use maxFeePerGas as gasPrice if we
-    // have a fee market estimation.
-    if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) {
-      gasPrice = getGasPriceInHexWei(gasFeeEstimates.medium);
-    } else if (gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE) {
-      gasPrice = getRoundedGasPrice(gasFeeEstimates.gasPrice);
-    } else if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {
-      gasPrice = getGasPriceInHexWei(
-        gasFeeEstimates.medium.suggestedMaxFeePerGas,
-      );
-    } else {
-      gasPrice = gasFeeEstimates.gasPrice
-        ? getRoundedGasPrice(gasFeeEstimates.gasPrice)
-        : '0x0';
+    if (sendState.stage !== SEND_STAGES.EDIT) {
+      // Because we are only interested in getting a gasLimit estimation we only
+      // need to worry about gasPrice. So we use maxFeePerGas as gasPrice if we
+      // have a fee market estimation.
+      if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) {
+        gasPrice = getGasPriceInHexWei(gasFeeEstimates.medium);
+      } else if (gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE) {
+        gasPrice = getRoundedGasPrice(gasFeeEstimates.gasPrice);
+      } else if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) {
+        gasPrice = getGasPriceInHexWei(
+          gasFeeEstimates.medium.suggestedMaxFeePerGas,
+        );
+      } else {
+        gasPrice = gasFeeEstimates.gasPrice
+          ? getRoundedGasPrice(gasFeeEstimates.gasPrice)
+          : '0x0';
+      }
     }
 
     // Set a basic gasLimit in the event that other estimation fails
-    let gasLimit =
-      asset.type === ASSET_TYPES.TOKEN || asset.type === ASSET_TYPES.COLLECTIBLE
-        ? GAS_LIMITS.BASE_TOKEN_ESTIMATE
-        : GAS_LIMITS.SIMPLE;
+    let { gasLimit } = draftTransaction.gas;
     if (
       gasEstimateType !== GAS_ESTIMATE_TYPES.NONE &&
-      stage !== SEND_STAGES.EDIT &&
-      recipient.address
+      sendState.stage !== SEND_STAGES.EDIT &&
+      draftTransaction.recipient.address
     ) {
+      gasLimit =
+        draftTransaction.asset.type === ASSET_TYPES.TOKEN ||
+        draftTransaction.asset.type === ASSET_TYPES.COLLECTIBLE
+          ? GAS_LIMITS.BASE_TOKEN_ESTIMATE
+          : GAS_LIMITS.SIMPLE;
       // Run our estimateGasLimit logic to get a more accurate estimation of
       // required gas. If this value isn't nullish, set it as the new gasLimit
       const estimatedGasLimit = await estimateGasLimitForSend({
         gasPrice,
         blockGasLimit: metamask.currentBlockGasLimit,
-        selectedAddress: fromAddress,
-        sendToken: asset.details,
-        to: recipient.address.toLowerCase(),
-        value: amount.value,
-        data: userInputHexData,
+        selectedAddress:
+          draftTransaction.fromAccount?.address ??
+          sendState.selectedAccount.address,
+        sendToken: draftTransaction.asset.details,
+        to: draftTransaction.recipient.address.toLowerCase(),
+        value: draftTransaction.amount.value,
+        data: draftTransaction.userInputHexData,
         isNonStandardEthChain,
         chainId,
       });
@@ -634,38 +673,11 @@ export const initializeSendState = createAsyncThunk(
     // We have to keep the gas slice in sync with the send slice state
     // so that it'll be initialized correctly if the gas modal is opened.
     await thunkApi.dispatch(setCustomGasLimit(gasLimit));
-    // We must determine the balance of the asset that the transaction will be
-    // sending. This is done by referencing the native balance on the account
-    // for native assets, and calling the balanceOf method on the ERC20
-    // contract for token sends.
-    let { balance } = account;
-    if (asset.type === ASSET_TYPES.TOKEN) {
-      if (asset.details === null) {
-        // If we're sending a token but details have not been provided we must
-        // abort and set the send slice into invalid status.
-        throw new Error(
-          'Send slice initialized as token send without token details',
-        );
-      }
-      balance = await getERC20Balance(asset.details, fromAddress);
-    }
-
-    if (asset.type === ASSET_TYPES.COLLECTIBLE) {
-      if (asset.details === null) {
-        // If we're sending a collectible but details have not been provided we must
-        // abort and set the send slice into invalid status.
-        throw new Error(
-          'Send slice initialized as collectibles send without token details',
-        );
-      }
-      balance = '0x1';
-    }
     return {
-      address: fromAddress,
-      nativeBalance: account.balance,
-      assetBalance: balance,
+      account,
       chainId: getCurrentChainId(state),
       tokens: getTokens(state),
+      chainHasChanged,
       gasFeeEstimates,
       gasEstimateType,
       gasLimit,
@@ -678,275 +690,187 @@ export const initializeSendState = createAsyncThunk(
   },
 );
 
+// Action Payload Typedefs
 /**
- * @typedef {Object} SendState
- * @property {string} [id] - The id of a transaction that is being edited
- * @property {SendStateStagesStrings} stage - The stage of the send flow that
- *  the user has progressed to. Defaults to 'INACTIVE' which results in the
- *  send screen not being shown.
- * @property {SendStateStatusStrings} status - The status of the send slice
- *  which will be either 'VALID' or 'INVALID'
- * @property {string} transactionType - Determines type of transaction being
- *  sent, defaulted to 0x0 (legacy).
- * @property {boolean} eip1559support - tracks whether the current network
- *  supports EIP 1559 transactions.
- * @property {Object} account - Details about the user's account.
- * @property {string} [account.address] - from account address, defaults to
- *  selected account. will be the account the original transaction was sent
- *  from in the case of the EDIT stage.
- * @property {string} [account.balance] - Hex string representing the balance
- *  of the from account.
- * @property {string} [userInputHexData] - When a user has enabled custom hex
- *  data field in advanced options, they can supply data to the field which is
- *  stored under this key.
- * @property {Object} gas - Details about the current gas settings
- * @property {boolean} gas.isGasEstimateLoading - Indicates whether the gas
- *  estimate is loading.
- * @property {string} [gas.gasEstimatePollToken] - String token identifying a
- *  listener for polling on the gasFeeController
- * @property {boolean} gas.isCustomGasSet - true if the user set custom gas in
- *  the custom gas modal
- * @property {string} gas.gasLimit - maximum gas needed for tx.
- * @property {string} gas.gasPrice - price in wei to pay per gas.
- * @property {string} gas.maxFeePerGas - Maximum price in wei to pay per gas.
- * @property {string} gas.maxPriorityFeePerGas - Maximum priority fee in wei to
- *  pay per gas.
- * @property {string} gas.gasPriceEstimate - Expected price in wei necessary to
- *  pay per gas used for a transaction to be included in a reasonable timeframe.
- *  Comes from the GasFeeController.
- * @property {string} gas.gasTotal - maximum total price in wei to pay.
- * @property {string} gas.minimumGasLimit - minimum supported gasLimit.
- * @property {string} [gas.error] - error to display for gas fields.
- * @property {Object} amount - An object containing information about the
- *  amount of currency to send.
- * @property {SendStateAmountModeStrings} amount.mode - Describe whether the
- *  user has manually input an amount or if they have selected max to send the
- *  maximum amount of the selected currency.
- * @property {string} amount.value - A hex string representing the amount of
- *  the selected currency to send.
- * @property {string} [amount.error] - Error to display for the amount field.
- * @property {Object} asset - An object that describes the asset that the user
- *  has selected to send.
- * @property {AssetTypesString} asset.type - The type of asset that the user
- *  is attempting to send. Defaults to 'NATIVE' which represents the native
- *  asset of the chain. Can also be 'TOKEN' or 'COLLECTIBLE'.
- * @property {string} asset.balance - A hex string representing the balance
- *  that the user holds of the asset that they are attempting to send.
- * @property {Object} [asset.details] - An object that describes the selected
- *  asset in the case that the user is sending a token or collectibe. Will be
- *  null when asset.type is 'NATIVE'.
- * @property {string} [asset.details.address] - The address of the selected
- *  'TOKEN' or 'COLLECTIBLE' contract.
- * @property {string} [asset.details.symbol] - The symbol of the selected
- *  asset.
- * @property {number} [asset.details.decimals] - The number of decimals of the
- *  selected 'TOKEN' asset.
- * @property {number} [asset.details.tokenId] - The id of the selected
- *  'COLLECTIBLE' asset.
- * @property {TokenStandardStrings} [asset.details.standard] - The standard
- *  of the selected 'TOKEN' or 'COLLECTIBLE' asset.
- * @property {boolean} [asset.details.isERC721] - True when the asset is a
- *  ERC721 token.
- * @property {string} [asset.error] - Error to display when there is an issue
- *  with the asset.
- * @property {Object} recipient - An object that describes the intended
- *  recipient of the transaction.
- * @property {SendStateRecipientModeStrings} recipient.mode - Describes which
- *  list of recipients the user is shown on the add recipient screen. When this
- *  key is set to 'MY_ACCOUNTS' the user is shown the list of accounts they
- *  own. When it is 'CONTACT_LIST' the user is shown the list of contacts they
- *  have saved in MetaMask and any addresses they have recently sent to.
- * @property {string} recipient.address - The fully qualified address of the
- *  recipient. This is set after the recipient.userInput is validated, the
- *  userInput field is quickly updated to avoid delay between keystrokes and
- *  seeing the input field updated. After a debounc the address typed is
- *  validated and then the address field is updated. The address field is also
- *  set when the user selects a contact or account from the list, or an ENS
- *  resolution when typing ENS names.
- * @property {string} recipient.userInput - The user input of the recipient
- *  which is updated quickly to avoid delays in the UI reflecting manual entry
- *  of addresses.
- * @property {string} recipient.nickname - The nickname that the user has added
- *  to their address book for the recipient.address.
- * @property {string} [recipient.error] - Error to display on the address field.
- * @property {string} [recipient.warning] - Warning to display on the address
- *  field.
- * @property {Object} multiLayerFees - An object containing attributes for use
- *  on chains that have layer 1 and layer 2 fees to consider for gas
- *  calculations.
- * @property {string} multiLayerFees.layer1GasTotal -  Layer 1 gas fee total on
- *  multi-layer fee networks
- * @property {Array<{event: string, timestamp: number}>} history - An array of
- *  entries that describe the user's journey through the send flow. This is
- *  sent to the controller for attaching to state logs for troubleshooting and
- *  support.
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<string>
+ * )} SimpleStringPayload
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<MapValuesToUnion<SendStateAmountModes>>
+ * )} SendStateAmountModePayload
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<DraftTransaction['asset']>
+ * )} UpdateAssetPayload
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<Partial<
+ *   Pick<DraftTransaction['recipient'], 'address' | 'nickname'>>
+ *  >
+ * )} updateRecipientPayload
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<SendState['recipientMode']>
+ * )} UpdateRecipientModePayload
  */
 
 /**
- * @type {SendState}
+ * @typedef {Object} GasFeeUpdateParams
+ * @property {TransactionTypeString} transactionType - The transaction type
+ * @property {string} [maxFeePerGas] - The maximum amount in hex wei to pay
+ *  per gas on a FEE_MARKET transaction.
+ * @property {string} [maxPriorityFeePerGas] - The maximum amount in hex
+ *  wei to pay per gas as an incentive to miners on a FEE_MARKET
+ *  transaction.
+ * @property {string} [gasPrice] - The amount in hex wei to pay per gas on
+ *  a LEGACY transaction.
+ * @property {boolean} [isAutomaticUpdate] - true if the update is the
+ *  result of a gas estimate update from the controller.
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<GasFeeUpdateParams>
+ * )} GasFeeUpdatePayload
  */
-export const initialState = {
-  id: null,
-  stage: SEND_STAGES.INACTIVE,
-  status: SEND_STATUSES.VALID,
-  transactionType: TRANSACTION_ENVELOPE_TYPES.LEGACY,
-  eip1559support: false,
-  account: {
-    address: null,
-    balance: '0x0',
-  },
-  userInputHexData: null,
-  gas: {
-    isGasEstimateLoading: true,
-    gasEstimatePollToken: null,
-    isCustomGasSet: false,
-    gasLimit: '0x0',
-    gasPrice: '0x0',
-    maxFeePerGas: '0x0',
-    maxPriorityFeePerGas: '0x0',
-    gasPriceEstimate: '0x0',
-    gasTotal: '0x0',
-    minimumGasLimit: GAS_LIMITS.SIMPLE,
-    error: null,
-  },
-  amount: {
-    mode: AMOUNT_MODES.INPUT,
-    value: '0x0',
-    error: null,
-  },
-  asset: {
-    type: ASSET_TYPES.NATIVE,
-    balance: '0x0',
-    details: null,
-    error: null,
-  },
-  recipient: {
-    mode: RECIPIENT_SEARCH_MODES.CONTACT_LIST,
-    userInput: '',
-    address: '',
-    nickname: '',
-    error: null,
-    warning: null,
-  },
-  multiLayerFees: {
-    layer1GasTotal: '0x0',
-  },
-  history: [],
-};
 
 /**
- * Generates a txParams from the send slice.
- *
- * @param {SendState} state - the Send slice state
- * @returns {import(
- *  '../../../shared/constants/transaction'
- * ).TxParams} A txParams object that can be used to create a transaction or
- *  update an existing transaction.
+ * @typedef {Object} GasEstimateUpdateParams
+ * @property {GasEstimateType} gasEstimateType - The type of gas estimation
+ *  provided by the controller.
+ * @property {(
+ *  EthGasPriceEstimate | LegacyGasPriceEstimate | GasFeeEstimates
+ * )} gasFeeEstimates - The gas fee estimates provided by the controller.
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<GasEstimateUpdateParams>
+ * )} GasEstimateUpdatePayload
  */
-function generateTransactionParams(state) {
-  const txParams = {
-    from: state.account.address,
-    // gasLimit always needs to be set regardless of the asset being sent
-    // or the type of transaction.
-    gas: state.gas.gasLimit,
-  };
-  switch (state.asset.type) {
-    case ASSET_TYPES.TOKEN:
-      // When sending a token the to address is the contract address of
-      // the token being sent. The value is set to '0x0' and the data
-      // is generated from the recipient address, token being sent and
-      // amount.
-      txParams.to = state.asset.details.address;
-      txParams.value = '0x0';
-      txParams.data = generateERC20TransferData({
-        toAddress: state.recipient.address,
-        amount: state.amount.value,
-        sendToken: state.asset.details,
-      });
-      break;
-    case ASSET_TYPES.COLLECTIBLE:
-      // When sending a token the to address is the contract address of
-      // the token being sent. The value is set to '0x0' and the data
-      // is generated from the recipient address, token being sent and
-      // amount.
-      txParams.to = state.asset.details.address;
-      txParams.value = '0x0';
-      txParams.data = generateERC721TransferData({
-        toAddress: state.recipient.address,
-        fromAddress: state.account.address,
-        tokenId: state.asset.details.tokenId,
-      });
-      break;
-    case ASSET_TYPES.NATIVE:
-    default:
-      // When sending native currency the to and value fields use the
-      // recipient and amount values and the data key is either null or
-      // populated with the user input provided in hex field.
-      txParams.to = state.recipient.address;
-      txParams.value = state.amount.value;
-      txParams.data = state.userInputHexData ?? undefined;
-  }
 
-  // We need to make sure that we only include the right gas fee fields
-  // based on the type of transaction the network supports. We will also set
-  // the type param here.
-  if (state.eip1559support) {
-    txParams.type = TRANSACTION_ENVELOPE_TYPES.FEE_MARKET;
-
-    txParams.maxFeePerGas = state.gas.maxFeePerGas;
-    txParams.maxPriorityFeePerGas = state.gas.maxPriorityFeePerGas;
-
-    if (!txParams.maxFeePerGas || txParams.maxFeePerGas === '0x0') {
-      txParams.maxFeePerGas = state.gas.gasPrice;
-    }
-
-    if (
-      !txParams.maxPriorityFeePerGas ||
-      txParams.maxPriorityFeePerGas === '0x0'
-    ) {
-      txParams.maxPriorityFeePerGas = txParams.maxFeePerGas;
-    }
-  } else {
-    txParams.gasPrice = state.gas.gasPrice;
-    txParams.type = TRANSACTION_ENVELOPE_TYPES.LEGACY;
-  }
-
-  return txParams;
-}
+/**
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<DraftTransaction['asset']>
+ * )} UpdateAssetPayload
+ * @typedef {(
+ *  import('@reduxjs/toolkit').PayloadAction<DraftTransaction>
+ * )} DraftTransactionPayload
+ */
 
 const slice = createSlice({
   name,
   initialState,
   reducers: {
-    addHistoryEntry: (state, action) => {
-      state.history.push({
-        entry: action.payload,
-        timestamp: Date.now(),
-      });
-    },
     /**
-     * update current amount.value in state and run post update validation of
-     * the amount field and the send state.
+     * Adds a new draft transaction to state, first generating a new UUID for
+     * the transaction and setting that as the currentTransactionUUID. If the
+     * draft has an id property set, the stage is set to EDIT.
      *
      * @param {SendStateDraft} state - A writable draft of the send state to be
      *  updated.
-     * @param {import('@reduxjs/toolkit').PayloadAction<string>} action - The
-     *  hex string to be set as the amount value.
+     * @param {DraftTransactionPayload} action - An action with payload that is
+     *  a new draft transaction that will be added to state.
+     * @returns {void}
      */
-    updateSendAmount: (state, action) => {
-      state.amount.value = addHexPrefix(action.payload);
-      // Once amount has changed, validate the field
-      slice.caseReducers.validateAmountField(state);
-      if (state.asset.type === ASSET_TYPES.NATIVE) {
-        // if sending the native asset the amount being sent will impact the
-        // gas field as well because the gas validation takes into
-        // consideration the available balance minus amount sent before
-        // checking if there is enough left to cover the gas fee.
-        slice.caseReducers.validateGasField(state);
+    addNewDraft: (state, action) => {
+      state.currentTransactionUUID = uuidv4();
+      state.draftTransactions[state.currentTransactionUUID] = action.payload;
+      if (action.payload.id) {
+        state.stage = SEND_STAGES.EDIT;
+      } else {
+        state.stage = SEND_STAGES.ADD_RECIPIENT;
       }
+    },
+    /**
+     * Adds an entry, with timestamp, to the draftTransaction history.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {SimpleStringPayload} action - An action with payload that is
+     *  a string to be added to the history of the draftTransaction
+     * @returns {void}
+     */
+    addHistoryEntry: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      if (draftTransaction) {
+        draftTransaction.history.push({
+          entry: action.payload,
+          timestamp: Date.now(),
+        });
+      }
+    },
+    /**
+     * gasTotal is computed based on gasPrice and gasLimit and set in state
+     * recomputes the maximum amount if the current amount mode is 'MAX' and
+     * sending the native token. ERC20 assets max amount is unaffected by
+     * gasTotal so does not need to be recomputed. Finally, validates the gas
+     * field and send state.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @returns {void}
+     */
+    calculateGasTotal: (state) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      // use maxFeePerGas as the multiplier if working with a FEE_MARKET transaction
+      // otherwise use gasPrice
+      if (
+        draftTransaction.transactionType ===
+        TRANSACTION_ENVELOPE_TYPES.FEE_MARKET
+      ) {
+        draftTransaction.gas.gasTotal = addHexPrefix(
+          calcGasTotal(
+            draftTransaction.gas.gasLimit,
+            draftTransaction.gas.maxFeePerGas,
+          ),
+        );
+      } else {
+        draftTransaction.gas.gasTotal = addHexPrefix(
+          calcGasTotal(
+            draftTransaction.gas.gasLimit,
+            draftTransaction.gas.gasPrice,
+          ),
+        );
+      }
+      if (
+        state.amountMode === AMOUNT_MODES.MAX &&
+        draftTransaction.asset.type === ASSET_TYPES.NATIVE
+      ) {
+        slice.caseReducers.updateAmountToMax(state);
+      }
+      slice.caseReducers.validateAmountField(state);
+      slice.caseReducers.validateGasField(state);
       // validate send state
       slice.caseReducers.validateSendState(state);
     },
+    /**
+     * Clears all drafts from send state and drops the currentTransactionUUID.
+     * This is an important first step before adding a new draft transaction to
+     * avoid possible collision.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @returns {void}
+     */
+    clearPreviousDrafts: (state) => {
+      state.currentTransactionUUID = null;
+      state.draftTransactions = {};
+    },
+    /**
+     * Clears the send state by setting it to the initial value
+     *
+     * @returns {SendState}
+     */
+    resetSendState: () => initialState,
+    /**
+     * sets the amount mode to the provided value as long as it is one of the
+     * supported modes (MAX|INPUT)
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {SendStateAmountModePayload} action - The amount mode
+     *  to set the state to.
+     * @returns {void}
+     */
+    updateAmountMode: (state, action) => {
+      if (Object.values(AMOUNT_MODES).includes(action.payload)) {
+        state.amountMode = action.payload;
+      }
+    },
     /**
      * computes the maximum amount of asset that can be sent and then calls
      * the updateSendAmount action above with the computed value, which will
@@ -954,25 +878,32 @@ const slice = createSlice({
      *
      * @param {SendStateDraft} state - A writable draft of the send state to be
      *  updated.
+     * @returns {void}
      */
     updateAmountToMax: (state) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
       let amount = '0x0';
-      if (state.asset.type === ASSET_TYPES.TOKEN) {
-        const decimals = state.asset.details?.decimals ?? 0;
+      if (draftTransaction.asset.type === ASSET_TYPES.TOKEN) {
+        const decimals = draftTransaction.asset.details?.decimals ?? 0;
         const multiplier = Math.pow(10, Number(decimals));
 
-        amount = multiplyCurrencies(state.asset.balance, multiplier, {
-          toNumericBase: 'hex',
-          multiplicandBase: 16,
-          multiplierBase: 10,
-        });
+        amount = multiplyCurrencies(
+          draftTransaction.asset.balance,
+          multiplier,
+          {
+            toNumericBase: 'hex',
+            multiplicandBase: 16,
+            multiplierBase: 10,
+          },
+        );
       } else {
         const _gasTotal = sumHexes(
-          state.gas.gasTotal || '0x0',
-          state.multiLayerFees?.layer1GasTotal || '0x0',
+          draftTransaction.gas.gasTotal || '0x0',
+          state.gasTotalForLayer1 || '0x0',
         );
         amount = subtractCurrencies(
-          addHexPrefix(state.asset.balance),
+          addHexPrefix(draftTransaction.asset.balance),
           addHexPrefix(_gasTotal),
           {
             toNumericBase: 'hex',
@@ -986,176 +917,62 @@ const slice = createSlice({
       });
     },
     /**
-     * updates the userInputHexData state key
+     * Updates the currently selected asset
      *
      * @param {SendStateDraft} state - A writable draft of the send state to be
      *  updated.
-     * @param {import('@reduxjs/toolkit').PayloadAction<string>} action - The
-     *  hex string to be set as the userInputHexData value.
+     * @param {UpdateAssetPayload} action - The asest to set in the
+     *  draftTransaction.
+     * @returns {void}
      */
-    updateUserInputHexData: (state, action) => {
-      state.userInputHexData = action.payload;
-    },
-    /**
-     * Transaction details of a previously created transaction that the user
-     * has selected to edit.
-     *
-     * @typedef {Object} EditTransactionPayload
-     * @property {string} gasLimit - The hex string maximum gas to use.
-     * @property {string} gasPrice - The amount in wei to pay for gas, in hex
-     *  format.
-     * @property {string} amount - The amount of the currency to send, in hex
-     *  format.
-     * @property {string} address - The address to send the transaction to.
-     * @property {string} [nickname] - The nickname the user has associated
-     *  with the address in their contact book.
-     * @property {string} id - The id of the transaction in the
-     *  TransactionController state[
-     * @property {string} from - the address that the user is sending from
-     * @property {string} [data] - The hex data that describes the transaction.
-     *  Used primarily for contract interactions, like token sends, but can
-     *  also be provided by the user.
-     */
-    /**
-     * Initiates the edit transaction flow by setting the stage to 'EDIT' and
-     * then pulling the details of the previously submitted transaction from
-     * the action payload.
-     *
-     * @param {SendStateDraft} state - A writable draft of the send state to be
-     *  updated.
-     * @param {import(
-     *  '@reduxjs/toolkit'
-     * ).PayloadAction<EditTransactionPayload>} action - The details of the
-     *  transaction to be edited.
-     */
-    editTransaction: (state, action) => {
-      state.stage = SEND_STAGES.EDIT;
-      state.gas.gasLimit = action.payload.gasLimit;
-      state.gas.gasPrice = action.payload.gasPrice;
-      state.amount.value = action.payload.amount;
-      state.gas.error = null;
-      state.amount.error = null;
-      state.asset.error = null;
-      state.recipient.address = action.payload.address;
-      state.recipient.nickname = action.payload.nickname;
-      state.id = action.payload.id;
-      state.account.address = action.payload.from;
-      state.userInputHexData = action.payload.data;
-    },
-    /**
-     * gasTotal is computed based on gasPrice and gasLimit and set in state
-     * recomputes the maximum amount if the current amount mode is 'MAX' and
-     * sending the native token. ERC20 assets max amount is unaffected by
-     * gasTotal so does not need to be recomputed. Finally, validates the gas
-     * field and send state.
-     *
-     * @param {SendStateDraft} state - A writable draft of the send state to be
-     *  updated.
-     */
-    calculateGasTotal: (state) => {
-      // use maxFeePerGas as the multiplier if working with a FEE_MARKET transaction
-      // otherwise use gasPrice
-      if (state.transactionType === TRANSACTION_ENVELOPE_TYPES.FEE_MARKET) {
-        state.gas.gasTotal = addHexPrefix(
-          calcGasTotal(state.gas.gasLimit, state.gas.maxFeePerGas),
-        );
-      } else {
-        state.gas.gasTotal = addHexPrefix(
-          calcGasTotal(state.gas.gasLimit, state.gas.gasPrice),
-        );
-      }
+    updateAsset: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+
+      // If an asset update occurs that changes the type from 'NATIVE' to
+      // 'NATIVE' then this is likely the initial asset set of an edit
+      // transaction. We don't need to set the amount to zero in this case.
+      // The only times where an update would occur of this nature that we
+      // would want to set the amount to zero is on a network or account change
+      // but that update is handled elsewhere.
+      const skipAmountUpdate =
+        action.payload.type === ASSET_TYPES.NATIVE &&
+        draftTransaction.asset.type === ASSET_TYPES.NATIVE;
+      draftTransaction.asset.type = action.payload.type;
+      draftTransaction.asset.balance = action.payload.balance;
+      draftTransaction.asset.error = action.payload.error;
+
       if (
-        state.amount.mode === AMOUNT_MODES.MAX &&
-        state.asset.type === ASSET_TYPES.NATIVE
+        draftTransaction.asset.type === ASSET_TYPES.TOKEN ||
+        draftTransaction.asset.type === ASSET_TYPES.COLLECTIBLE
       ) {
-        slice.caseReducers.updateAmountToMax(state);
+        draftTransaction.asset.details = action.payload.details;
+      } else {
+        // clear the details object when sending native currency
+        draftTransaction.asset.details = null;
+        if (draftTransaction.recipient.error === CONTRACT_ADDRESS_ERROR) {
+          // Errors related to sending tokens to their own contract address
+          // are no longer valid when sending native currency.
+          draftTransaction.recipient.error = null;
+        }
+      }
+      // if amount mode is MAX update amount to max of new asset, otherwise set
+      // to zero. This will revalidate the send amount field.
+      if (state.amountMode === AMOUNT_MODES.MAX) {
+        slice.caseReducers.updateAmountToMax(state);
+      } else if (skipAmountUpdate === false) {
+        slice.caseReducers.updateSendAmount(state, { payload: '0x0' });
       }
-      slice.caseReducers.validateAmountField(state);
-      slice.caseReducers.validateGasField(state);
       // validate send state
       slice.caseReducers.validateSendState(state);
     },
-    /**
-     * sets the provided gasLimit in state and then recomputes the gasTotal.
-     *
-     * @param {SendStateDraft} state - A writable draft of the send state to be
-     *  updated.
-     * @param {import('@reduxjs/toolkit').PayloadAction<string>} action - The
-     *  gasLimit in hex to set in state.
-     */
-    updateGasLimit: (state, action) => {
-      state.gas.gasLimit = addHexPrefix(action.payload);
-      slice.caseReducers.calculateGasTotal(state);
-    },
-    /**
-     * @typedef {Object} GasFeeUpdatePayload
-     * @property {TransactionTypeString} transactionType - The transaction type
-     * @property {string} [maxFeePerGas] - The maximum amount in hex wei to pay
-     *  per gas on a FEE_MARKET transaction.
-     * @property {string} [maxPriorityFeePerGas] - The maximum amount in hex
-     *  wei to pay per gas as an incentive to miners on a FEE_MARKET
-     *  transaction.
-     * @property {string} [gasPrice] - The amount in hex wei to pay per gas on
-     *  a LEGACY transaction.
-     * @property {boolean} [isAutomaticUpdate] - true if the update is the
-     *  result of a gas estimate update from the controller.
-     */
-    /**
-     * Sets the appropriate gas fees in state and determines and sets the
-     * appropriate transactionType based on gas fee fields received.
-     *
-     * @param {SendStateDraft} state - A writable draft of the send state to be
-     *  updated.
-     * @param {import(
-     *  '@reduxjs/toolkit'
-     * ).PayloadAction<GasFeeUpdatePayload>} action
-     */
-    updateGasFees: (state, action) => {
-      if (
-        action.payload.transactionType === TRANSACTION_ENVELOPE_TYPES.FEE_MARKET
-      ) {
-        state.gas.maxFeePerGas = addHexPrefix(action.payload.maxFeePerGas);
-        state.gas.maxPriorityFeePerGas = addHexPrefix(
-          action.payload.maxPriorityFeePerGas,
-        );
-        state.transactionType = TRANSACTION_ENVELOPE_TYPES.FEE_MARKET;
-      } else {
-        // Until we remove the old UI we don't want to automatically update
-        // gasPrice if the user has already manually changed the field value.
-        // When receiving a new estimate the isAutomaticUpdate property will be
-        // on the payload (and set to true). If isAutomaticUpdate is true,
-        // then we check if the previous estimate was '0x0' or if the previous
-        // gasPrice equals the previous gasEstimate. if either of those cases
-        // are true then we update the gasPrice otherwise we skip it because
-        // it indicates the user has ejected from the estimates by modifying
-        // the field.
-        if (
-          action.payload.isAutomaticUpdate !== true ||
-          state.gas.gasPriceEstimate === '0x0' ||
-          state.gas.gasPrice === state.gas.gasPriceEstimate
-        ) {
-          state.gas.gasPrice = addHexPrefix(action.payload.gasPrice);
-        }
-        state.transactionType = TRANSACTION_ENVELOPE_TYPES.LEGACY;
-      }
-      slice.caseReducers.calculateGasTotal(state);
-    },
-    /**
-     * @typedef {Object} GasEstimateUpdatePayload
-     * @property {GasEstimateType} gasEstimateType - The type of gas estimation
-     *  provided by the controller.
-     * @property {(
-     *  EthGasPriceEstimate | LegacyGasPriceEstimate | GasFeeEstimates
-     * )} gasFeeEstimates - The gas fee estimates provided by the controller.
-     */
     /**
      * Sets the appropriate gas fees in state after receiving new estimates.
      *
      * @param {SendStateDraft} state - A writable draft of the send state to be
      *  updated.
-     * @param {(
-     *  import('@reduxjs/toolkit').PayloadAction<GasEstimateUpdatePayload
-     * )} action - The gas fee update payload
+     * @param {GasEstimateUpdatePayload)} action - The gas fee update payload
+     * @returns {void}
      */
     updateGasFeeEstimates: (state, action) => {
       const { gasFeeEstimates, gasEstimateType } = action.payload;
@@ -1199,82 +1016,112 @@ const slice = createSlice({
           break;
       }
       // Record the latest gasPriceEstimate for future comparisons
-      state.gas.gasPriceEstimate = addHexPrefix(gasPriceEstimate);
+      state.gasPriceEstimate = addHexPrefix(gasPriceEstimate);
+    },
+    /**
+     * Sets the appropriate gas fees in state and determines and sets the
+     * appropriate transactionType based on gas fee fields received.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {GasFeeUpdatePayload} action - The gas fees to update with
+     * @returns {void}
+     */
+    updateGasFees: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      if (draftTransaction) {
+        if (
+          action.payload.transactionType ===
+          TRANSACTION_ENVELOPE_TYPES.FEE_MARKET
+        ) {
+          draftTransaction.gas.maxFeePerGas = addHexPrefix(
+            action.payload.maxFeePerGas,
+          );
+          draftTransaction.gas.maxPriorityFeePerGas = addHexPrefix(
+            action.payload.maxPriorityFeePerGas,
+          );
+          draftTransaction.transactionType =
+            TRANSACTION_ENVELOPE_TYPES.FEE_MARKET;
+        } else {
+          // Until we remove the old UI we don't want to automatically update
+          // gasPrice if the user has already manually changed the field value.
+          // When receiving a new estimate the isAutomaticUpdate property will be
+          // on the payload (and set to true). If isAutomaticUpdate is true,
+          // then we check if the previous estimate was '0x0' or if the previous
+          // gasPrice equals the previous gasEstimate. if either of those cases
+          // are true then we update the gasPrice otherwise we skip it because
+          // it indicates the user has ejected from the estimates by modifying
+          // the field.
+          if (
+            action.payload.isAutomaticUpdate !== true ||
+            state.gasPriceEstimate === '0x0' ||
+            draftTransaction.gas.gasPrice === state.gasPriceEstimate
+          ) {
+            draftTransaction.gas.gasPrice = addHexPrefix(
+              action.payload.gasPrice,
+            );
+          }
+          draftTransaction.transactionType = TRANSACTION_ENVELOPE_TYPES.LEGACY;
+        }
+        slice.caseReducers.calculateGasTotal(state);
+      }
+    },
+    /**
+     * sets the provided gasLimit in state and then recomputes the gasTotal.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {SimpleStringPayload} action - The
+     *  gasLimit in hex to set in state.
+     * @returns {void}
+     */
+    updateGasLimit: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      if (draftTransaction) {
+        draftTransaction.gas.gasLimit = addHexPrefix(action.payload);
+        slice.caseReducers.calculateGasTotal(state);
+      }
     },
     /**
      * sets the layer 1 fees total (for a multi-layer fee network)
      *
      * @param {SendStateDraft} state - A writable draft of the send state to be
      *  updated.
-     * @param {import('@reduxjs/toolkit').PayloadAction<string>} action - the
-     *  layer1GasTotal to set in hex wei.
+     * @param {SimpleStringPayload} action - the
+     *  gasTotalForLayer1 to set in hex wei.
+     * @returns {void}
      */
     updateLayer1Fees: (state, action) => {
-      state.multiLayerFees.layer1GasTotal = action.payload;
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      state.gasTotalForLayer1 = action.payload;
       if (
-        state.amount.mode === AMOUNT_MODES.MAX &&
-        state.asset.type === ASSET_TYPES.NATIVE
+        state.amountMode === AMOUNT_MODES.MAX &&
+        draftTransaction.asset.type === ASSET_TYPES.NATIVE
       ) {
         slice.caseReducers.updateAmountToMax(state);
       }
     },
     /**
-     * sets the amount mode to the provided value as long as it is one of the
-     * supported modes (MAX|INPUT)
+     * Updates the recipient of the draftTransaction
      *
      * @param {SendStateDraft} state - A writable draft of the send state to be
      *  updated.
-     * @param {import(
-     *  '@reduxjs/toolkit'
-     * ).PayloadAction<SendStateAmountModeStrings>} action - The amount mode
-     *  to set the state to.
+     * @param {updateRecipientPayload} action - The recipient to set in the
+     *  draftTransaction.
+     * @returns {void}
      */
-    updateAmountMode: (state, action) => {
-      if (Object.values(AMOUNT_MODES).includes(action.payload)) {
-        state.amount.mode = action.payload;
-      }
-    },
-    updateAsset: (state, action) => {
-      state.asset.type = action.payload.type;
-      state.asset.balance = action.payload.balance;
-      state.asset.error = action.payload.error;
-      if (
-        state.asset.type === ASSET_TYPES.TOKEN ||
-        state.asset.type === ASSET_TYPES.COLLECTIBLE
-      ) {
-        state.asset.details = action.payload.details;
-      } else {
-        // clear the details object when sending native currency
-        state.asset.details = null;
-        if (state.recipient.error === CONTRACT_ADDRESS_ERROR) {
-          // Errors related to sending tokens to their own contract address
-          // are no longer valid when sending native currency.
-          state.recipient.error = null;
-        }
-
-        if (state.recipient.warning === KNOWN_RECIPIENT_ADDRESS_WARNING) {
-          // Warning related to sending tokens to a known contract address
-          // are no longer valid when sending native currency.
-          state.recipient.warning = null;
-        }
-      }
-      // if amount mode is MAX update amount to max of new asset, otherwise set
-      // to zero. This will revalidate the send amount field.
-      if (state.amount.mode === AMOUNT_MODES.MAX) {
-        slice.caseReducers.updateAmountToMax(state);
-      } else {
-        slice.caseReducers.updateSendAmount(state, { payload: '0x0' });
-      }
-      // validate send state
-      slice.caseReducers.validateSendState(state);
-    },
     updateRecipient: (state, action) => {
-      state.recipient.error = null;
-      state.recipient.userInput = '';
-      state.recipient.address = action.payload.address ?? '';
-      state.recipient.nickname = action.payload.nickname ?? '';
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      draftTransaction.recipient.error = null;
+      state.recipientInput = '';
+      draftTransaction.recipient.address = action.payload.address ?? '';
+      draftTransaction.recipient.nickname = action.payload.nickname ?? '';
 
-      if (state.recipient.address === '') {
+      if (draftTransaction.recipient.address === '') {
         // If address is null we are clearing the recipient and must return
         // to the ADD_RECIPIENT stage.
         state.stage = SEND_STAGES.ADD_RECIPIENT;
@@ -1282,214 +1129,321 @@ const slice = createSlice({
         // if an address is provided and an id exists, we progress to the EDIT
         // stage, otherwise we progress to the DRAFT stage. We also reset the
         // search mode for recipient search.
-        state.stage = state.id === null ? SEND_STAGES.DRAFT : SEND_STAGES.EDIT;
-        state.recipient.mode = RECIPIENT_SEARCH_MODES.CONTACT_LIST;
+        state.stage =
+          draftTransaction.id === null ? SEND_STAGES.DRAFT : SEND_STAGES.EDIT;
+        state.recipientMode = RECIPIENT_SEARCH_MODES.CONTACT_LIST;
       }
 
       // validate send state
       slice.caseReducers.validateSendState(state);
     },
-    useDefaultGas: (state) => {
-      // Show the default gas price/limit fields in the send page
-      state.gas.isCustomGasSet = false;
+    /**
+     * Clears the user input and changes the recipient search mode to the
+     * specified value
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {UpdateRecipientModePayload} action - The mode to set the
+     *  recipient search to
+     * @returns {void}
+     */
+    updateRecipientSearchMode: (state, action) => {
+      state.recipientInput = '';
+      state.recipientMode = action.payload;
     },
-    useCustomGas: (state) => {
-      // Show the gas fees set in the custom gas modal (state.gas.customData)
-      state.gas.isCustomGasSet = true;
+
+    updateRecipientWarning: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      draftTransaction.recipient.warning = action.payload;
     },
+
+    updateDraftTransactionStatus: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      draftTransaction.status = action.payload;
+    },
+
+    acknowledgeRecipientWarning: (state) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      draftTransaction.recipient.recipientWarningAcknowledged = true;
+      slice.caseReducers.validateSendState(state);
+    },
+
+    /**
+     * Updates the value of the recipientInput key with what the user has
+     * typed into the recipient input field in the UI.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {SimpleStringPayload} action - the value the user has typed into
+     *  the recipient field.
+     * @returns {void}
+     */
     updateRecipientUserInput: (state, action) => {
       // Update the value in state to match what the user is typing into the
       // input field
-      state.recipient.userInput = action.payload;
+      state.recipientInput = action.payload;
     },
-    validateRecipientUserInput: (state, action) => {
-      const { asset, recipient } = state;
-
-      if (
-        recipient.mode === RECIPIENT_SEARCH_MODES.MY_ACCOUNTS ||
-        recipient.userInput === '' ||
-        recipient.userInput === null
-      ) {
-        recipient.error = null;
-        recipient.warning = null;
-      } else {
-        const isSendingToken =
-          asset.type === ASSET_TYPES.TOKEN ||
-          asset.type === ASSET_TYPES.COLLECTIBLE;
-        const { chainId, tokens, tokenAddressList } = action.payload;
-        if (
-          isBurnAddress(recipient.userInput) ||
-          (!isValidHexAddress(recipient.userInput, {
-            mixedCaseUseChecksum: true,
-          }) &&
-            !isValidDomainName(recipient.userInput))
-        ) {
-          recipient.error = isDefaultMetaMaskChain(chainId)
-            ? INVALID_RECIPIENT_ADDRESS_ERROR
-            : INVALID_RECIPIENT_ADDRESS_NOT_ETH_NETWORK_ERROR;
-        } else if (
-          isSendingToken &&
-          isOriginContractAddress(recipient.userInput, asset.details.address)
-        ) {
-          recipient.error = CONTRACT_ADDRESS_ERROR;
-        } else {
-          recipient.error = null;
-        }
-        if (
-          isSendingToken &&
-          isValidHexAddress(recipient.userInput) &&
-          (tokenAddressList.find((address) =>
-            isEqualCaseInsensitive(address, recipient.userInput),
-          ) ||
-            checkExistingAddresses(recipient.userInput, tokens))
-        ) {
-          recipient.warning = KNOWN_RECIPIENT_ADDRESS_WARNING;
-        } else {
-          recipient.warning = null;
-        }
+    /**
+     * update current amount.value in state and run post update validation of
+     * the amount field and the send state.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {SimpleStringPayload} action - The hex string to be set as the
+     *  amount value.
+     * @returns {void}
+     */
+    updateSendAmount: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      draftTransaction.amount.value = addHexPrefix(action.payload);
+      // Once amount has changed, validate the field
+      slice.caseReducers.validateAmountField(state);
+      if (draftTransaction.asset.type === ASSET_TYPES.NATIVE) {
+        // if sending the native asset the amount being sent will impact the
+        // gas field as well because the gas validation takes into
+        // consideration the available balance minus amount sent before
+        // checking if there is enough left to cover the gas fee.
+        slice.caseReducers.validateGasField(state);
       }
+      // validate send state
+      slice.caseReducers.validateSendState(state);
     },
-    updateRecipientSearchMode: (state, action) => {
-      state.recipient.userInput = '';
-      state.recipient.mode = action.payload;
+    /**
+     * updates the userInputHexData state key
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @param {SimpleStringPayload} action - The hex string to be set as the
+     *  userInputHexData value.
+     * @returns {void}
+     */
+    updateUserInputHexData: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+      draftTransaction.userInputHexData = action.payload;
     },
-    resetSendState: () => initialState,
+    /**
+     * Updates the gasIsSetInModal property to true which results in showing
+     * the gas fees from the custom gas modal in the send page.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @returns {void}
+     */
+    useCustomGas: (state) => {
+      state.gasIsSetInModal = true;
+    },
+    /**
+     * Updates the gasIsSetInModal property to false which results in showing
+     * the default gas price/limit fields in the send page.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @returns {void}
+     */
+    useDefaultGas: (state) => {
+      state.gasIsSetInModal = false;
+    },
+    /**
+     * Checks for the validity of the draftTransactions selected amount to send
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @returns {void}
+     */
     validateAmountField: (state) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
       switch (true) {
         // set error to INSUFFICIENT_FUNDS_ERROR if the account balance is lower
         // than the total price of the transaction inclusive of gas fees.
-        case state.asset.type === ASSET_TYPES.NATIVE &&
+        case draftTransaction.asset.type === ASSET_TYPES.NATIVE &&
           !isBalanceSufficient({
-            amount: state.amount.value,
-            balance: state.asset.balance,
-            gasTotal: state.gas.gasTotal ?? '0x0',
+            amount: draftTransaction.amount.value,
+            balance: draftTransaction.asset.balance,
+            gasTotal: draftTransaction.gas.gasTotal ?? '0x0',
           }):
-          state.amount.error = INSUFFICIENT_FUNDS_ERROR;
+          draftTransaction.amount.error = INSUFFICIENT_FUNDS_ERROR;
           break;
         // set error to INSUFFICIENT_FUNDS_ERROR if the token balance is lower
         // than the amount of token the user is attempting to send.
-        case state.asset.type === ASSET_TYPES.TOKEN &&
+        case draftTransaction.asset.type === ASSET_TYPES.TOKEN &&
           !isTokenBalanceSufficient({
-            tokenBalance: state.asset.balance ?? '0x0',
-            amount: state.amount.value,
-            decimals: state.asset.details.decimals,
+            tokenBalance: draftTransaction.asset.balance ?? '0x0',
+            amount: draftTransaction.amount.value,
+            decimals: draftTransaction.asset.details.decimals,
           }):
-          state.amount.error = INSUFFICIENT_TOKENS_ERROR;
+          draftTransaction.amount.error = INSUFFICIENT_TOKENS_ERROR;
           break;
         // if the amount is negative, set error to NEGATIVE_ETH_ERROR
         // TODO: change this to NEGATIVE_ERROR and remove the currency bias.
         case conversionGreaterThan(
           { value: 0, fromNumericBase: 'dec' },
-          { value: state.amount.value, fromNumericBase: 'hex' },
+          { value: draftTransaction.amount.value, fromNumericBase: 'hex' },
         ):
-          state.amount.error = NEGATIVE_ETH_ERROR;
+          draftTransaction.amount.error = NEGATIVE_ETH_ERROR;
           break;
         // If none of the above are true, set error to null
         default:
-          state.amount.error = null;
+          draftTransaction.amount.error = null;
       }
     },
+    /**
+     * Checks if the user has enough funds to cover the cost of gas, always
+     * uses the native currency and does not take into account the amount
+     * being sent. If the user has enough to cover cost of gas but not gas
+     * + amount then the error will be displayed on the amount field.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @returns {void}
+     */
     validateGasField: (state) => {
-      // Checks if the user has enough funds to cover the cost of gas, always
-      // uses the native currency and does not take into account the amount
-      // being sent. If the user has enough to cover cost of gas but not gas
-      // + amount then the error will be displayed on the amount field.
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
       const insufficientFunds = !isBalanceSufficient({
         amount:
-          state.asset.type === ASSET_TYPES.NATIVE ? state.amount.value : '0x0',
-        balance: state.account.balance,
-        gasTotal: state.gas.gasTotal ?? '0x0',
+          draftTransaction.asset.type === ASSET_TYPES.NATIVE
+            ? draftTransaction.amount.value
+            : '0x0',
+        balance:
+          draftTransaction.fromAccount?.balance ??
+          state.selectedAccount.balance,
+        gasTotal: draftTransaction.gas.gasTotal ?? '0x0',
       });
 
-      state.gas.error = insufficientFunds ? INSUFFICIENT_FUNDS_ERROR : null;
+      draftTransaction.gas.error = insufficientFunds
+        ? INSUFFICIENT_FUNDS_ERROR
+        : null;
     },
+    validateRecipientUserInput: (state, action) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
+
+      if (draftTransaction) {
+        if (
+          state.recipientMode === RECIPIENT_SEARCH_MODES.MY_ACCOUNTS ||
+          state.recipientInput === '' ||
+          state.recipientInput === null
+        ) {
+          draftTransaction.recipient.error = null;
+          draftTransaction.recipient.warning = null;
+        } else {
+          const {
+            chainId,
+            tokens,
+            tokenAddressList,
+            isProbablyAnAssetContract,
+          } = action.payload;
+
+          if (
+            isBurnAddress(state.recipientInput) ||
+            (!isValidHexAddress(state.recipientInput, {
+              mixedCaseUseChecksum: true,
+            }) &&
+              !isValidDomainName(state.recipientInput))
+          ) {
+            draftTransaction.recipient.error = isDefaultMetaMaskChain(chainId)
+              ? INVALID_RECIPIENT_ADDRESS_ERROR
+              : INVALID_RECIPIENT_ADDRESS_NOT_ETH_NETWORK_ERROR;
+          } else if (
+            isOriginContractAddress(
+              state.recipientInput,
+              draftTransaction.asset?.details?.address,
+            )
+          ) {
+            draftTransaction.recipient.error = CONTRACT_ADDRESS_ERROR;
+          } else {
+            draftTransaction.recipient.error = null;
+          }
+          if (
+            (isValidHexAddress(state.recipientInput) &&
+              (tokenAddressList.find((address) =>
+                isEqualCaseInsensitive(address, state.recipientInput),
+              ) ||
+                checkExistingAddresses(state.recipientInput, tokens))) ||
+            isProbablyAnAssetContract
+          ) {
+            draftTransaction.recipient.warning = KNOWN_RECIPIENT_ADDRESS_WARNING;
+          } else {
+            draftTransaction.recipient.warning = null;
+          }
+        }
+      }
+      slice.caseReducers.validateSendState(state);
+    },
+    /**
+     * Checks if the draftTransaction is currently valid. The following list of
+     * cases from the switch statement in this function describe when the
+     * transaction is invalid. Please keep this comment updated.
+     *
+     * case 1: State is invalid when amount field has an error.
+     * case 2: State is invalid when gas field has an error.
+     * case 3: State is invalid when asset field has an error.
+     * case 4: State is invalid if asset type is a token and the token details
+     *  are unknown.
+     * case 5: State is invalid if no recipient has been added.
+     * case 6: State is invalid if the send state is uninitialized.
+     * case 7: State is invalid if gas estimates are loading.
+     * case 8: State is invalid if gasLimit is less than the gasLimitMinimum.
+     *
+     * @param {SendStateDraft} state - A writable draft of the send state to be
+     *  updated.
+     * @returns {void}
+     */
     validateSendState: (state) => {
+      const draftTransaction =
+        state.draftTransactions[state.currentTransactionUUID];
       switch (true) {
-        // 1 + 2. State is invalid when either gas or amount or asset fields have errors
-        // 3. State is invalid if asset type is a token and the token details
-        //  are unknown.
-        // 4. State is invalid if no recipient has been added
-        // 5. State is invalid if the send state is uninitialized
-        // 6. State is invalid if gas estimates are loading
-        // 7. State is invalid if gasLimit is less than the minimumGasLimit
-        // 8. State is invalid if the selected asset is a ERC721
-        case Boolean(state.amount.error):
-        case Boolean(state.gas.error):
-        case Boolean(state.asset.error):
-        case state.asset.type === ASSET_TYPES.TOKEN &&
-          state.asset.details === null:
+        case Boolean(draftTransaction.amount.error):
+        case Boolean(draftTransaction.gas.error):
+        case Boolean(draftTransaction.asset.error):
+        case draftTransaction.asset.type === ASSET_TYPES.TOKEN &&
+          draftTransaction.asset.details === null:
         case state.stage === SEND_STAGES.ADD_RECIPIENT:
         case state.stage === SEND_STAGES.INACTIVE:
-        case state.gas.isGasEstimateLoading:
-        case new BigNumber(state.gas.gasLimit, 16).lessThan(
-          new BigNumber(state.gas.minimumGasLimit),
+        case state.gasEstimateIsLoading:
+        case new BigNumber(draftTransaction.gas.gasLimit, 16).lessThan(
+          new BigNumber(state.gasLimitMinimum),
         ):
-          state.status = SEND_STATUSES.INVALID;
+          draftTransaction.status = SEND_STATUSES.INVALID;
+          break;
+        case draftTransaction.recipient.warning === 'loading':
+        case draftTransaction.recipient.warning ===
+          KNOWN_RECIPIENT_ADDRESS_WARNING &&
+          draftTransaction.recipient.recipientWarningAcknowledged === false:
+          draftTransaction.status = SEND_STATUSES.INVALID;
           break;
         default:
-          state.status = SEND_STATUSES.VALID;
+          draftTransaction.status = SEND_STATUSES.VALID;
       }
     },
   },
   extraReducers: (builder) => {
     builder
-      .addCase(QR_CODE_DETECTED, (state, action) => {
-        // When data is received from the QR Code Scanner we set the recipient
-        // as long as a valid address can be pulled from the data. If an
-        // address is pulled but it is invalid, we display an error.
-        const qrCodeData = action.value;
-        if (qrCodeData) {
-          if (qrCodeData.type === 'address') {
-            const scannedAddress = qrCodeData.values.address.toLowerCase();
-            if (
-              isValidHexAddress(scannedAddress, { allowNonPrefixed: false })
-            ) {
-              if (state.recipient.address !== scannedAddress) {
-                slice.caseReducers.updateRecipient(state, {
-                  payload: { address: scannedAddress },
-                });
-              }
-            } else {
-              state.recipient.error = INVALID_RECIPIENT_ADDRESS_ERROR;
-            }
-          }
-        }
-      })
-      .addCase(SELECTED_ACCOUNT_CHANGED, (state, action) => {
-        // If we are on the edit flow the account we are keyed into will be the
-        // original 'from' account, which may differ from the selected account
-        if (state.stage !== SEND_STAGES.EDIT) {
-          // This event occurs when the user selects a new account from the
-          // account menu, or the currently active account's balance updates.
-          state.account.balance = action.payload.account.balance;
-          state.account.address = action.payload.account.address;
-          // We need to update the asset balance if the asset is the native
-          // network asset. Once we update the balance we recompute error state.
-          if (state.asset.type === ASSET_TYPES.NATIVE) {
-            state.asset.balance = action.payload.account.balance;
-          }
-          slice.caseReducers.validateAmountField(state);
-          slice.caseReducers.validateGasField(state);
-          slice.caseReducers.validateSendState(state);
-        }
-      })
       .addCase(ACCOUNT_CHANGED, (state, action) => {
         // If we are on the edit flow then we need to watch for changes to the
         // current account.address in state and keep balance updated
         // appropriately
         if (
           state.stage === SEND_STAGES.EDIT &&
-          action.payload.account.address === state.account.address
+          action.payload.account.address === state.selectedAccount.address
         ) {
           // This event occurs when the user's account details update due to
           // background state changes. If the account that is being updated is
           // the current from account on the edit flow we need to update
           // the balance for the account and revalidate the send state.
-          state.account.balance = action.payload.account.balance;
+          state.selectedAccount.balance = action.payload.account.balance;
           // We need to update the asset balance if the asset is the native
           // network asset. Once we update the balance we recompute error state.
-          if (state.asset.type === ASSET_TYPES.NATIVE) {
-            state.asset.balance = action.payload.account.balance;
+          const draftTransaction =
+            state.draftTransactions[state.currentTransactionUUID];
+          if (draftTransaction?.asset.type === ASSET_TYPES.NATIVE) {
+            draftTransaction.asset.balance = action.payload.account.balance;
           }
           slice.caseReducers.validateAmountField(state);
           slice.caseReducers.validateGasField(state);
@@ -1501,35 +1455,87 @@ const slice = createSlice({
         // to check to see if an entry exists for the current address or if the
         // entry changed.
         const { addressBook } = action.payload;
-        if (addressBook[state.recipient.address]?.name) {
-          state.recipient.nickname = addressBook[state.recipient.address].name;
+        const draftTransaction =
+          state.draftTransactions[state.currentTransactionUUID];
+        if (
+          draftTransaction &&
+          addressBook[draftTransaction.recipient.address]?.name
+        ) {
+          draftTransaction.recipient.nickname =
+            addressBook[draftTransaction.recipient.address].name;
         }
       })
+      .addCase(computeEstimatedGasLimit.pending, (state) => {
+        // When we begin to fetch gasLimit we should indicate we are loading
+        // a gas estimate.
+        state.gasEstimateIsLoading = true;
+      })
+      .addCase(computeEstimatedGasLimit.fulfilled, (state, action) => {
+        // When we receive a new gasLimit from the computeEstimatedGasLimit
+        // thunk we need to update our gasLimit in the slice. We call into the
+        // caseReducer updateGasLimit to tap into the appropriate follow up
+        // checks and gasTotal calculation. First set gasEstimateIsLoading to
+        // false.
+        state.gasEstimateIsLoading = false;
+        if (action.payload?.gasLimit) {
+          slice.caseReducers.updateGasLimit(state, {
+            payload: action.payload.gasLimit,
+          });
+        }
+        if (action.payload?.gasTotalForLayer1) {
+          slice.caseReducers.updateLayer1Fees(state, {
+            payload: action.payload.gasTotalForLayer1,
+          });
+        }
+      })
+      .addCase(computeEstimatedGasLimit.rejected, (state) => {
+        // If gas estimation fails, we should set the loading state to false,
+        // because it is no longer loading
+        state.gasEstimateIsLoading = false;
+      })
+      .addCase(GAS_FEE_ESTIMATES_UPDATED, (state, action) => {
+        // When the gasFeeController updates its gas fee estimates we need to
+        // update and validate state based on those new values
+        slice.caseReducers.updateGasFeeEstimates(state, {
+          payload: action.payload,
+        });
+      })
       .addCase(initializeSendState.pending, (state) => {
         // when we begin initializing state, which can happen when switching
-        // chains even after loading the send flow, we set
-        // gas.isGasEstimateLoading as initialization will trigger a fetch
-        // for gasPrice estimates.
-        state.gas.isGasEstimateLoading = true;
+        // chains even after loading the send flow, we set gasEstimateIsLoading
+        // as initialization will trigger a fetch for gasPrice estimates.
+        state.gasEstimateIsLoading = true;
       })
       .addCase(initializeSendState.fulfilled, (state, action) => {
         // writes the computed initialized state values into the slice and then
         // calculates slice validity using the caseReducers.
         state.eip1559support = action.payload.eip1559support;
-        state.account.address = action.payload.address;
-        state.account.balance = action.payload.nativeBalance;
-        state.asset.balance = action.payload.assetBalance;
-        state.gas.gasLimit = action.payload.gasLimit;
+        state.selectedAccount.address = action.payload.account.address;
+        state.selectedAccount.balance = action.payload.account.balance;
+        const draftTransaction =
+          state.draftTransactions[state.currentTransactionUUID];
+        draftTransaction.gas.gasLimit = action.payload.gasLimit;
         slice.caseReducers.updateGasFeeEstimates(state, {
           payload: {
             gasFeeEstimates: action.payload.gasFeeEstimates,
             gasEstimateType: action.payload.gasEstimateType,
           },
         });
-        state.gas.gasTotal = action.payload.gasTotal;
-        state.gas.gasEstimatePollToken = action.payload.gasEstimatePollToken;
+        draftTransaction.gas.gasTotal = action.payload.gasTotal;
+        state.gasEstimatePollToken = action.payload.gasEstimatePollToken;
+        if (action.payload.chainHasChanged) {
+          // If the state was reinitialized as a result of the user changing
+          // the network from the network dropdown, then the selected asset is
+          // no longer valid and should be set to the native asset for the
+          // network.
+          draftTransaction.asset.type = ASSET_TYPES.NATIVE;
+          draftTransaction.asset.balance =
+            draftTransaction.fromAccount?.balance ??
+            state.selectedAccount.balance;
+          draftTransaction.asset.details = null;
+        }
         if (action.payload.gasEstimatePollToken) {
-          state.gas.isGasEstimateLoading = false;
+          state.gasEstimateIsLoading = false;
         }
         if (state.stage !== SEND_STAGES.INACTIVE) {
           slice.caseReducers.validateRecipientUserInput(state, {
@@ -1541,48 +1547,59 @@ const slice = createSlice({
             },
           });
         }
-        state.stage =
-          state.stage === SEND_STAGES.INACTIVE
-            ? SEND_STAGES.ADD_RECIPIENT
-            : state.stage;
         slice.caseReducers.validateAmountField(state);
         slice.caseReducers.validateGasField(state);
         slice.caseReducers.validateSendState(state);
       })
-      .addCase(computeEstimatedGasLimit.pending, (state) => {
-        // When we begin to fetch gasLimit we should indicate we are loading
-        // a gas estimate.
-        state.gas.isGasEstimateLoading = true;
-      })
-      .addCase(computeEstimatedGasLimit.fulfilled, (state, action) => {
-        // When we receive a new gasLimit from the computeEstimatedGasLimit
-        // thunk we need to update our gasLimit in the slice. We call into the
-        // caseReducer updateGasLimit to tap into the appropriate follow up
-        // checks and gasTotal calculation. First set isGasEstimateLoading to
-        // false.
-        state.gas.isGasEstimateLoading = false;
-        if (action.payload?.gasLimit) {
-          slice.caseReducers.updateGasLimit(state, {
-            payload: action.payload.gasLimit,
-          });
-        }
-        if (action.payload?.layer1GasTotal) {
-          slice.caseReducers.updateLayer1Fees(state, {
-            payload: action.payload.layer1GasTotal,
-          });
+      .addCase(SELECTED_ACCOUNT_CHANGED, (state, action) => {
+        // If we are on the edit flow the account we are keyed into will be the
+        // original 'from' account, which may differ from the selected account
+        if (state.stage !== SEND_STAGES.EDIT) {
+          // This event occurs when the user selects a new account from the
+          // account menu, or the currently active account's balance updates.
+          state.selectedAccount.balance = action.payload.account.balance;
+          state.selectedAccount.address = action.payload.account.address;
+          const draftTransaction =
+            state.draftTransactions[state.currentTransactionUUID];
+          // This action will occur even when we aren't on the send flow, which
+          // is okay as it keeps the selectedAccount details up to date. We do
+          // not need to validate anything if there isn't a current draft
+          // transaction. If there is, we need to update the asset balance if
+          // the asset is set to the native network asset, and then validate
+          // the transaction.
+          if (draftTransaction) {
+            if (draftTransaction?.asset.type === ASSET_TYPES.NATIVE) {
+              draftTransaction.asset.balance = action.payload.account.balance;
+            }
+            slice.caseReducers.validateAmountField(state);
+            slice.caseReducers.validateGasField(state);
+            slice.caseReducers.validateSendState(state);
+          }
         }
       })
-      .addCase(computeEstimatedGasLimit.rejected, (state) => {
-        // If gas estimation fails, we should set the loading state to false,
-        // because it is no longer loading
-        state.gas.isGasEstimateLoading = false;
-      })
-      .addCase(GAS_FEE_ESTIMATES_UPDATED, (state, action) => {
-        // When the gasFeeController updates its gas fee estimates we need to
-        // update and validate state based on those new values
-        slice.caseReducers.updateGasFeeEstimates(state, {
-          payload: action.payload,
-        });
+      .addCase(QR_CODE_DETECTED, (state, action) => {
+        // When data is received from the QR Code Scanner we set the recipient
+        // as long as a valid address can be pulled from the data. If an
+        // address is pulled but it is invalid, we display an error.
+        const qrCodeData = action.value;
+        const draftTransaction =
+          state.draftTransactions[state.currentTransactionUUID];
+        if (qrCodeData && draftTransaction) {
+          if (qrCodeData.type === 'address') {
+            const scannedAddress = qrCodeData.values.address.toLowerCase();
+            if (
+              isValidHexAddress(scannedAddress, { allowNonPrefixed: false })
+            ) {
+              if (draftTransaction.recipient.address !== scannedAddress) {
+                slice.caseReducers.updateRecipient(state, {
+                  payload: { address: scannedAddress },
+                });
+              }
+            } else {
+              draftTransaction.recipient.error = INVALID_RECIPIENT_ADDRESS_ERROR;
+            }
+          }
+        }
       });
   },
 });
@@ -1598,12 +1615,157 @@ const {
   validateRecipientUserInput,
   updateRecipientSearchMode,
   addHistoryEntry,
+  acknowledgeRecipientWarning,
 } = actions;
 
-export { useDefaultGas, useCustomGas, updateGasLimit, addHistoryEntry };
+export {
+  useDefaultGas,
+  useCustomGas,
+  updateGasLimit,
+  addHistoryEntry,
+  acknowledgeRecipientWarning,
+};
 
 // Action Creators
 
+/**
+ * This method is for usage when validating user input so that validation
+ * is only run after a delay in typing of 300ms. Usage at callsites requires
+ * passing in both the dispatch method and the payload to dispatch, which makes
+ * it only applicable for use within action creators.
+ */
+const debouncedValidateRecipientUserInput = debounce(
+  (dispatch, payload, resolve) => {
+    dispatch(
+      addHistoryEntry(
+        `sendFlow - user typed ${payload.userInput} into recipient input field`,
+      ),
+    );
+    dispatch(validateRecipientUserInput(payload));
+    resolve();
+  },
+  300,
+);
+
+/**
+ * Begins a new draft transaction, derived from the txParams of an existing
+ * transaction in the TransactionController. This action will first clear out
+ * the previous draft transactions and currentTransactionUUID from state. This
+ * action is one of the two entry points into the send flow. NOTE: You must
+ * route to the send page *after* dispatching this action resolves to ensure
+ * that the draftTransaction is properly created.
+ *
+ * @param {AssetTypesString} assetType - The type of asset the transaction
+ *  being edited was sending. The details of the asset will be retrieved from
+ *  the transaction data in state.
+ * @param {string} transactionId - The id of the transaction being edited.
+ * @returns {ThunkAction<void>}
+ */
+export function editExistingTransaction(assetType, transactionId) {
+  return async (dispatch, getState) => {
+    await dispatch(actions.clearPreviousDrafts());
+    const state = getState();
+    const unapprovedTransactions = getUnapprovedTxs(state);
+    const transaction = unapprovedTransactions[transactionId];
+    const account = getTargetAccount(state, transaction.txParams.from);
+
+    if (assetType === ASSET_TYPES.NATIVE) {
+      await dispatch(
+        actions.addNewDraft({
+          ...draftTransactionInitialState,
+          id: transactionId,
+          fromAccount: account,
+          gas: {
+            ...draftTransactionInitialState.gas,
+            gasLimit: transaction.txParams.gas,
+            gasPrice: transaction.txParams.gasPrice,
+          },
+          userInputHexData: transaction.txParams.data,
+          recipient: {
+            ...draftTransactionInitialState.recipient,
+            address: transaction.txParams.to,
+            nickname:
+              getAddressBookEntryOrAccountName(state, transaction.txParams.to)
+                ?.name ?? '',
+          },
+          amount: {
+            ...draftTransactionInitialState.amount,
+            value: transaction.txParams.value,
+          },
+          history: [
+            `sendFlow - user clicked edit on transaction with id ${transactionId}`,
+          ],
+        }),
+      );
+      await dispatch(
+        updateSendAsset(
+          { type: ASSET_TYPES.NATIVE },
+          { skipComputeEstimatedGasLimit: true },
+        ),
+      );
+    } else {
+      const tokenData = parseStandardTokenTransactionData(
+        transaction.txParams.data,
+      );
+      const tokenAmountInDec =
+        assetType === ASSET_TYPES.TOKEN ? getTokenValueParam(tokenData) : '1';
+      const address = getTokenAddressParam(tokenData);
+      const nickname =
+        getAddressBookEntryOrAccountName(state, address)?.name ?? '';
+
+      const tokenAmountInHex = addHexPrefix(
+        conversionUtil(tokenAmountInDec, {
+          fromNumericBase: 'dec',
+          toNumericBase: 'hex',
+        }),
+      );
+
+      await dispatch(
+        actions.addNewDraft({
+          ...draftTransactionInitialState,
+          id: transactionId,
+          fromAccount: account,
+          gas: {
+            ...draftTransactionInitialState.gas,
+            gasLimit: transaction.txParams.gas,
+            gasPrice: transaction.txParams.gasPrice,
+          },
+          userInputHexData: transaction.txParams.data,
+          recipient: {
+            ...draftTransactionInitialState.recipient,
+            address,
+            nickname,
+          },
+          amount: {
+            ...draftTransactionInitialState.amount,
+            value: tokenAmountInHex,
+          },
+          history: [
+            `sendFlow - user clicked edit on transaction with id ${transactionId}`,
+          ],
+        }),
+      );
+
+      await dispatch(
+        updateSendAsset(
+          {
+            type: assetType,
+            details: {
+              address: transaction.txParams.to,
+              ...(assetType === ASSET_TYPES.COLLECTIBLE
+                ? { tokenId: getTokenValueParam(tokenData) }
+                : {}),
+            },
+          },
+          { skipComputeEstimatedGasLimit: true },
+        ),
+      );
+    }
+
+    await dispatch(initializeSendState());
+  };
+}
+
 /**
  * This method is a temporary placeholder to support the old UI in both the
  * gas modal and the send flow. Soon we won't need to modify gasPrice from the
@@ -1614,6 +1776,7 @@ export { useDefaultGas, useCustomGas, updateGasLimit, addHistoryEntry };
  *
  * @deprecated - don't extend the usage of this temporary method
  * @param {string} gasPrice - new gas price in hex wei
+ * @returns {ThunkAction<void>}
  */
 export function updateGasPrice(gasPrice) {
   return (dispatch) => {
@@ -1629,255 +1792,6 @@ export function updateGasPrice(gasPrice) {
   };
 }
 
-export function resetSendState() {
-  return async (dispatch, getState) => {
-    const state = getState();
-    dispatch(actions.resetSendState());
-
-    if (state[name].gas.gasEstimatePollToken) {
-      await disconnectGasFeeEstimatePoller(
-        state[name].gas.gasEstimatePollToken,
-      );
-      removePollingTokenFromAppState(state[name].gas.gasEstimatePollToken);
-    }
-  };
-}
-/**
- * Updates the amount the user intends to send and performs side effects.
- * 1. If the current mode is MAX change to INPUT
- * 2. If sending a token, recompute the gasLimit estimate
- *
- * @param {string} amount - hex string representing value
- */
-export function updateSendAmount(amount) {
-  return async (dispatch, getState) => {
-    const state = getState();
-    const { metamask } = state;
-    let logAmount = amount;
-    if (state[name].asset.type === ASSET_TYPES.TOKEN) {
-      const multiplier = Math.pow(
-        10,
-        Number(state[name].asset.details?.decimals || 0),
-      );
-      const decimalValueString = conversionUtil(addHexPrefix(amount), {
-        fromNumericBase: 'hex',
-        toNumericBase: 'dec',
-        toCurrency: state[name].asset.details?.symbol,
-        conversionRate: multiplier,
-        invertConversionRate: true,
-      });
-
-      logAmount = `${Number(decimalValueString) ? decimalValueString : ''} ${
-        state[name].asset.details?.symbol
-      }`;
-    } else {
-      const ethValue = getValueFromWeiHex({
-        value: amount,
-        toCurrency: ETH,
-        numberOfDecimals: 8,
-      });
-      logAmount = `${ethValue} ${metamask?.provider?.ticker || ETH}`;
-    }
-    await dispatch(
-      addHistoryEntry(`sendFlow - user set amount to ${logAmount}`),
-    );
-    await dispatch(actions.updateSendAmount(amount));
-    if (state.send.amount.mode === AMOUNT_MODES.MAX) {
-      await dispatch(actions.updateAmountMode(AMOUNT_MODES.INPUT));
-    }
-    await dispatch(computeEstimatedGasLimit());
-  };
-}
-
-/**
- * Defines the shape for the details input parameter for updateSendAsset
- *
- * @typedef {Object} TokenDetails
- * @property {string} address - The contract address for the ERC20 token.
- * @property {string} decimals - The number of token decimals.
- * @property {string} symbol - The asset symbol to display.
- */
-
-/**
- * updates the asset to send to one of NATIVE or TOKEN and ensures that the
- * asset balance is set. If sending a TOKEN also updates the asset details
- * object with the appropriate ERC20 details including address, symbol and
- * decimals.
- *
- * @param {Object} payload - action payload
- * @param {string} payload.type - type of asset to send
- * @param {TokenDetails} [payload.details] - ERC20 details if sending TOKEN asset
- */
-export function updateSendAsset({ type, details }) {
-  return async (dispatch, getState) => {
-    dispatch(addHistoryEntry(`sendFlow - user set asset type to ${type}`));
-    dispatch(
-      addHistoryEntry(
-        `sendFlow - user set asset symbol to ${details?.symbol ?? 'undefined'}`,
-      ),
-    );
-    dispatch(
-      addHistoryEntry(
-        `sendFlow - user set asset address to ${
-          details?.address ?? 'undefined'
-        }`,
-      ),
-    );
-    const state = getState();
-    let { balance, error } = state.send.asset;
-    const userAddress = state.send.account.address ?? getSelectedAddress(state);
-    if (type === ASSET_TYPES.TOKEN) {
-      if (details) {
-        if (details.standard === undefined) {
-          await dispatch(showLoadingIndication());
-          const { standard } = await getTokenStandardAndDetails(
-            details.address,
-            userAddress,
-          );
-          if (
-            process.env.COLLECTIBLES_V1 &&
-            (standard === TOKEN_STANDARDS.ERC721 ||
-              standard === TOKEN_STANDARDS.ERC1155)
-          ) {
-            await dispatch(hideLoadingIndication());
-            dispatch(
-              showModal({
-                name: 'CONVERT_TOKEN_TO_NFT',
-                tokenAddress: details.address,
-              }),
-            );
-            error = INVALID_ASSET_TYPE;
-            throw new Error(error);
-          }
-          details.standard = standard;
-        }
-
-        // if changing to a token, get the balance from the network. The asset
-        // overview page and asset list on the wallet overview page contain
-        // send buttons that call this method before initialization occurs.
-        // When this happens we don't yet have an account.address so default to
-        // the currently active account. In addition its possible for the balance
-        // check to take a decent amount of time, so we display a loading
-        // indication so that that immediate feedback is displayed to the user.
-        if (details.standard === TOKEN_STANDARDS.ERC20) {
-          error = null;
-          balance = await getERC20Balance(details, userAddress);
-        }
-        await dispatch(hideLoadingIndication());
-      }
-    } else if (type === ASSET_TYPES.COLLECTIBLE) {
-      let isCurrentOwner = true;
-      try {
-        isCurrentOwner = await isCollectibleOwner(
-          getSelectedAddress(state),
-          details.address,
-          details.tokenId,
-        );
-      } catch (err) {
-        if (err.message.includes('Unable to verify ownership.')) {
-          // this would indicate that either our attempts to verify ownership failed because of network issues,
-          // or, somehow a token has been added to collectibles state with an incorrect chainId.
-        } else {
-          // Any other error is unexpected and should be surfaced.
-          dispatch(displayWarning(err.message));
-        }
-      }
-
-      if (details.standard === undefined) {
-        const { standard } = await getTokenStandardAndDetails(
-          details.address,
-          userAddress,
-        );
-        details.standard = standard;
-      }
-
-      if (details.standard === TOKEN_STANDARDS.ERC1155) {
-        throw new Error('Sends of ERC1155 tokens are not currently supported');
-      }
-
-      if (isCurrentOwner) {
-        error = null;
-        balance = '0x1';
-      } else {
-        throw new Error(
-          'Send slice initialized as collectible send with a collectible not currently owned by the select account',
-        );
-      }
-    } else {
-      error = null;
-      // if changing to native currency, get it from the account key in send
-      // state which is kept in sync when accounts change.
-      balance = state.send.account.balance;
-    }
-    // update the asset in state which will re-run amount and gas validation
-    await dispatch(actions.updateAsset({ type, details, balance, error }));
-    await dispatch(computeEstimatedGasLimit());
-  };
-}
-
-/**
- * This method is for usage when validating user input so that validation
- * is only run after a delay in typing of 300ms. Usage at callsites requires
- * passing in both the dispatch method and the payload to dispatch, which makes
- * it only applicable for use within action creators.
- */
-const debouncedValidateRecipientUserInput = debounce((dispatch, payload) => {
-  dispatch(
-    addHistoryEntry(
-      `sendFlow - user typed ${payload.userInput} into recipient input field`,
-    ),
-  );
-  dispatch(validateRecipientUserInput(payload));
-}, 300);
-
-/**
- * This method is called to update the user's input into the ENS input field.
- * Once the field is updated, the field will be validated using a debounced
- * version of the validateRecipientUserInput action. This way validation only
- * occurs once the user has stopped typing.
- *
- * @param {string} userInput - the value that the user is typing into the field
- */
-export function updateRecipientUserInput(userInput) {
-  return async (dispatch, getState) => {
-    await dispatch(actions.updateRecipientUserInput(userInput));
-    const state = getState();
-    const chainId = getCurrentChainId(state);
-    const tokens = getTokens(state);
-    const useTokenDetection = getUseTokenDetection(state);
-    const tokenAddressList = Object.keys(getTokenList(state));
-    debouncedValidateRecipientUserInput(dispatch, {
-      userInput,
-      chainId,
-      tokens,
-      useTokenDetection,
-      tokenAddressList,
-    });
-  };
-}
-
-export function useContactListForRecipientSearch() {
-  return (dispatch) => {
-    dispatch(
-      addHistoryEntry(
-        `sendFlow - user selected back to all on recipient screen`,
-      ),
-    );
-    dispatch(updateRecipientSearchMode(RECIPIENT_SEARCH_MODES.CONTACT_LIST));
-  };
-}
-
-export function useMyAccountsForRecipientSearch() {
-  return (dispatch) => {
-    dispatch(
-      addHistoryEntry(
-        `sendFlow - user selected transfer to my accounts on recipient screen`,
-      ),
-    );
-    dispatch(updateRecipientSearchMode(RECIPIENT_SEARCH_MODES.MY_ACCOUNTS));
-  };
-}
-
 /**
  * Updates the recipient in state based on the input provided, and then will
  * recompute gas limit when sending a TOKEN asset type. Changing the recipient
@@ -1893,6 +1807,7 @@ export function useMyAccountsForRecipientSearch() {
  * @param {string} recipient.address - hex address to send the transaction to
  * @param {string} [recipient.nickname] - Alias for the address to display
  *  to the user
+ * @returns {ThunkAction<void>}
  */
 export function updateRecipient({ address, nickname }) {
   return async (dispatch, getState) => {
@@ -1912,15 +1827,259 @@ export function updateRecipient({ address, nickname }) {
 }
 
 /**
- * Clears out the recipient user input, ENS resolution and recipient validation.
+ * This method is called to update the user's input into the ENS input field.
+ * Once the field is updated, the field will be validated using a debounced
+ * version of the validateRecipientUserInput action. This way validation only
+ * occurs once the user has stopped typing.
+ *
+ * @param {string} userInput - the value that the user is typing into the field
  */
-export function resetRecipientInput() {
-  return async (dispatch) => {
-    await dispatch(addHistoryEntry(`sendFlow - user cleared recipient input`));
-    await dispatch(updateRecipientUserInput(''));
-    await dispatch(updateRecipient({ address: '', nickname: '' }));
-    await dispatch(resetEnsResolution());
-    await dispatch(validateRecipientUserInput());
+export function updateRecipientUserInput(userInput) {
+  return async (dispatch, getState) => {
+    dispatch(actions.updateRecipientWarning('loading'));
+    dispatch(actions.updateDraftTransactionStatus(SEND_STATUSES.INVALID));
+    await dispatch(actions.updateRecipientUserInput(userInput));
+    const state = getState();
+    const draftTransaction =
+      state[name].draftTransactions[state[name].currentTransactionUUID];
+    const sendingAddress =
+      draftTransaction.fromAccount?.address ??
+      state[name].selectedAccount.address ??
+      getSelectedAddress(state);
+    const chainId = getCurrentChainId(state);
+    const tokens = getTokens(state);
+    const useTokenDetection = getUseTokenDetection(state);
+    const tokenMap = getTokenList(state);
+    const tokenAddressList = Object.keys(tokenMap);
+
+    const inputIsValidHexAddress = isValidHexAddress(userInput);
+    let isProbablyAnAssetContract = false;
+    if (inputIsValidHexAddress) {
+      const { symbol, decimals } = getTokenMetadata(userInput, tokenMap) || {};
+
+      isProbablyAnAssetContract = symbol && decimals !== undefined;
+
+      if (!isProbablyAnAssetContract) {
+        try {
+          const { standard } = await getTokenStandardAndDetails(
+            userInput,
+            sendingAddress,
+          );
+          isProbablyAnAssetContract = Boolean(standard);
+        } catch (e) {
+          console.log(e);
+        }
+      }
+    }
+
+    return new Promise((resolve) => {
+      debouncedValidateRecipientUserInput(
+        dispatch,
+        {
+          userInput,
+          chainId,
+          tokens,
+          useTokenDetection,
+          tokenAddressList,
+          isProbablyAnAssetContract,
+        },
+        resolve,
+      );
+    });
+  };
+}
+
+/**
+ * Updates the amount the user intends to send and performs side effects.
+ * 1. If the current mode is MAX change to INPUT
+ * 2. If sending a token, recompute the gasLimit estimate
+ *
+ * @param {string} amount - hex string representing value
+ * @returns {ThunkAction<void>}
+ */
+export function updateSendAmount(amount) {
+  return async (dispatch, getState) => {
+    const state = getState();
+    const { metamask } = state;
+    const draftTransaction =
+      state[name].draftTransactions[state[name].currentTransactionUUID];
+    let logAmount = amount;
+    if (draftTransaction.asset.type === ASSET_TYPES.TOKEN) {
+      const multiplier = Math.pow(
+        10,
+        Number(draftTransaction.asset.details?.decimals || 0),
+      );
+      const decimalValueString = conversionUtil(addHexPrefix(amount), {
+        fromNumericBase: 'hex',
+        toNumericBase: 'dec',
+        toCurrency: draftTransaction.asset.details?.symbol,
+        conversionRate: multiplier,
+        invertConversionRate: true,
+      });
+
+      logAmount = `${Number(decimalValueString) ? decimalValueString : ''} ${
+        draftTransaction.asset.details?.symbol
+      }`;
+    } else {
+      const ethValue = getValueFromWeiHex({
+        value: amount,
+        toCurrency: ETH,
+        numberOfDecimals: 8,
+      });
+      logAmount = `${ethValue} ${metamask?.provider?.ticker || ETH}`;
+    }
+    await dispatch(
+      addHistoryEntry(`sendFlow - user set amount to ${logAmount}`),
+    );
+    await dispatch(actions.updateSendAmount(amount));
+    if (state[name].amountMode === AMOUNT_MODES.MAX) {
+      await dispatch(actions.updateAmountMode(AMOUNT_MODES.INPUT));
+    }
+    await dispatch(computeEstimatedGasLimit());
+  };
+}
+
+/**
+ * updates the asset to send to one of NATIVE or TOKEN and ensures that the
+ * asset balance is set. If sending a TOKEN also updates the asset details
+ * object with the appropriate ERC20 details including address, symbol and
+ * decimals.
+ *
+ * @param {Object} payload - action payload
+ * @param {string} payload.type - type of asset to send
+ * @param {TokenDetails} [payload.details] - ERC20 details if sending TOKEN asset
+ * @returns {ThunkAction<void>}
+ */
+export function updateSendAsset(
+  { type, details: providedDetails },
+  { skipComputeEstimatedGasLimit = false } = {},
+) {
+  return async (dispatch, getState) => {
+    const state = getState();
+    const draftTransaction =
+      state[name].draftTransactions[state[name].currentTransactionUUID];
+    const sendingAddress =
+      draftTransaction.fromAccount?.address ??
+      state[name].selectedAccount.address ??
+      getSelectedAddress(state);
+    const account = getTargetAccount(state, sendingAddress);
+    if (type === ASSET_TYPES.NATIVE) {
+      const unapprovedTxs = getUnapprovedTxs(state);
+      const unapprovedTx = unapprovedTxs?.[draftTransaction.id];
+
+      await dispatch(
+        addHistoryEntry(
+          `sendFlow - user set asset of type ${
+            ASSET_TYPES.NATIVE
+          } with symbol ${state.metamask.provider?.ticker ?? ETH}`,
+        ),
+      );
+      await dispatch(
+        actions.updateAsset({
+          type,
+          details: null,
+          balance: account.balance,
+          error: null,
+        }),
+      );
+
+      // This is meant to handle cases where we are editing an unapprovedTx from the background state
+      // and its type is a token method. In such a case, the hex data will be the necessary hex data
+      // for calling the contract transfer method.
+      // Now that we are updating the transaction to be a send of a native asset type, we should
+      // set the hex data of the transaction being editing to be empty.
+      // then the user will not want to send any hex data now that they have change the
+      if (
+        unapprovedTx?.type === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM ||
+        unapprovedTx?.type === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER ||
+        unapprovedTx?.type === TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM
+      ) {
+        await dispatch(actions.updateUserInputHexData(''));
+      }
+    } else {
+      await dispatch(showLoadingIndication());
+      const details = {
+        ...providedDetails,
+        ...(await getTokenStandardAndDetails(
+          providedDetails.address,
+          sendingAddress,
+          providedDetails.tokenId,
+        )),
+      };
+      await dispatch(hideLoadingIndication());
+      const balance = addHexPrefix(
+        calcTokenAmount(details.balance, details.decimals).toString(16),
+      );
+      const asset = {
+        type,
+        details,
+        balance,
+        error: null,
+      };
+      if (
+        details.standard === TOKEN_STANDARDS.ERC1155 &&
+        type === ASSET_TYPES.COLLECTIBLE
+      ) {
+        throw new Error('Sends of ERC1155 tokens are not currently supported');
+      } else if (
+        details.standard === TOKEN_STANDARDS.ERC1155 ||
+        details.standard === TOKEN_STANDARDS.ERC721
+      ) {
+        if (type === ASSET_TYPES.TOKEN && process.env.COLLECTIBLES_V1) {
+          dispatch(
+            showModal({
+              name: 'CONVERT_TOKEN_TO_NFT',
+              tokenAddress: details.address,
+            }),
+          );
+          asset.error = INVALID_ASSET_TYPE;
+          throw new Error(INVALID_ASSET_TYPE);
+        } else {
+          let isCurrentOwner = true;
+          try {
+            isCurrentOwner = await isCollectibleOwner(
+              sendingAddress,
+              details.address,
+              details.tokenId,
+            );
+          } catch (err) {
+            if (err.message.includes('Unable to verify ownership.')) {
+              // this would indicate that either our attempts to verify ownership failed because of network issues,
+              // or, somehow a token has been added to collectibles state with an incorrect chainId.
+            } else {
+              // Any other error is unexpected and should be surfaced.
+              dispatch(displayWarning(err.message));
+            }
+          }
+
+          if (isCurrentOwner) {
+            asset.error = null;
+            asset.balance = '0x1';
+          } else {
+            throw new Error(
+              'Send slice initialized as collectible send with a collectible not currently owned by the select account',
+            );
+          }
+          await dispatch(
+            addHistoryEntry(
+              `sendFlow - user set asset to NFT with tokenId ${details.tokenId} and address ${details.address}`,
+            ),
+          );
+        }
+      } else {
+        await dispatch(
+          addHistoryEntry(
+            `sendFlow - user set asset to ERC20 token with symbol ${details.symbol} and address ${details.address}`,
+          ),
+        );
+        // do nothing extra.
+      }
+
+      await dispatch(actions.updateAsset(asset));
+    }
+    if (skipComputeEstimatedGasLimit === false) {
+      await dispatch(computeEstimatedGasLimit());
+    }
   };
 }
 
@@ -1933,39 +2092,87 @@ export function resetRecipientInput() {
  * recipient and value, NOT what the user has supplied.
  *
  * @param {string} hexData - hex encoded string representing transaction data.
+ * @returns {ThunkAction<void>}
  */
 export function updateSendHexData(hexData) {
   return async (dispatch, getState) => {
     await dispatch(
       addHistoryEntry(`sendFlow - user added custom hexData ${hexData}`),
     );
+
     await dispatch(actions.updateUserInputHexData(hexData));
     const state = getState();
-    if (state.send.asset.type === ASSET_TYPES.NATIVE) {
+    const draftTransaction =
+      state[name].draftTransactions[state[name].currentTransactionUUID];
+    if (draftTransaction.asset.type === ASSET_TYPES.NATIVE) {
       await dispatch(computeEstimatedGasLimit());
     }
   };
 }
 
 /**
- * Toggles the amount.mode between INPUT and MAX modes.
- * As a result, the amount.value will change to either '0x0' when moving from
- * MAX to INPUT, or to the maximum allowable amount based on current asset when
- * moving from INPUT to MAX.
+ * Sets the recipient search mode to show a list of the user's contacts and
+ * recently interacted with addresses.
+ *
+ * @returns {ThunkAction<void>}
  */
-export function toggleSendMaxMode() {
+export function useContactListForRecipientSearch() {
+  return (dispatch) => {
+    dispatch(
+      addHistoryEntry(
+        `sendFlow - user selected back to all on recipient screen`,
+      ),
+    );
+    dispatch(updateRecipientSearchMode(RECIPIENT_SEARCH_MODES.CONTACT_LIST));
+  };
+}
+
+/**
+ * Sets the recipient search mode to show a list of the user's own accounts.
+ *
+ * @returns {ThunkAction<void>}
+ */
+export function useMyAccountsForRecipientSearch() {
+  return (dispatch) => {
+    dispatch(
+      addHistoryEntry(
+        `sendFlow - user selected transfer to my accounts on recipient screen`,
+      ),
+    );
+    dispatch(updateRecipientSearchMode(RECIPIENT_SEARCH_MODES.MY_ACCOUNTS));
+  };
+}
+
+/**
+ * Clears out the recipient user input, ENS resolution and recipient validation.
+ *
+ * @returns {ThunkAction<void>}
+ */
+export function resetRecipientInput() {
+  return async (dispatch) => {
+    await dispatch(addHistoryEntry(`sendFlow - user cleared recipient input`));
+    await dispatch(updateRecipientUserInput(''));
+    await dispatch(updateRecipient({ address: '', nickname: '' }));
+    await dispatch(resetEnsResolution());
+    await dispatch(validateRecipientUserInput());
+  };
+}
+
+/**
+ * Resets the entire send state tree to the initial state. It also disconnects
+ * polling from the gas controller if the token is present in state.
+ *
+ * @returns {ThunkAction<void>}
+ */
+export function resetSendState() {
   return async (dispatch, getState) => {
     const state = getState();
-    if (state.send.amount.mode === AMOUNT_MODES.MAX) {
-      await dispatch(actions.updateAmountMode(AMOUNT_MODES.INPUT));
-      await dispatch(actions.updateSendAmount('0x0'));
-      await dispatch(addHistoryEntry(`sendFlow - user toggled max mode off`));
-    } else {
-      await dispatch(actions.updateAmountMode(AMOUNT_MODES.MAX));
-      await dispatch(actions.updateAmountToMax());
-      await dispatch(addHistoryEntry(`sendFlow - user toggled max mode on`));
+    dispatch(actions.resetSendState());
+
+    if (state[name].gasEstimatePollToken) {
+      await disconnectGasFeeEstimatePoller(state[name].gasEstimatePollToken);
+      removePollingTokenFromAppState(state[name].gasEstimatePollToken);
     }
-    await dispatch(computeEstimatedGasLimit());
   };
 }
 
@@ -1976,12 +2183,16 @@ export function toggleSendMaxMode() {
  * will create the transaction in state (by way of the various global provider
  * constructs) which will eventually (and fairly quickly from user perspective)
  * result in a confirmation window being displayed for the transaction.
+ *
+ * @returns {ThunkAction<void>}
  */
 export function signTransaction() {
   return async (dispatch, getState) => {
     const state = getState();
-    const { id, asset, stage, eip1559support } = state[name];
+    const { stage, eip1559support } = state[name];
     const txParams = generateTransactionParams(state[name]);
+    const draftTransaction =
+      state[name].draftTransactions[state[name].currentTransactionUUID];
     if (stage === SEND_STAGES.EDIT) {
       // When dealing with the edit flow there is already a transaction in
       // state that we must update, this branch is responsible for that logic.
@@ -1989,7 +2200,7 @@ export function signTransaction() {
       // merge in the modified txParams. Once the transaction has been modified
       // we can send that to the background to update the transaction in state.
       const unapprovedTxs = getUnapprovedTxs(state);
-      const unapprovedTx = unapprovedTxs[id];
+      const unapprovedTx = unapprovedTxs[draftTransaction.id];
       // We only update the tx params that can be changed via the edit flow UX
       const eip1559OnlyTxParamsToUpdate = {
         data: txParams.data,
@@ -2014,15 +2225,24 @@ export function signTransaction() {
           `sendFlow - user clicked next and transaction should be updated in controller`,
         ),
       );
-      await dispatch(updateTransactionSendFlowHistory(id, state[name].history));
-      dispatch(updateEditableParams(id, editingTx.txParams));
-      dispatch(updateTransactionGasFees(id, editingTx.txParams));
+      await dispatch(
+        updateTransactionSendFlowHistory(
+          draftTransaction.id,
+          draftTransaction.history,
+        ),
+      );
+      await dispatch(
+        updateEditableParams(draftTransaction.id, editingTx.txParams),
+      );
+      await dispatch(
+        updateTransactionGasFees(draftTransaction.id, editingTx.txParams),
+      );
     } else {
       let transactionType = TRANSACTION_TYPES.SIMPLE_SEND;
 
-      if (asset.type !== ASSET_TYPES.NATIVE) {
+      if (draftTransaction.asset.type !== ASSET_TYPES.NATIVE) {
         transactionType =
-          asset.type === ASSET_TYPES.COLLECTIBLE
+          draftTransaction.asset.type === ASSET_TYPES.COLLECTIBLE
             ? TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM
             : TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER;
       }
@@ -2036,155 +2256,168 @@ export function signTransaction() {
         addUnapprovedTransactionAndRouteToConfirmationPage(
           txParams,
           transactionType,
-          state[name].history,
+          draftTransaction.history,
         ),
       );
     }
   };
 }
 
-export function editTransaction(
-  assetType,
-  transactionId,
-  tokenData,
-  assetDetails,
-) {
+/**
+ * Toggles the amount.mode between INPUT and MAX modes.
+ * As a result, the amount.value will change to either '0x0' when moving from
+ * MAX to INPUT, or to the maximum allowable amount based on current asset when
+ * moving from INPUT to MAX.
+ *
+ * @returns {ThunkAction<void>}
+ */
+export function toggleSendMaxMode() {
   return async (dispatch, getState) => {
     const state = getState();
-    await dispatch(
-      addHistoryEntry(
-        `sendFlow - user clicked edit on transaction with id ${transactionId}`,
-      ),
-    );
-    const unapprovedTransactions = getUnapprovedTxs(state);
-    const transaction = unapprovedTransactions[transactionId];
-    const { txParams } = transaction;
-    if (assetType === ASSET_TYPES.NATIVE) {
-      const {
-        data,
-        from,
-        gas: gasLimit,
-        gasPrice,
-        to: address,
-        value: amount,
-      } = txParams;
-      const nickname = getAddressBookEntry(state, address)?.name ?? '';
-      await dispatch(
-        actions.editTransaction({
-          data,
-          id: transactionId,
-          gasLimit,
-          gasPrice,
-          from,
-          amount,
-          address,
-          nickname,
-        }),
-      );
-    } else if (!tokenData || !assetDetails) {
-      throw new Error(
-        `send/editTransaction dispatched with assetType 'TOKEN' but missing assetData or assetDetails parameter`,
-      );
-    } else if (assetType === ASSET_TYPES.TOKEN) {
-      const {
-        data,
-        from,
-        to: tokenAddress,
-        gas: gasLimit,
-        gasPrice,
-      } = txParams;
-      const tokenAmountInDec = getTokenValueParam(tokenData);
-      const address = getTokenAddressParam(tokenData);
-      const nickname = getAddressBookEntry(state, address)?.name ?? '';
-
-      const tokenAmountInHex = addHexPrefix(
-        conversionUtil(tokenAmountInDec, {
-          fromNumericBase: 'dec',
-          toNumericBase: 'hex',
-        }),
-      );
-
-      await dispatch(
-        updateSendAsset({
-          type: ASSET_TYPES.TOKEN,
-          details: { ...assetDetails, address: tokenAddress },
-        }),
-      );
-
-      await dispatch(
-        actions.editTransaction({
-          data,
-          id: transactionId,
-          gasLimit,
-          gasPrice,
-          from,
-          amount: tokenAmountInHex,
-          address,
-          nickname,
-        }),
-      );
-    } else if (assetType === ASSET_TYPES.COLLECTIBLE) {
-      const {
-        data,
-        from,
-        to: tokenAddress,
-        gas: gasLimit,
-        gasPrice,
-      } = txParams;
-      const address = getTokenAddressParam(tokenData);
-      const nickname = getAddressBookEntry(state, address)?.name ?? '';
-
-      await dispatch(
-        updateSendAsset({
-          type: ASSET_TYPES.COLLECTIBLE,
-          details: { ...assetDetails, address: tokenAddress },
-        }),
-      );
-
-      await dispatch(
-        actions.editTransaction({
-          data,
-          id: transactionId,
-          gasLimit,
-          gasPrice,
-          from,
-          amount: '0x1',
-          address,
-          nickname,
-        }),
-      );
+    if (state[name].amountMode === AMOUNT_MODES.MAX) {
+      await dispatch(actions.updateAmountMode(AMOUNT_MODES.INPUT));
+      await dispatch(actions.updateSendAmount('0x0'));
+      await dispatch(addHistoryEntry(`sendFlow - user toggled max mode off`));
+    } else {
+      await dispatch(actions.updateAmountMode(AMOUNT_MODES.MAX));
+      await dispatch(actions.updateAmountToMax());
+      await dispatch(addHistoryEntry(`sendFlow - user toggled max mode on`));
     }
+    await dispatch(computeEstimatedGasLimit());
+  };
+}
+
+/**
+ * Begins a new draft transaction, clearing out the previous draft transactions
+ * from state, and clearing the currentTransactionUUID. This action is one of
+ * the two entry points into the send flow. NOTE: You must route to the send
+ * page *after* dispatching this action resolves to ensure that the
+ * draftTransaction is properly created.
+ *
+ * @param {Pick<Asset, 'type' | 'details'>} asset - A partial asset
+ *  object containing at least the asset type. If specifying a non-native asset
+ *  then the asset details must be included with at least the address.
+ * @returns {ThunkAction<void>}
+ */
+export function startNewDraftTransaction(asset) {
+  return async (dispatch) => {
+    await dispatch(actions.clearPreviousDrafts());
+
+    await dispatch(
+      actions.addNewDraft({
+        ...draftTransactionInitialState,
+        history: [`sendFlow - User started new draft transaction`],
+      }),
+    );
+
+    await dispatch(
+      updateSendAsset({
+        type: asset.type ?? ASSET_TYPES.NATIVE,
+        details: asset.details,
+      }),
+    );
+
+    await dispatch(initializeSendState());
   };
 }
 
 // Selectors
+/**
+ * The following typedef is a shortcut for typing selectors below. It uses a
+ * generic type, T, so that each selector can specify it's return type.
+ *
+ * @template T
+ * @typedef {(state: MetaMaskState) => T} Selector
+ */
+
+/**
+ * Selector that returns the current draft transaction's UUID.
+ *
+ * @type {Selector<string>}
+ */
+export function getCurrentTransactionUUID(state) {
+  return state[name].currentTransactionUUID;
+}
+
+/**
+ * Selector that returns the current draft transaction.
+ *
+ * @type {Selector<DraftTransaction>}
+ */
+export function getCurrentDraftTransaction(state) {
+  return state[name].draftTransactions[getCurrentTransactionUUID(state)] ?? {};
+}
+
+/**
+ * Selector that returns true if a draft transaction exists.
+ *
+ * @type {Selector<boolean>}
+ */
+export function getDraftTransactionExists(state) {
+  const draftTransaction = getCurrentDraftTransaction(state);
+  if (Object.keys(draftTransaction).length === 0) {
+    return false;
+  }
+  return true;
+}
 
 // Gas selectors
+
+/**
+ * Selector that returns the current draft transaction's gasLimit.
+ *
+ * @type {Selector<?string>}
+ */
 export function getGasLimit(state) {
-  return state[name].gas.gasLimit;
+  return getCurrentDraftTransaction(state).gas?.gasLimit;
 }
 
+/**
+ * Selector that returns the current draft transaction's gasPrice.
+ *
+ * @type {Selector<?string>}
+ */
 export function getGasPrice(state) {
-  return state[name].gas.gasPrice;
+  return getCurrentDraftTransaction(state).gas?.gasPrice;
 }
 
+/**
+ * Selector that returns the current draft transaction's gasTotal.
+ *
+ * @type {Selector<?string>}
+ */
 export function getGasTotal(state) {
-  return state[name].gas.gasTotal;
+  return getCurrentDraftTransaction(state).gas?.gasTotal;
 }
 
+/**
+ * Selector that returns the error, if present, for the gas fields.
+ *
+ * @type {Selector<?string>}
+ */
 export function gasFeeIsInError(state) {
-  return Boolean(state[name].gas.error);
+  return Boolean(getCurrentDraftTransaction(state).gas?.error);
 }
 
+/**
+ * Selector that returns the minimum gasLimit for the current network.
+ *
+ * @type {Selector<string>}
+ */
 export function getMinimumGasLimitForSend(state) {
-  return state[name].gas.minimumGasLimit;
+  return state[name].gasLimitMinimum;
 }
 
+/**
+ * Selector that returns the current draft transaction's gasLimit.
+ *
+ * @type {Selector<MapValuesToUnion<SendStateGasModes>>}
+ */
 export function getGasInputMode(state) {
   const isMainnet = getIsMainnet(state);
   const gasEstimateType = getGasEstimateType(state);
   const showAdvancedGasFields = getAdvancedInlineGasShown(state);
-  if (state[name].gas.isCustomGasSet) {
+  if (state[name].gasIsSetInModal) {
     return GAS_INPUT_MODES.CUSTOM;
   }
   if ((!isMainnet && !process.env.IN_TEST) || showAdvancedGasFields) {
@@ -2204,95 +2437,207 @@ export function getGasInputMode(state) {
 }
 
 // Asset Selectors
+/**
+ * Selector that returns the asset the current draft transaction is sending.
+ *
+ * @type {Selector<?Asset>}
+ */
 export function getSendAsset(state) {
-  return state[name].asset;
+  return getCurrentDraftTransaction(state).asset;
 }
 
+/**
+ * Selector that returns the contract address of the non-native asset that
+ * the current transaction is sending, if it exists.
+ *
+ * @type {Selector<?string>}
+ */
 export function getSendAssetAddress(state) {
   return getSendAsset(state)?.details?.address;
 }
 
+/**
+ * Selector that returns a boolean value describing whether the currently
+ * selected asset is sendable, based upon the standard of the token.
+ *
+ * @type {Selector<boolean>}
+ */
 export function getIsAssetSendable(state) {
-  if (state[name].asset.type === ASSET_TYPES.NATIVE) {
+  if (getSendAsset(state)?.type === ASSET_TYPES.NATIVE) {
     return true;
   }
-  return state[name].asset.details.isERC721 === false;
+  return getSendAsset(state)?.details?.isERC721 === false;
 }
 
+/**
+ * Selector that returns the asset error if it exists.
+ *
+ * @type {Selector<?string>}
+ */
 export function getAssetError(state) {
-  return state[name].asset.error;
+  return getSendAsset(state).error;
 }
 
 // Amount Selectors
+/**
+ * Selector that returns the amount that current draft transaction is sending.
+ *
+ * @type {Selector<?string>}
+ */
 export function getSendAmount(state) {
-  return state[name].amount.value;
+  return getCurrentDraftTransaction(state).amount?.value;
 }
 
+/**
+ * Selector that returns true if the user has enough native asset balance to
+ * cover the cost of the transaction.
+ *
+ * @type {Selector<boolean>}
+ */
 export function getIsBalanceInsufficient(state) {
-  return state[name].gas.error === INSUFFICIENT_FUNDS_ERROR;
+  return (
+    getCurrentDraftTransaction(state).gas?.error === INSUFFICIENT_FUNDS_ERROR
+  );
 }
+
+/**
+ * Selector that returns the amoung send mode, either MAX or INPUT.
+ *
+ * @type {Selector<boolean>}
+ */
 export function getSendMaxModeState(state) {
-  return state[name].amount.mode === AMOUNT_MODES.MAX;
+  return state[name].amountMode === AMOUNT_MODES.MAX;
 }
 
+/**
+ * Selector that returns the current draft transaction's data field.
+ *
+ * @type {Selector<?string>}
+ */
 export function getSendHexData(state) {
-  return state[name].userInputHexData;
+  return getCurrentDraftTransaction(state).userInputHexData;
 }
 
+/**
+ * Selector that returns the current draft transaction's id, if present.
+ *
+ * @type {Selector<?string>}
+ */
 export function getDraftTransactionID(state) {
-  return state[name].id;
+  return getCurrentDraftTransaction(state).id;
 }
 
+/**
+ * Selector that returns true if there is an error on the amount field.
+ *
+ * @type {Selector<boolean>}
+ */
 export function sendAmountIsInError(state) {
-  return Boolean(state[name].amount.error);
+  return Boolean(getCurrentDraftTransaction(state).amount?.error);
 }
 
 // Recipient Selectors
-
-export function getSendTo(state) {
-  return state[name].recipient.address;
-}
-
-export function getIsUsingMyAccountForRecipientSearch(state) {
-  return state[name].recipient.mode === RECIPIENT_SEARCH_MODES.MY_ACCOUNTS;
-}
-
-export function getRecipientUserInput(state) {
-  return state[name].recipient.userInput;
-}
-
+/**
+ * Selector that returns the current draft transaction's recipient.
+ *
+ * @type {Selector<DraftTransaction['recipient']>}
+ */
 export function getRecipient(state) {
-  const checksummedAddress = toChecksumHexAddress(
-    state[name].recipient.address,
-  );
+  const draft = getCurrentDraftTransaction(state);
+  if (!draft.recipient) {
+    return {
+      address: '',
+      nickname: '',
+      error: null,
+      warning: null,
+    };
+  }
+  const checksummedAddress = toChecksumHexAddress(draft.recipient.address);
   if (state.metamask.ensResolutionsByAddress) {
     return {
-      ...state[name].recipient,
+      ...draft.recipient,
       nickname:
-        state[name].recipient.nickname ||
+        draft.recipient.nickname ||
         getEnsResolutionByAddress(state, checksummedAddress),
     };
   }
-  return state[name].recipient;
+  return draft.recipient;
+}
+
+/**
+ * Selector that returns the addres of the current draft transaction's
+ * recipient.
+ *
+ * @type {Selector<?string>}
+ */
+export function getSendTo(state) {
+  return getRecipient(state)?.address;
+}
+
+/**
+ * Selector that returns true if the current recipientMode is MY_ACCOUNTS
+ *
+ * @type {Selector<boolean>}
+ */
+export function getIsUsingMyAccountForRecipientSearch(state) {
+  return state[name].recipientMode === RECIPIENT_SEARCH_MODES.MY_ACCOUNTS;
+}
+
+/**
+ * Selector that returns the value that the user has typed into the recipient
+ * input field.
+ *
+ * @type {Selector<?string>}
+ */
+export function getRecipientUserInput(state) {
+  return state[name].recipientInput;
+}
+
+export function getRecipientWarningAcknowledgement(state) {
+  return (
+    getCurrentDraftTransaction(state).recipient?.recipientWarningAcknowledged ??
+    false
+  );
 }
 
 // Overall validity and stage selectors
 
+/**
+ * Selector that returns the gasFee and amount errors, if they exist.
+ *
+ * @type {Selector<{ gasFee?: string, amount?: string}>}
+ */
 export function getSendErrors(state) {
   return {
-    gasFee: state.send.gas.error,
-    amount: state.send.amount.error,
+    gasFee: getCurrentDraftTransaction(state).gas?.error,
+    amount: getCurrentDraftTransaction(state).amount?.error,
   };
 }
 
+/**
+ * Selector that returns true if the stage is anything except INACTIVE
+ *
+ * @type {Selector<boolean>}
+ */
 export function isSendStateInitialized(state) {
   return state[name].stage !== SEND_STAGES.INACTIVE;
 }
 
+/**
+ * Selector that returns true if the current draft transaction is valid and in
+ * a sendable state.
+ *
+ * @type {Selector<boolean>}
+ */
 export function isSendFormInvalid(state) {
-  return state[name].status === SEND_STATUSES.INVALID;
+  return getCurrentDraftTransaction(state).status === SEND_STATUSES.INVALID;
 }
 
+/**
+ * Selector that returns the current stage of the send flow
+ *
+ * @type {Selector<MapValuesToUnion<SendStateStages>>}
+ */
 export function getSendStage(state) {
   return state[name].stage;
 }
diff --git a/ui/ducks/send/send.test.js b/ui/ducks/send/send.test.js
index 707460980..339770198 100644
--- a/ui/ducks/send/send.test.js
+++ b/ui/ducks/send/send.test.js
@@ -18,10 +18,19 @@ import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../../shared/constants/gas';
 import {
   ASSET_TYPES,
   TRANSACTION_ENVELOPE_TYPES,
-  TRANSACTION_TYPES,
 } from '../../../shared/constants/transaction';
 import * as Actions from '../../store/actions';
 import { setBackgroundConnection } from '../../../test/jest';
+import {
+  generateERC20TransferData,
+  generateERC721TransferData,
+} from '../../pages/send/send.utils';
+import { BURN_ADDRESS } from '../../../shared/modules/hexstring-utils';
+import { TOKEN_STANDARDS } from '../../helpers/constants/common';
+import {
+  getInitialSendStateWithExistingTxState,
+  INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+} from '../../../test/jest/mocks';
 import sendReducer, {
   initialState,
   initializeSendState,
@@ -39,7 +48,6 @@ import sendReducer, {
   SEND_STAGES,
   AMOUNT_MODES,
   RECIPIENT_SEARCH_MODES,
-  editTransaction,
   getGasLimit,
   getGasPrice,
   getGasTotal,
@@ -66,6 +74,7 @@ import sendReducer, {
   getSendStage,
   updateGasPrice,
 } from './send';
+import { draftTransactionInitialState, editExistingTransaction } from '.';
 
 const mockStore = createMockStore([thunk]);
 
@@ -78,6 +87,11 @@ jest.mock('./send', () => {
   };
 });
 
+jest.mock('lodash', () => ({
+  ...jest.requireActual('lodash'),
+  debounce: (fn) => fn,
+}));
+
 setBackgroundConnection({
   addPollingTokenToAppState: jest.fn(),
   addUnapprovedTransaction: jest.fn((_w, _x, _y, _z, cb) => {
@@ -86,6 +100,8 @@ setBackgroundConnection({
   updateTransactionSendFlowHistory: jest.fn((_x, _y, cb) => cb(null)),
 });
 
+const getTestUUIDTx = (state) => state.draftTransactions['test-uuid'];
+
 describe('Send Slice', () => {
   let getTokenStandardAndDetailsStub;
   let addUnapprovedTransactionAndRouteToConfirmationPageStub;
@@ -93,7 +109,14 @@ describe('Send Slice', () => {
     jest.useFakeTimers();
     getTokenStandardAndDetailsStub = jest
       .spyOn(Actions, 'getTokenStandardAndDetails')
-      .mockImplementation(() => Promise.resolve({ standard: 'ERC20' }));
+      .mockImplementation(() =>
+        Promise.resolve({
+          standard: 'ERC20',
+          balance: '0x0',
+          symbol: 'SYMB',
+          decimals: 18,
+        }),
+      );
     addUnapprovedTransactionAndRouteToConfirmationPageStub = jest.spyOn(
       Actions,
       'addUnapprovedTransactionAndRouteToConfirmationPage',
@@ -119,11 +142,130 @@ describe('Send Slice', () => {
   });
 
   describe('Reducers', () => {
+    describe('addNewDraft', () => {
+      it('should add new draft transaction and set currentTransactionUUID', () => {
+        const action = {
+          type: 'send/addNewDraft',
+          payload: { ...draftTransactionInitialState, id: 4 },
+        };
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
+        expect(result.currentTransactionUUID).not.toStrictEqual('test-uuid');
+        const uuid = result.currentTransactionUUID;
+        const draft = result.draftTransactions[uuid];
+        expect(draft.id).toStrictEqual(4);
+      });
+    });
+    describe('addHistoryEntry', () => {
+      it('should append a history item to the current draft transaction, including timestamp', () => {
+        const action = {
+          type: 'send/addHistoryEntry',
+          payload: 'test entry',
+        };
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
+        expect(result.currentTransactionUUID).toStrictEqual('test-uuid');
+        const draft = getTestUUIDTx(result);
+        const latestHistory = draft.history[draft.history.length - 1];
+        expect(latestHistory.timestamp).toBeDefined();
+        expect(latestHistory.entry).toStrictEqual('test entry');
+      });
+    });
+    describe('calculateGasTotal', () => {
+      it('should set gasTotal to maxFeePerGax * gasLimit for FEE_MARKET transaction', () => {
+        const action = {
+          type: 'send/calculateGasTotal',
+        };
+        const result = sendReducer(
+          getInitialSendStateWithExistingTxState({
+            gas: {
+              gasPrice: '0x1',
+              maxFeePerGas: '0x2',
+              gasLimit: GAS_LIMITS.SIMPLE,
+            },
+            transactionType: TRANSACTION_ENVELOPE_TYPES.FEE_MARKET,
+          }),
+          action,
+        );
+        expect(result.currentTransactionUUID).toStrictEqual('test-uuid');
+        const draft = getTestUUIDTx(result);
+        expect(draft.gas.gasTotal).toStrictEqual(`0xa410`);
+      });
+
+      it('should set gasTotal to gasPrice * gasLimit for non FEE_MARKET transaction', () => {
+        const action = {
+          type: 'send/calculateGasTotal',
+        };
+        const result = sendReducer(
+          getInitialSendStateWithExistingTxState({
+            gas: {
+              gasPrice: '0x1',
+              maxFeePerGas: '0x2',
+              gasLimit: GAS_LIMITS.SIMPLE,
+            },
+          }),
+          action,
+        );
+        expect(result.currentTransactionUUID).toStrictEqual('test-uuid');
+        const draft = getTestUUIDTx(result);
+        expect(draft.gas.gasTotal).toStrictEqual(GAS_LIMITS.SIMPLE);
+      });
+
+      it('should call updateAmountToMax if amount mode is max', () => {
+        const action = {
+          type: 'send/calculateGasTotal',
+        };
+        const result = sendReducer(
+          {
+            ...getInitialSendStateWithExistingTxState({
+              asset: { balance: '0xffff' },
+              gas: {
+                gasPrice: '0x1',
+                gasLimit: GAS_LIMITS.SIMPLE,
+              },
+              recipient: {
+                address: '0x00',
+              },
+            }),
+            selectedAccount: {
+              balance: '0xffff',
+              address: '0x00',
+            },
+            gasEstimateIsLoading: false,
+            amountMode: AMOUNT_MODES.MAX,
+            stage: SEND_STAGES.DRAFT,
+          },
+          action,
+        );
+        expect(result.currentTransactionUUID).toStrictEqual('test-uuid');
+        const draft = getTestUUIDTx(result);
+        expect(draft.amount.value).toStrictEqual('0xadf7');
+        expect(draft.status).toStrictEqual(SEND_STATUSES.VALID);
+      });
+    });
+    describe('resetSendState', () => {
+      it('should set the state back to a blank slate matching the initialState object', () => {
+        const action = {
+          type: 'send/resetSendState',
+        };
+
+        const result = sendReducer({}, action);
+
+        expect(result).toStrictEqual(initialState);
+      });
+    });
     describe('updateSendAmount', () => {
       it('should', async () => {
         const action = { type: 'send/updateSendAmount', payload: '0x1' };
-        const result = sendReducer(initialState, action);
-        expect(result.amount.value).toStrictEqual('0x1');
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
+        expect(getTestUUIDTx(result).amount.value).toStrictEqual('0x1');
       });
     });
 
@@ -137,17 +279,19 @@ describe('Send Slice', () => {
             balance: '0x56bc75e2d63100000', // 100000000000000000000
           },
           gas: {
-            gasLimit: '0x5208', // 21000
+            gasLimit: GAS_LIMITS.SIMPLE, // 21000
             gasTotal: '0x1319718a5000', // 21000000000000
-            minimumGasLimit: '0x5208',
+            minimumGasLimit: GAS_LIMITS.SIMPLE,
           },
         };
 
-        const state = { ...initialState, ...maxAmountState };
+        const state = getInitialSendStateWithExistingTxState(maxAmountState);
         const action = { type: 'send/updateAmountToMax' };
         const result = sendReducer(state, action);
 
-        expect(result.amount.value).toStrictEqual('0x56bc74b13f185b000'); // 99999979000000000000
+        expect(getTestUUIDTx(result).amount.value).toStrictEqual(
+          '0x56bc74b13f185b000',
+        ); // 99999979000000000000
       });
     });
 
@@ -161,17 +305,22 @@ describe('Send Slice', () => {
             maxPriorityFeePerGas: '0x1',
           },
         };
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.gas.maxFeePerGas).toStrictEqual(
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.gas.maxFeePerGas).toStrictEqual(
           action.payload.maxFeePerGas,
         );
 
-        expect(result.gas.maxPriorityFeePerGas).toStrictEqual(
+        expect(draftTransaction.gas.maxPriorityFeePerGas).toStrictEqual(
           action.payload.maxPriorityFeePerGas,
         );
 
-        expect(result.transactionType).toBe(
+        expect(draftTransaction.transactionType).toBe(
           TRANSACTION_ENVELOPE_TYPES.FEE_MARKET,
         );
       });
@@ -184,10 +333,19 @@ describe('Send Slice', () => {
             gasPrice: '0x1',
           },
         };
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.gas.gasPrice).toStrictEqual(action.payload.gasPrice);
-        expect(result.transactionType).toBe(TRANSACTION_ENVELOPE_TYPES.LEGACY);
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.gas.gasPrice).toStrictEqual(
+          action.payload.gasPrice,
+        );
+        expect(draftTransaction.transactionType).toBe(
+          TRANSACTION_ENVELOPE_TYPES.LEGACY,
+        );
       });
     });
 
@@ -197,54 +355,59 @@ describe('Send Slice', () => {
           type: 'send/updateUserInputHexData',
           payload: 'TestData',
         };
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
+        const draftTransaction = getTestUUIDTx(result);
 
-        expect(result.userInputHexData).toStrictEqual(action.payload);
+        expect(draftTransaction.userInputHexData).toStrictEqual(action.payload);
       });
     });
 
     describe('updateGasLimit', () => {
       const action = {
         type: 'send/updateGasLimit',
-        payload: '0x5208', // 21000
+        payload: GAS_LIMITS.SIMPLE, // 21000
       };
 
       it('should', () => {
         const result = sendReducer(
           {
-            ...initialState,
+            ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
             stage: SEND_STAGES.DRAFT,
-            gas: { ...initialState.gas, isGasEstimateLoading: false },
+            gasEstimateIsLoading: false,
           },
           action,
         );
 
-        expect(result.gas.gasLimit).toStrictEqual(action.payload);
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.gas.gasLimit).toStrictEqual(action.payload);
       });
 
       it('should recalculate gasTotal', () => {
-        const gasState = {
-          ...initialState,
+        const gasState = getInitialSendStateWithExistingTxState({
           gas: {
             gasLimit: '0x0',
             gasPrice: '0x3b9aca00', // 1000000000
           },
-        };
+        });
 
         const result = sendReducer(gasState, action);
 
-        expect(result.gas.gasLimit).toStrictEqual(action.payload);
-        expect(result.gas.gasPrice).toStrictEqual(gasState.gas.gasPrice);
-        expect(result.gas.gasTotal).toStrictEqual('0x1319718a5000'); // 21000000000000
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.gas.gasLimit).toStrictEqual(action.payload);
+        expect(draftTransaction.gas.gasPrice).toStrictEqual('0x3b9aca00');
+        expect(draftTransaction.gas.gasTotal).toStrictEqual('0x1319718a5000'); // 21000000000000
       });
     });
 
     describe('updateAmountMode', () => {
       it('should change to INPUT amount mode', () => {
         const emptyAmountModeState = {
-          amount: {
-            mode: '',
-          },
+          amountMode: '',
         };
 
         const action = {
@@ -253,7 +416,7 @@ describe('Send Slice', () => {
         };
         const result = sendReducer(emptyAmountModeState, action);
 
-        expect(result.amount.mode).toStrictEqual(action.payload);
+        expect(result.amountMode).toStrictEqual(action.payload);
       });
 
       it('should change to MAX amount mode', () => {
@@ -261,9 +424,12 @@ describe('Send Slice', () => {
           type: 'send/updateAmountMode',
           payload: AMOUNT_MODES.MAX,
         };
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.amount.mode).toStrictEqual(action.payload);
+        expect(result.amountMode).toStrictEqual(action.payload);
       });
 
       it('should', () => {
@@ -271,21 +437,23 @@ describe('Send Slice', () => {
           type: 'send/updateAmountMode',
           payload: 'RANDOM',
         };
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.amount.mode).not.toStrictEqual(action.payload);
+        expect(result.amountMode).not.toStrictEqual(action.payload);
       });
     });
 
     describe('updateAsset', () => {
       it('should update asset type and balance from respective action payload', () => {
-        const updateAssetState = {
-          ...initialState,
+        const updateAssetState = getInitialSendStateWithExistingTxState({
           asset: {
             type: 'old type',
             balance: 'old balance',
           },
-        };
+        });
 
         const action = {
           type: 'send/updateAsset',
@@ -297,20 +465,23 @@ describe('Send Slice', () => {
 
         const result = sendReducer(updateAssetState, action);
 
-        expect(result.asset.type).toStrictEqual(action.payload.type);
-        expect(result.asset.balance).toStrictEqual(action.payload.balance);
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.asset.type).toStrictEqual(action.payload.type);
+        expect(draftTransaction.asset.balance).toStrictEqual(
+          action.payload.balance,
+        );
       });
 
       it('should nullify old contract address error when asset types is not TOKEN', () => {
-        const recipientErrorState = {
-          ...initialState,
+        const recipientErrorState = getInitialSendStateWithExistingTxState({
           recipient: {
             error: CONTRACT_ADDRESS_ERROR,
           },
           asset: {
             type: ASSET_TYPES.TOKEN,
           },
-        };
+        });
 
         const action = {
           type: 'send/updateAsset',
@@ -321,36 +492,12 @@ describe('Send Slice', () => {
 
         const result = sendReducer(recipientErrorState, action);
 
-        expect(result.recipient.error).not.toStrictEqual(
-          recipientErrorState.recipient.error,
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.error).not.toStrictEqual(
+          CONTRACT_ADDRESS_ERROR,
         );
-        expect(result.recipient.error).toBeNull();
-      });
-
-      it('should nullify old known address error when asset types is not TOKEN', () => {
-        const recipientErrorState = {
-          ...initialState,
-          recipient: {
-            warning: KNOWN_RECIPIENT_ADDRESS_WARNING,
-          },
-          asset: {
-            type: ASSET_TYPES.TOKEN,
-          },
-        };
-
-        const action = {
-          type: 'send/updateAsset',
-          payload: {
-            type: 'New Type',
-          },
-        };
-
-        const result = sendReducer(recipientErrorState, action);
-
-        expect(result.recipient.warning).not.toStrictEqual(
-          recipientErrorState.recipient.warning,
-        );
-        expect(result.recipient.warning).toBeNull();
+        expect(draftTransaction.recipient.error).toBeNull();
       });
 
       it('should update asset type and details to TOKEN payload', () => {
@@ -366,9 +513,17 @@ describe('Send Slice', () => {
           },
         };
 
-        const result = sendReducer(initialState, action);
-        expect(result.asset.type).toStrictEqual(action.payload.type);
-        expect(result.asset.details).toStrictEqual(action.payload.details);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.asset.type).toStrictEqual(action.payload.type);
+        expect(draftTransaction.asset.details).toStrictEqual(
+          action.payload.details,
+        );
       });
     });
 
@@ -381,10 +536,17 @@ describe('Send Slice', () => {
           },
         };
 
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
+
+        const draftTransaction = getTestUUIDTx(result);
 
         expect(result.stage).toStrictEqual(SEND_STAGES.DRAFT);
-        expect(result.recipient.address).toStrictEqual(action.payload.address);
+        expect(draftTransaction.recipient.address).toStrictEqual(
+          action.payload.address,
+        );
       });
     });
 
@@ -394,9 +556,12 @@ describe('Send Slice', () => {
           type: 'send/useDefaultGas',
         };
 
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.gas.isCustomGasSet).toStrictEqual(false);
+        expect(result.gasIsSetInModal).toStrictEqual(false);
       });
     });
 
@@ -406,9 +571,12 @@ describe('Send Slice', () => {
           type: 'send/useCustomGas',
         };
 
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.gas.isCustomGasSet).toStrictEqual(true);
+        expect(result.gasIsSetInModal).toStrictEqual(true);
       });
     });
 
@@ -419,21 +587,32 @@ describe('Send Slice', () => {
           payload: 'user input',
         };
 
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.recipient.userInput).toStrictEqual(action.payload);
+        expect(result.recipientInput).toStrictEqual(action.payload);
       });
     });
 
     describe('validateRecipientUserInput', () => {
       it('should set recipient error and warning to null when user input is', () => {
         const noUserInputState = {
-          recipient: {
-            mode: RECIPIENT_SEARCH_MODES.MY_ACCOUNTS,
-            userInput: '',
-            error: 'someError',
-            warning: 'someWarning',
-          },
+          ...getInitialSendStateWithExistingTxState({
+            recipient: {
+              error: 'someError',
+              warning: 'someWarning',
+            },
+            amount: {},
+            gas: {
+              gasLimit: '0x0',
+              minimumGasLimit: '0x0',
+            },
+            asset: {},
+          }),
+          recipientInput: '',
+          recipientMode: RECIPIENT_SEARCH_MODES.MY_ACCOUNTS,
         };
 
         const action = {
@@ -442,16 +621,16 @@ describe('Send Slice', () => {
 
         const result = sendReducer(noUserInputState, action);
 
-        expect(result.recipient.error).toBeNull();
-        expect(result.recipient.warning).toBeNull();
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.error).toBeNull();
+        expect(draftTransaction.recipient.warning).toBeNull();
       });
 
       it('should error with an invalid address error when user input is not a valid hex string', () => {
         const tokenAssetTypeState = {
-          ...initialState,
-          recipient: {
-            userInput: '0xValidateError',
-          },
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          recipientInput: '0xValidateError',
         };
         const action = {
           type: 'send/validateRecipientUserInput',
@@ -465,16 +644,18 @@ describe('Send Slice', () => {
 
         const result = sendReducer(tokenAssetTypeState, action);
 
-        expect(result.recipient.error).toStrictEqual('invalidAddressRecipient');
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.error).toStrictEqual(
+          'invalidAddressRecipient',
+        );
       });
 
       // TODO: Expectation might change in the future
       it('should error with an invalid network error when user input is not a valid hex string on a non default network', () => {
         const tokenAssetTypeState = {
-          ...initialState,
-          recipient: {
-            userInput: '0xValidateError',
-          },
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          recipientInput: '0xValidateError',
         };
         const action = {
           type: 'send/validateRecipientUserInput',
@@ -488,17 +669,17 @@ describe('Send Slice', () => {
 
         const result = sendReducer(tokenAssetTypeState, action);
 
-        expect(result.recipient.error).toStrictEqual(
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.error).toStrictEqual(
           'invalidAddressRecipientNotEthNetwork',
         );
       });
 
       it('should error with invalid address recipient when the user inputs the burn address', () => {
         const tokenAssetTypeState = {
-          ...initialState,
-          recipient: {
-            userInput: '0x0000000000000000000000000000000000000000',
-          },
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          recipientInput: '0x0000000000000000000000000000000000000000',
         };
         const action = {
           type: 'send/validateRecipientUserInput',
@@ -512,21 +693,24 @@ describe('Send Slice', () => {
 
         const result = sendReducer(tokenAssetTypeState, action);
 
-        expect(result.recipient.error).toStrictEqual('invalidAddressRecipient');
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.error).toStrictEqual(
+          'invalidAddressRecipient',
+        );
       });
 
       it('should error with same address recipient as a token', () => {
         const tokenAssetTypeState = {
-          ...initialState,
-          asset: {
-            type: ASSET_TYPES.TOKEN,
-            details: {
-              address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+          ...getInitialSendStateWithExistingTxState({
+            asset: {
+              type: ASSET_TYPES.TOKEN,
+              details: {
+                address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+              },
             },
-          },
-          recipient: {
-            userInput: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
-          },
+          }),
+          recipientInput: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
         };
 
         const action = {
@@ -540,8 +724,87 @@ describe('Send Slice', () => {
         };
 
         const result = sendReducer(tokenAssetTypeState, action);
+        const draftTransaction = getTestUUIDTx(result);
 
-        expect(result.recipient.error).toStrictEqual('contractAddressError');
+        expect(draftTransaction.recipient.error).toStrictEqual(
+          'contractAddressError',
+        );
+      });
+
+      it('should set a warning when sending to a token address in the token address list', () => {
+        const tokenAssetTypeState = {
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          recipientInput: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+        };
+
+        const action = {
+          type: 'send/validateRecipientUserInput',
+          payload: {
+            chainId: '0x4',
+            tokens: [],
+            useTokenDetection: true,
+            tokenAddressList: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'],
+          },
+        };
+
+        const result = sendReducer(tokenAssetTypeState, action);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.warning).toStrictEqual(
+          KNOWN_RECIPIENT_ADDRESS_WARNING,
+        );
+      });
+
+      it('should set a warning when sending to a token address in the token list', () => {
+        const tokenAssetTypeState = {
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          recipientInput: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+        };
+
+        const action = {
+          type: 'send/validateRecipientUserInput',
+          payload: {
+            chainId: '0x4',
+            tokens: [{ address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc' }],
+            useTokenDetection: true,
+            tokenAddressList: [],
+          },
+        };
+
+        const result = sendReducer(tokenAssetTypeState, action);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.warning).toStrictEqual(
+          KNOWN_RECIPIENT_ADDRESS_WARNING,
+        );
+      });
+
+      it('should set a warning when sending to an address that is probably a token contract', () => {
+        const tokenAssetTypeState = {
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          recipientInput: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
+        };
+
+        const action = {
+          type: 'send/validateRecipientUserInput',
+          payload: {
+            chainId: '0x4',
+            tokens: [{ address: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }],
+            useTokenDetection: true,
+            tokenAddressList: ['0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'],
+            isProbablyAnAssetContract: true,
+          },
+        };
+
+        const result = sendReducer(tokenAssetTypeState, action);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.warning).toStrictEqual(
+          KNOWN_RECIPIENT_ADDRESS_WARNING,
+        );
       });
     });
 
@@ -552,28 +815,18 @@ describe('Send Slice', () => {
           payload: 'a-random-string',
         };
 
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.recipient.mode).toStrictEqual(action.payload);
-      });
-    });
-
-    describe('resetSendState', () => {
-      it('should', () => {
-        const action = {
-          type: 'send/resetSendState',
-        };
-
-        const result = sendReducer({}, action);
-
-        expect(result).toStrictEqual(initialState);
+        expect(result.recipientMode).toStrictEqual(action.payload);
       });
     });
 
     describe('validateAmountField', () => {
       it('should error with insufficient funds when amount asset value plust gas is higher than asset balance', () => {
-        const nativeAssetState = {
-          ...initialState,
+        const nativeAssetState = getInitialSendStateWithExistingTxState({
           amount: {
             value: '0x6fc23ac0', // 1875000000
           },
@@ -584,7 +837,7 @@ describe('Send Slice', () => {
           gas: {
             gasTotal: '0x8f0d180', // 150000000
           },
-        };
+        });
 
         const action = {
           type: 'send/validateAmountField',
@@ -592,12 +845,15 @@ describe('Send Slice', () => {
 
         const result = sendReducer(nativeAssetState, action);
 
-        expect(result.amount.error).toStrictEqual(INSUFFICIENT_FUNDS_ERROR);
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.amount.error).toStrictEqual(
+          INSUFFICIENT_FUNDS_ERROR,
+        );
       });
 
       it('should error with insufficient tokens when amount value of tokens is higher than asset balance of token', () => {
-        const tokenAssetState = {
-          ...initialState,
+        const tokenAssetState = getInitialSendStateWithExistingTxState({
           amount: {
             value: '0x77359400', // 2000000000
           },
@@ -608,7 +864,7 @@ describe('Send Slice', () => {
               decimals: 0,
             },
           },
-        };
+        });
 
         const action = {
           type: 'send/validateAmountField',
@@ -616,16 +872,19 @@ describe('Send Slice', () => {
 
         const result = sendReducer(tokenAssetState, action);
 
-        expect(result.amount.error).toStrictEqual(INSUFFICIENT_TOKENS_ERROR);
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.amount.error).toStrictEqual(
+          INSUFFICIENT_TOKENS_ERROR,
+        );
       });
 
       it('should error negative value amount', () => {
-        const negativeAmountState = {
-          ...initialState,
+        const negativeAmountState = getInitialSendStateWithExistingTxState({
           amount: {
             value: '-1',
           },
-        };
+        });
 
         const action = {
           type: 'send/validateAmountField',
@@ -633,12 +892,13 @@ describe('Send Slice', () => {
 
         const result = sendReducer(negativeAmountState, action);
 
-        expect(result.amount.error).toStrictEqual(NEGATIVE_ETH_ERROR);
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.amount.error).toStrictEqual(NEGATIVE_ETH_ERROR);
       });
 
       it('should not error for positive value amount', () => {
-        const otherState = {
-          ...initialState,
+        const otherState = getInitialSendStateWithExistingTxState({
           amount: {
             error: 'someError',
             value: '1',
@@ -646,119 +906,135 @@ describe('Send Slice', () => {
           asset: {
             type: '',
           },
-        };
+        });
 
         const action = {
           type: 'send/validateAmountField',
         };
 
         const result = sendReducer(otherState, action);
-        expect(result.amount.error).toBeNull();
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.amount.error).toBeNull();
       });
     });
 
     describe('validateGasField', () => {
       it('should error when total amount of gas is higher than account balance', () => {
-        const gasFieldState = {
-          ...initialState,
+        const gasFieldState = getInitialSendStateWithExistingTxState({
           account: {
             balance: '0x0',
           },
           gas: {
             gasTotal: '0x1319718a5000', // 21000000000000
           },
-        };
+        });
 
         const action = {
           type: 'send/validateGasField',
         };
 
         const result = sendReducer(gasFieldState, action);
-        expect(result.gas.error).toStrictEqual(INSUFFICIENT_FUNDS_ERROR);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.gas.error).toStrictEqual(
+          INSUFFICIENT_FUNDS_ERROR,
+        );
       });
     });
 
     describe('validateSendState', () => {
       it('should set `INVALID` send state status when amount error is present', () => {
-        const amountErrorState = {
-          ...initialState,
+        const amountErrorState = getInitialSendStateWithExistingTxState({
           amount: {
             error: 'Some Amount Error',
           },
-        };
+        });
 
         const action = {
           type: 'send/validateSendState',
         };
 
         const result = sendReducer(amountErrorState, action);
-        expect(result.status).toStrictEqual(SEND_STATUSES.INVALID);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.status).toStrictEqual(SEND_STATUSES.INVALID);
       });
 
       it('should set `INVALID` send state status when gas error is present', () => {
-        const gasErrorState = {
-          ...initialState,
+        const gasErrorState = getInitialSendStateWithExistingTxState({
           gas: {
             error: 'Some Amount Error',
           },
-        };
+        });
 
         const action = {
           type: 'send/validateSendState',
         };
 
         const result = sendReducer(gasErrorState, action);
-        expect(result.status).toStrictEqual(SEND_STATUSES.INVALID);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.status).toStrictEqual(SEND_STATUSES.INVALID);
       });
 
       it('should set `INVALID` send state status when asset type is `TOKEN` without token details present', () => {
-        const assetErrorState = {
-          ...initialState,
+        const assetErrorState = getInitialSendStateWithExistingTxState({
           asset: {
             type: ASSET_TYPES.TOKEN,
           },
-        };
+        });
 
         const action = {
           type: 'send/validateSendState',
         };
 
         const result = sendReducer(assetErrorState, action);
-        expect(result.status).toStrictEqual(SEND_STATUSES.INVALID);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.status).toStrictEqual(SEND_STATUSES.INVALID);
       });
 
       it('should set `INVALID` send state status when gasLimit is under the minimumGasLimit', () => {
-        const gasLimitErroState = {
-          ...initialState,
+        const gasLimitErroState = getInitialSendStateWithExistingTxState({
           gas: {
             gasLimit: '0x5207',
-            minimumGasLimit: '0x5208',
+            minimumGasLimit: GAS_LIMITS.SIMPLE,
           },
-        };
+        });
 
         const action = {
           type: 'send/validateSendState',
         };
 
         const result = sendReducer(gasLimitErroState, action);
-        expect(result.status).toStrictEqual(SEND_STATUSES.INVALID);
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.status).toStrictEqual(SEND_STATUSES.INVALID);
       });
 
       it('should set `VALID` send state status when conditionals have not been met', () => {
         const validSendStatusState = {
-          ...initialState,
-          stage: SEND_STAGES.DRAFT,
-          asset: {
-            type: ASSET_TYPES.TOKEN,
-            details: {
-              address: '0x000',
+          ...getInitialSendStateWithExistingTxState({
+            asset: {
+              type: ASSET_TYPES.TOKEN,
+              details: {
+                address: '0x000',
+              },
             },
-          },
-          gas: {
-            isGasEstimateLoading: false,
-            gasLimit: '0x5208',
-            minimumGasLimit: '0x5208',
-          },
+            gas: {
+              gasLimit: GAS_LIMITS.SIMPLE,
+            },
+          }),
+          stage: SEND_STAGES.DRAFT,
+          gasEstimateIsLoading: false,
+          minimumGasLimit: GAS_LIMITS.SIMPLE,
         };
 
         const action = {
@@ -767,19 +1043,20 @@ describe('Send Slice', () => {
 
         const result = sendReducer(validSendStatusState, action);
 
-        expect(result.status).toStrictEqual(SEND_STATUSES.VALID);
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.status).toStrictEqual(SEND_STATUSES.VALID);
       });
     });
   });
 
   describe('extraReducers/externalReducers', () => {
     describe('QR Code Detected', () => {
-      const qrCodestate = {
-        ...initialState,
+      const qrCodestate = getInitialSendStateWithExistingTxState({
         recipient: {
           address: '0xAddress',
         },
-      };
+      });
 
       it('should set the recipient address to the scanned address value if they are not equal', () => {
         const action = {
@@ -793,7 +1070,10 @@ describe('Send Slice', () => {
         };
 
         const result = sendReducer(qrCodestate, action);
-        expect(result.recipient.address).toStrictEqual(
+
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.address).toStrictEqual(
           action.value.values.address,
         );
       });
@@ -811,10 +1091,10 @@ describe('Send Slice', () => {
 
         const result = sendReducer(qrCodestate, badQRAddressAction);
 
-        expect(result.recipient.address).toStrictEqual(
-          qrCodestate.recipient.address,
-        );
-        expect(result.recipient.error).toStrictEqual(
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.recipient.address).toStrictEqual('0xAddress');
+        expect(draftTransaction.recipient.error).toStrictEqual(
           INVALID_RECIPIENT_ADDRESS_ERROR,
         );
       });
@@ -823,8 +1103,8 @@ describe('Send Slice', () => {
     describe('Selected Address Changed', () => {
       it('should update selected account address and balance on non-edit stages', () => {
         const olderState = {
-          ...initialState,
-          account: {
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          selectedAccount: {
             balance: '0x0',
             address: '0xAddress',
           },
@@ -842,10 +1122,10 @@ describe('Send Slice', () => {
 
         const result = sendReducer(olderState, action);
 
-        expect(result.account.balance).toStrictEqual(
+        expect(result.selectedAccount.balance).toStrictEqual(
           action.payload.account.balance,
         );
-        expect(result.account.address).toStrictEqual(
+        expect(result.selectedAccount.address).toStrictEqual(
           action.payload.account.address,
         );
       });
@@ -854,9 +1134,9 @@ describe('Send Slice', () => {
     describe('Account Changed', () => {
       it('should', () => {
         const accountsChangedState = {
-          ...initialState,
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
           stage: SEND_STAGES.EDIT,
-          account: {
+          selectedAccount: {
             address: '0xAddress',
             balance: '0x0',
           },
@@ -874,16 +1154,16 @@ describe('Send Slice', () => {
 
         const result = sendReducer(accountsChangedState, action);
 
-        expect(result.account.balance).toStrictEqual(
+        expect(result.selectedAccount.balance).toStrictEqual(
           action.payload.account.balance,
         );
       });
 
       it(`should not edit account balance if action payload address is not the same as state's address`, () => {
         const accountsChangedState = {
-          ...initialState,
+          ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
           stage: SEND_STAGES.EDIT,
-          account: {
+          selectedAccount: {
             address: '0xAddress',
             balance: '0x0',
           },
@@ -900,10 +1180,10 @@ describe('Send Slice', () => {
         };
 
         const result = sendReducer(accountsChangedState, action);
-        expect(result.account.address).not.toStrictEqual(
+        expect(result.selectedAccount.address).not.toStrictEqual(
           action.payload.account.address,
         );
-        expect(result.account.balance).not.toStrictEqual(
+        expect(result.selectedAccount.balance).not.toStrictEqual(
           action.payload.account.balance,
         );
       });
@@ -976,7 +1256,7 @@ describe('Send Slice', () => {
               },
             },
           },
-          send: initialState,
+          send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
           gas: {
             basicEstimateStatus: 'LOADING',
             basicEstimatesStatus: {
@@ -1004,14 +1284,15 @@ describe('Send Slice', () => {
     describe('Set Basic Gas Estimate Data', () => {
       it('should recalculate gas based off of average basic estimate data', () => {
         const gasState = {
-          ...initialState,
-          gas: {
-            gasPrice: '0x0',
-            gasPriceEstimate: '0x0',
-            gasLimit: '0x5208',
-            gasTotal: '0x0',
-            minimumGasLimit: '0x5208',
-          },
+          ...getInitialSendStateWithExistingTxState({
+            gas: {
+              gasPrice: '0x0',
+              gasLimit: GAS_LIMITS.SIMPLE,
+              gasTotal: '0x0',
+            },
+          }),
+          minimumGasLimit: GAS_LIMITS.SIMPLE,
+          gasPriceEstimate: '0x0',
         };
 
         const action = {
@@ -1026,9 +1307,11 @@ describe('Send Slice', () => {
 
         const result = sendReducer(gasState, action);
 
-        expect(result.gas.gasPrice).toStrictEqual('0x3b9aca00'); // 1000000000
-        expect(result.gas.gasLimit).toStrictEqual(gasState.gas.gasLimit);
-        expect(result.gas.gasTotal).toStrictEqual('0x1319718a5000');
+        const draftTransaction = getTestUUIDTx(result);
+
+        expect(draftTransaction.gas.gasPrice).toStrictEqual('0x3b9aca00'); // 1000000000
+        expect(draftTransaction.gas.gasLimit).toStrictEqual(GAS_LIMITS.SIMPLE);
+        expect(draftTransaction.gas.gasTotal).toStrictEqual('0x1319718a5000');
       });
     });
   });
@@ -1037,11 +1320,11 @@ describe('Send Slice', () => {
     describe('updateGasPrice', () => {
       it('should update gas price and update draft transaction with validated state', async () => {
         const store = mockStore({
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasPrice: undefined,
             },
-          },
+          }),
         });
 
         const newGasPrice = '0x0';
@@ -1069,17 +1352,6 @@ describe('Send Slice', () => {
     });
 
     describe('UpdateSendAmount', () => {
-      const defaultSendAmountState = {
-        send: {
-          amount: {
-            mode: undefined,
-          },
-          asset: {
-            type: '',
-          },
-        },
-      };
-
       it('should create an action to update send amount', async () => {
         const sendState = {
           metamask: {
@@ -1089,8 +1361,7 @@ describe('Send Slice', () => {
               chainId: '0x1',
             },
           },
-          ...defaultSendAmountState.send,
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             asset: {
               details: {},
             },
@@ -1104,7 +1375,7 @@ describe('Send Slice', () => {
               value: '',
             },
             userInputHexData: '',
-          },
+          }),
         };
         const store = mockStore(sendState);
 
@@ -1143,8 +1414,7 @@ describe('Send Slice', () => {
               chainId: '0x1',
             },
           },
-          ...defaultSendAmountState.send,
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             asset: {
               details: {},
             },
@@ -1158,7 +1428,7 @@ describe('Send Slice', () => {
               value: '',
             },
             userInputHexData: '',
-          },
+          }),
         };
 
         const store = mockStore(sendState);
@@ -1196,8 +1466,7 @@ describe('Send Slice', () => {
               chainId: '0x1',
             },
           },
-          ...defaultSendAmountState.send,
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             asset: {
               type: ASSET_TYPES.TOKEN,
               details: {},
@@ -1212,7 +1481,7 @@ describe('Send Slice', () => {
               value: '',
             },
             userInputHexData: '',
-          },
+          }),
         };
 
         const store = mockStore(tokenAssetTypeSendState);
@@ -1239,27 +1508,39 @@ describe('Send Slice', () => {
           blockGasLimit: '',
           selectedAddress: '',
           provider: {
-            chainId: '0x1',
+            chainId: RINKEBY_CHAIN_ID,
+          },
+          cachedBalances: {
+            [RINKEBY_CHAIN_ID]: {
+              '0xAddress': '0x0',
+            },
+          },
+          accounts: {
+            '0xAddress': {
+              address: '0xAddress',
+            },
           },
         },
         send: {
-          account: {
-            balance: '',
+          ...getInitialSendStateWithExistingTxState({
+            asset: {
+              type: '',
+              details: {},
+            },
+            gas: {
+              gasPrice: '',
+            },
+            recipient: {
+              address: '',
+            },
+            amount: {
+              value: '',
+            },
+            userInputHexData: '',
+          }),
+          selectedAccount: {
+            address: '0xAddress',
           },
-          asset: {
-            type: '',
-            details: {},
-          },
-          gas: {
-            gasPrice: '',
-          },
-          recipient: {
-            address: '',
-          },
-          amount: {
-            value: '',
-          },
-          userInputHexData: '',
         },
       };
 
@@ -1267,48 +1548,44 @@ describe('Send Slice', () => {
         const store = mockStore(defaultSendAssetState);
 
         const newSendAsset = {
-          type: '',
-          details: {
-            address: '',
-            symbol: '',
-            decimals: '',
-          },
+          type: ASSET_TYPES.NATIVE,
         };
 
         await store.dispatch(updateSendAsset(newSendAsset));
 
         const actionResult = store.getActions();
 
-        expect(actionResult).toHaveLength(6);
+        expect(actionResult).toHaveLength(4);
+
         expect(actionResult[0]).toMatchObject({
           type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset type to ',
+          payload: 'sendFlow - user set asset of type NATIVE with symbol ETH',
         });
-        expect(actionResult[1]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset symbol to ',
-        });
-        expect(actionResult[2]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset address to ',
-        });
-
-        expect(actionResult[3].type).toStrictEqual('send/updateAsset');
-        expect(actionResult[3].payload).toStrictEqual({
-          ...newSendAsset,
-          balance: '',
+        expect(actionResult[1].type).toStrictEqual('send/updateAsset');
+        expect(actionResult[1].payload).toStrictEqual({
+          type: ASSET_TYPES.NATIVE,
+          balance: '0x0',
           error: null,
+          details: null,
         });
 
-        expect(actionResult[4].type).toStrictEqual(
+        expect(actionResult[2].type).toStrictEqual(
           'send/computeEstimatedGasLimit/pending',
         );
-        expect(actionResult[5].type).toStrictEqual(
+        expect(actionResult[3].type).toStrictEqual(
           'send/computeEstimatedGasLimit/rejected',
         );
       });
 
       it('should create actions for updateSendAsset with tokens', async () => {
+        getTokenStandardAndDetailsStub.mockImplementation(() =>
+          Promise.resolve({
+            standard: 'ERC20',
+            balance: '0x0',
+            symbol: 'TokenSymbol',
+            decimals: 18,
+          }),
+        );
         global.eth = {
           contract: sinon.stub().returns({
             at: sinon.stub().returns({
@@ -1331,31 +1608,30 @@ describe('Send Slice', () => {
 
         const actionResult = store.getActions();
 
-        expect(actionResult).toHaveLength(8);
-        expect(actionResult[0]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: `sendFlow - user set asset type to ${ASSET_TYPES.TOKEN}`,
-        });
-        expect(actionResult[1]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset symbol to tokenSymbol',
-        });
+        expect(actionResult).toHaveLength(6);
+        expect(actionResult[0].type).toStrictEqual('SHOW_LOADING_INDICATION');
+        expect(actionResult[1].type).toStrictEqual('HIDE_LOADING_INDICATION');
         expect(actionResult[2]).toMatchObject({
           type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset address to tokenAddress',
+          payload: `sendFlow - user set asset to ERC20 token with symbol TokenSymbol and address tokenAddress`,
         });
-        expect(actionResult[3].type).toStrictEqual('SHOW_LOADING_INDICATION');
-        expect(actionResult[4].type).toStrictEqual('HIDE_LOADING_INDICATION');
-        expect(actionResult[5].payload).toStrictEqual({
-          ...newSendAsset,
+        expect(actionResult[3].payload).toStrictEqual({
+          type: ASSET_TYPES.TOKEN,
+          details: {
+            address: 'tokenAddress',
+            symbol: 'TokenSymbol',
+            decimals: 18,
+            standard: 'ERC20',
+            balance: '0x0',
+          },
           balance: '0x0',
           error: null,
         });
 
-        expect(actionResult[6].type).toStrictEqual(
+        expect(actionResult[4].type).toStrictEqual(
           'send/computeEstimatedGasLimit/pending',
         );
-        expect(actionResult[7].type).toStrictEqual(
+        expect(actionResult[5].type).toStrictEqual(
           'send/computeEstimatedGasLimit/rejected',
         );
       });
@@ -1363,7 +1639,7 @@ describe('Send Slice', () => {
       it('should show ConvertTokenToNFT modal and throw "invalidAssetType" error when token passed in props is an ERC721 or ERC1155', async () => {
         process.env.COLLECTIBLES_V1 = true;
         getTokenStandardAndDetailsStub.mockImplementation(() =>
-          Promise.resolve({ standard: 'ERC1155' }),
+          Promise.resolve({ standard: 'ERC1155', balance: '0x1' }),
         );
         const store = mockStore(defaultSendAssetState);
 
@@ -1380,22 +1656,10 @@ describe('Send Slice', () => {
           store.dispatch(updateSendAsset(newSendAsset)),
         ).rejects.toThrow('invalidAssetType');
         const actionResult = store.getActions();
-        expect(actionResult).toHaveLength(6);
-        expect(actionResult[0]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: `sendFlow - user set asset type to ${ASSET_TYPES.TOKEN}`,
-        });
-        expect(actionResult[1]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset symbol to tokenSymbol',
-        });
-        expect(actionResult[2]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset address to tokenAddress',
-        });
-        expect(actionResult[3].type).toStrictEqual('SHOW_LOADING_INDICATION');
-        expect(actionResult[4].type).toStrictEqual('HIDE_LOADING_INDICATION');
-        expect(actionResult[5]).toStrictEqual({
+        expect(actionResult).toHaveLength(3);
+        expect(actionResult[0].type).toStrictEqual('SHOW_LOADING_INDICATION');
+        expect(actionResult[1].type).toStrictEqual('HIDE_LOADING_INDICATION');
+        expect(actionResult[2]).toStrictEqual({
           payload: {
             name: 'CONVERT_TOKEN_TO_NFT',
             tokenAddress: 'tokenAddress',
@@ -1439,11 +1703,10 @@ describe('Send Slice', () => {
             },
           },
         },
+        send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
       };
 
       it('should create actions for updateRecipientUserInput and checks debounce for validation', async () => {
-        const clock = sinon.useFakeTimers();
-
         const store = mockStore(updateRecipientUserInputState);
         const newUserRecipientInput = 'newUserRecipientInput';
 
@@ -1451,29 +1714,35 @@ describe('Send Slice', () => {
 
         const actionResult = store.getActions();
 
-        expect(actionResult).toHaveLength(1);
+        expect(actionResult).toHaveLength(5);
+
         expect(actionResult[0].type).toStrictEqual(
+          'send/updateRecipientWarning',
+        );
+        expect(actionResult[0].payload).toStrictEqual('loading');
+
+        expect(actionResult[1].type).toStrictEqual(
+          'send/updateDraftTransactionStatus',
+        );
+
+        expect(actionResult[2].type).toStrictEqual(
           'send/updateRecipientUserInput',
         );
-        expect(actionResult[0].payload).toStrictEqual(newUserRecipientInput);
+        expect(actionResult[2].payload).toStrictEqual(newUserRecipientInput);
 
-        clock.tick(300); // debounce
-
-        const actionResultAfterDebounce = store.getActions();
-        expect(actionResultAfterDebounce).toHaveLength(3);
-
-        expect(actionResultAfterDebounce[1]).toMatchObject({
+        expect(actionResult[3]).toMatchObject({
           type: 'send/addHistoryEntry',
           payload: `sendFlow - user typed ${newUserRecipientInput} into recipient input field`,
         });
 
-        expect(actionResultAfterDebounce[2].type).toStrictEqual(
+        expect(actionResult[4].type).toStrictEqual(
           'send/validateRecipientUserInput',
         );
-        expect(actionResultAfterDebounce[2].payload).toStrictEqual({
+        expect(actionResult[4].payload).toStrictEqual({
           chainId: '',
           tokens: [],
           useTokenDetection: true,
+          isProbablyAnAssetContract: false,
           userInput: newUserRecipientInput,
           tokenAddressList: ['0x514910771af9ca656af840dff83e8264ecf986ca'],
         });
@@ -1730,21 +1999,7 @@ describe('Send Slice', () => {
               },
             },
           },
-          send: {
-            asset: {
-              type: '',
-            },
-            recipient: {
-              address: 'Address',
-              nickname: 'NickName',
-            },
-            gas: {
-              gasPrice: '0x1',
-            },
-            amount: {
-              value: '0x1',
-            },
-          },
+          send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
         };
 
         const store = mockStore(updateRecipientState);
@@ -1752,24 +2007,36 @@ describe('Send Slice', () => {
         await store.dispatch(resetRecipientInput());
         const actionResult = store.getActions();
 
-        expect(actionResult).toHaveLength(7);
+        expect(actionResult).toHaveLength(11);
         expect(actionResult[0]).toMatchObject({
           type: 'send/addHistoryEntry',
           payload: 'sendFlow - user cleared recipient input',
         });
         expect(actionResult[1].type).toStrictEqual(
+          'send/updateRecipientWarning',
+        );
+        expect(actionResult[2].type).toStrictEqual(
+          'send/updateDraftTransactionStatus',
+        );
+
+        expect(actionResult[3].type).toStrictEqual(
           'send/updateRecipientUserInput',
         );
-        expect(actionResult[1].payload).toStrictEqual('');
-        expect(actionResult[2].type).toStrictEqual('send/updateRecipient');
-        expect(actionResult[3].type).toStrictEqual(
+        expect(actionResult[4].payload).toStrictEqual(
+          'sendFlow - user typed  into recipient input field',
+        );
+        expect(actionResult[5].type).toStrictEqual(
+          'send/validateRecipientUserInput',
+        );
+        expect(actionResult[6].type).toStrictEqual('send/updateRecipient');
+        expect(actionResult[7].type).toStrictEqual(
           'send/computeEstimatedGasLimit/pending',
         );
-        expect(actionResult[4].type).toStrictEqual(
+        expect(actionResult[8].type).toStrictEqual(
           'send/computeEstimatedGasLimit/rejected',
         );
-        expect(actionResult[5].type).toStrictEqual('ENS/resetEnsResolution');
-        expect(actionResult[6].type).toStrictEqual(
+        expect(actionResult[9].type).toStrictEqual('ENS/resetEnsResolution');
+        expect(actionResult[10].type).toStrictEqual(
           'send/validateRecipientUserInput',
         );
       });
@@ -1777,11 +2044,11 @@ describe('Send Slice', () => {
 
     describe('UpdateSendHexData', () => {
       const sendHexDataState = {
-        send: {
+        send: getInitialSendStateWithExistingTxState({
           asset: {
             type: '',
           },
-        },
+        }),
       };
 
       it('should create action to update hexData', async () => {
@@ -1853,24 +2120,26 @@ describe('Send Slice', () => {
         );
       });
 
-      it('should create actions to toggle off  max mode when send amount mode is max', async () => {
+      it('should create actions to toggle off max mode when send amount mode is max', async () => {
         const sendMaxModeState = {
           send: {
-            asset: {
-              type: ASSET_TYPES.TOKEN,
-              details: {},
-            },
-            gas: {
-              gasPrice: '',
-            },
-            recipient: {
-              address: '',
-            },
-            amount: {
-              mode: AMOUNT_MODES.MAX,
-              value: '',
-            },
-            userInputHexData: '',
+            ...getInitialSendStateWithExistingTxState({
+              asset: {
+                type: ASSET_TYPES.TOKEN,
+                details: {},
+              },
+              gas: {
+                gasPrice: '',
+              },
+              recipient: {
+                address: '',
+              },
+              amount: {
+                value: '',
+              },
+              userInputHexData: '',
+            }),
+            amountMode: AMOUNT_MODES.MAX,
           },
           metamask: {
             provider: {
@@ -1902,16 +2171,15 @@ describe('Send Slice', () => {
 
     describe('SignTransaction', () => {
       const signTransactionState = {
-        send: {
+        send: getInitialSendStateWithExistingTxState({
+          id: 1,
           asset: {},
-          stage: '',
           recipient: {},
           amount: {},
-          account: {},
           gas: {
             gasLimit: GAS_LIMITS.SIMPLE,
           },
-        },
+        }),
       };
 
       it('should show confirm tx page when no other conditions for signing have been met', async () => {
@@ -1944,23 +2212,24 @@ describe('Send Slice', () => {
               },
             },
             send: {
-              ...signTransactionState.send,
-              stage: SEND_STAGES.DRAFT,
-              id: 1,
-              account: {
-                address: '0x6784e8507A1A46443f7bDc8f8cA39bdA92A675A6',
-              },
-              asset: {
-                details: {
-                  address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
+              ...getInitialSendStateWithExistingTxState({
+                id: 1,
+                asset: {
+                  details: {
+                    address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
+                  },
+                  type: 'TOKEN',
                 },
-                type: 'TOKEN',
-              },
-              recipient: {
-                address: '4F90e18605Fd46F9F9Fab0e225D88e1ACf5F5324',
-              },
-              amount: {
-                value: '0x1',
+                recipient: {
+                  address: '4F90e18605Fd46F9F9Fab0e225D88e1ACf5F5324',
+                },
+                amount: {
+                  value: '0x1',
+                },
+              }),
+              stage: SEND_STAGES.DRAFT,
+              selectedAccount: {
+                address: '0x6784e8507A1A46443f7bDc8f8cA39bdA92A675A6',
               },
             },
           };
@@ -1999,7 +2268,6 @@ describe('Send Slice', () => {
           send: {
             ...signTransactionState.send,
             stage: SEND_STAGES.EDIT,
-            id: 1,
           },
         };
 
@@ -2026,10 +2294,12 @@ describe('Send Slice', () => {
       });
     });
 
-    describe('editTransaction', () => {
+    describe('editExistingTransaction', () => {
       it('should set up the appropriate state for editing a native asset transaction', async () => {
         const editTransactionState = {
           metamask: {
+            gasEstimateType: GAS_ESTIMATE_TYPES.NONE,
+            gasFeeEstimates: {},
             provider: {
               chainId: RINKEBY_CHAIN_ID,
             },
@@ -2038,6 +2308,18 @@ describe('Send Slice', () => {
               [RINKEBY_CHAIN_ID]: {},
             },
             identities: {},
+            accounts: {
+              '0xAddress': {
+                address: '0xAddress',
+                balance: '0x0',
+              },
+            },
+            cachedBalances: {
+              [RINKEBY_CHAIN_ID]: {
+                '0xAddress': '0x0',
+              },
+            },
+            tokenList: {},
             unapprovedTxs: {
               1: {
                 id: 1,
@@ -2053,49 +2335,100 @@ describe('Send Slice', () => {
             },
           },
           send: {
-            asset: {
-              type: '',
-            },
-            recipient: {
-              address: 'Address',
-              nickname: 'NickName',
-            },
+            // We are going to remove this transaction as a part of the flow,
+            // but we need this stub to have the fromAccount because for our
+            // action checker the state isn't actually modified after each
+            // action is ran.
+            ...getInitialSendStateWithExistingTxState({
+              id: 1,
+              fromAccount: {
+                address: '0xAddress',
+              },
+            }),
           },
         };
 
         const store = mockStore(editTransactionState);
 
-        await store.dispatch(editTransaction(ASSET_TYPES.NATIVE, 1));
+        await store.dispatch(editExistingTransaction(ASSET_TYPES.NATIVE, 1));
         const actionResult = store.getActions();
 
-        expect(actionResult).toHaveLength(2);
+        expect(actionResult).toHaveLength(7);
         expect(actionResult[0]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user clicked edit on transaction with id 1',
+          type: 'send/clearPreviousDrafts',
         });
-        expect(actionResult[1].type).toStrictEqual('send/editTransaction');
-        expect(actionResult[1].payload).toStrictEqual({
-          address: '0xRecipientAddress',
-          amount: '0xde0b6b3a7640000',
-          data: '',
-          from: '0xAddress',
-          gasLimit: GAS_LIMITS.SIMPLE,
-          gasPrice: '0x3b9aca00',
-          id: 1,
-          nickname: '',
+        expect(actionResult[1]).toStrictEqual({
+          type: 'send/addNewDraft',
+          payload: {
+            amount: {
+              value: '0xde0b6b3a7640000',
+              error: null,
+            },
+            asset: {
+              balance: '0x0',
+              details: null,
+              error: null,
+              type: ASSET_TYPES.NATIVE,
+            },
+            fromAccount: {
+              address: '0xAddress',
+              balance: '0x0',
+            },
+            gas: {
+              error: null,
+              gasLimit: GAS_LIMITS.SIMPLE,
+              gasPrice: '0x3b9aca00',
+              gasTotal: '0x0',
+              maxFeePerGas: '0x0',
+              maxPriorityFeePerGas: '0x0',
+            },
+            history: ['sendFlow - user clicked edit on transaction with id 1'],
+            id: 1,
+            recipient: {
+              address: '0xRecipientAddress',
+              error: null,
+              nickname: '',
+              warning: null,
+              recipientWarningAcknowledged: false,
+            },
+            status: SEND_STATUSES.VALID,
+            transactionType: '0x0',
+            userInputHexData: '',
+          },
         });
 
         const action = actionResult[1];
 
-        const result = sendReducer(initialState, action);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.gas.gasLimit).toStrictEqual(action.payload.gasLimit);
-        expect(result.gas.gasPrice).toStrictEqual(action.payload.gasPrice);
+        expect(result.currentTransactionUUID).not.toStrictEqual('test-uuid');
 
-        expect(result.amount.value).toStrictEqual(action.payload.amount);
+        const draftTransaction =
+          result.draftTransactions[result.currentTransactionUUID];
+
+        expect(draftTransaction.gas.gasLimit).toStrictEqual(
+          action.payload.gas.gasLimit,
+        );
+        expect(draftTransaction.gas.gasPrice).toStrictEqual(
+          action.payload.gas.gasPrice,
+        );
+
+        expect(draftTransaction.amount.value).toStrictEqual(
+          action.payload.amount.value,
+        );
       });
 
       it('should set up the appropriate state for editing a collectible asset transaction', async () => {
+        getTokenStandardAndDetailsStub.mockImplementation(() =>
+          Promise.resolve({
+            standard: 'ERC721',
+            balance: '0x1',
+            address: '0xCollectibleAddress',
+          }),
+        );
         const editTransactionState = {
           metamask: {
             blockGasLimit: '0x3a98',
@@ -2108,13 +2441,29 @@ describe('Send Slice', () => {
               [RINKEBY_CHAIN_ID]: {},
             },
             identities: {},
+            accounts: {
+              '0xAddress': {
+                address: '0xAddress',
+                balance: '0x0',
+              },
+            },
+            cachedBalances: {
+              [RINKEBY_CHAIN_ID]: {
+                '0xAddress': '0x0',
+              },
+            },
+            tokenList: {},
             unapprovedTxs: {
               1: {
                 id: 1,
                 txParams: {
-                  data: '',
+                  data: generateERC721TransferData({
+                    toAddress: BURN_ADDRESS,
+                    fromAddress: '0xAddress',
+                    tokenId: ethers.BigNumber.from(15000).toString(),
+                  }),
                   from: '0xAddress',
-                  to: '0xTokenAddress',
+                  to: '0xCollectibleAddress',
                   gas: GAS_LIMITS.BASE_TOKEN_ESTIMATE,
                   gasPrice: '0x3b9aca00', // 1000000000
                   value: '0x0',
@@ -2123,25 +2472,12 @@ describe('Send Slice', () => {
             },
           },
           send: {
-            account: {
-              address: '0xAddress',
-              balance: '0x0',
-            },
-            asset: {
-              type: '',
-            },
-            gas: {
-              gasPrice: '',
-            },
-            amount: {
-              value: '',
-            },
-            userInputHexData: '',
-
-            recipient: {
-              address: 'Address',
-              nickname: 'NickName',
-            },
+            ...getInitialSendStateWithExistingTxState({
+              id: 1,
+              test: 'wow',
+              gas: { gasLimit: GAS_LIMITS.SIMPLE },
+            }),
+            stage: SEND_STAGES.EDIT,
           },
         };
 
@@ -2157,78 +2493,107 @@ describe('Send Slice', () => {
         const store = mockStore(editTransactionState);
 
         await store.dispatch(
-          editTransaction(
-            ASSET_TYPES.COLLECTIBLE,
-            1,
-            {
-              name: TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
-              args: {
-                _to: '0xRecipientAddress',
-                _value: ethers.BigNumber.from(15000),
-              },
-            },
-            {
-              address: '0xf5de760f2e916647fd766B4AD9E85ff943cE3A2b',
-              description: 'A test NFT dispensed from faucet.paradigm.xyz.',
-              image:
-                'https://ipfs.io/ipfs/bafybeifvwitulq6elvka2hoqhwixfhgb42l4aiukmtrw335osetikviuuu',
-              name: 'MultiFaucet Test NFT',
-              standard: 'ERC721',
-              tokenId: '26847',
-            },
-          ),
+          editExistingTransaction(ASSET_TYPES.COLLECTIBLE, 1),
         );
         const actionResult = store.getActions();
         expect(actionResult).toHaveLength(9);
         expect(actionResult[0]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user clicked edit on transaction with id 1',
+          type: 'send/clearPreviousDrafts',
         });
-        expect(actionResult[1]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: `sendFlow - user set asset type to ${ASSET_TYPES.COLLECTIBLE}`,
-        });
-        expect(actionResult[2]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset symbol to undefined',
-        });
-        expect(actionResult[3]).toMatchObject({
-          type: 'send/addHistoryEntry',
-          payload: 'sendFlow - user set asset address to 0xTokenAddress',
-        });
-        expect(actionResult[4].type).toStrictEqual('send/updateAsset');
-        expect(actionResult[4].payload).toStrictEqual({
-          balance: '0x1',
-          type: ASSET_TYPES.COLLECTIBLE,
-          error: null,
-          details: {
-            address: '0xTokenAddress',
-            description: 'A test NFT dispensed from faucet.paradigm.xyz.',
-            image:
-              'https://ipfs.io/ipfs/bafybeifvwitulq6elvka2hoqhwixfhgb42l4aiukmtrw335osetikviuuu',
-            name: 'MultiFaucet Test NFT',
-            standard: 'ERC721',
-            tokenId: '26847',
+        expect(actionResult[1]).toStrictEqual({
+          type: 'send/addNewDraft',
+          payload: {
+            amount: {
+              error: null,
+              value: '0x1',
+            },
+            asset: {
+              balance: '0x0',
+              details: null,
+              error: null,
+              type: ASSET_TYPES.NATIVE,
+            },
+            fromAccount: {
+              address: '0xAddress',
+              balance: '0x0',
+            },
+            gas: {
+              error: null,
+              gasLimit: GAS_LIMITS.BASE_TOKEN_ESTIMATE,
+              gasPrice: '0x3b9aca00',
+              gasTotal: '0x0',
+              maxFeePerGas: '0x0',
+              maxPriorityFeePerGas: '0x0',
+            },
+            history: ['sendFlow - user clicked edit on transaction with id 1'],
+            id: 1,
+            recipient: {
+              address: BURN_ADDRESS,
+              error: null,
+              nickname: '',
+              warning: null,
+              recipientWarningAcknowledged: false,
+            },
+            status: SEND_STATUSES.VALID,
+            transactionType: '0x0',
+            userInputHexData:
+              editTransactionState.metamask.unapprovedTxs[1].txParams.data,
+          },
+        });
+        expect(actionResult[2].type).toStrictEqual('SHOW_LOADING_INDICATION');
+        expect(actionResult[3].type).toStrictEqual('HIDE_LOADING_INDICATION');
+        expect(actionResult[4]).toStrictEqual({
+          type: 'send/addHistoryEntry',
+          payload:
+            'sendFlow - user set asset to NFT with tokenId 15000 and address 0xCollectibleAddress',
+        });
+        expect(actionResult[5]).toStrictEqual({
+          type: 'send/updateAsset',
+          payload: {
+            balance: '0x1',
+            details: {
+              address: '0xCollectibleAddress',
+              balance: '0x1',
+              standard: TOKEN_STANDARDS.ERC721,
+              tokenId: '15000',
+            },
+            error: null,
+            type: ASSET_TYPES.COLLECTIBLE,
           },
         });
-        expect(actionResult[5].type).toStrictEqual(
-          'send/computeEstimatedGasLimit/pending',
-        );
         expect(actionResult[6].type).toStrictEqual(
-          'metamask/gas/SET_CUSTOM_GAS_LIMIT',
+          'send/initializeSendState/pending',
         );
-        expect(actionResult[7].type).toStrictEqual(
-          'send/computeEstimatedGasLimit/fulfilled',
+        expect(actionResult[7]).toStrictEqual({
+          type: 'metamask/gas/SET_CUSTOM_GAS_LIMIT',
+          value: GAS_LIMITS.SIMPLE,
+        });
+        expect(actionResult[8].type).toStrictEqual(
+          'send/initializeSendState/fulfilled',
         );
-        expect(actionResult[8].type).toStrictEqual('send/editTransaction');
-        const action = actionResult[8];
 
-        const result = sendReducer(initialState, action);
+        const action = actionResult[1];
 
-        expect(result.gas.gasLimit).toStrictEqual(action.payload.gasLimit);
-        expect(result.gas.gasPrice).toStrictEqual(action.payload.gasPrice);
+        const result = sendReducer(
+          INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          action,
+        );
 
-        expect(result.amount.value).toStrictEqual(action.payload.amount);
+        expect(result.currentTransactionUUID).not.toStrictEqual('test-uuid');
+
+        const draftTransaction =
+          result.draftTransactions[result.currentTransactionUUID];
+
+        expect(draftTransaction.gas.gasLimit).toStrictEqual(
+          action.payload.gas.gasLimit,
+        );
+        expect(draftTransaction.gas.gasPrice).toStrictEqual(
+          action.payload.gas.gasPrice,
+        );
+
+        expect(draftTransaction.amount.value).toStrictEqual(
+          action.payload.amount.value,
+        );
       });
     });
 
@@ -2240,16 +2605,46 @@ describe('Send Slice', () => {
           provider: {
             chainId: RINKEBY_CHAIN_ID,
           },
-          tokens: [],
+          tokens: [
+            {
+              address: '0xTokenAddress',
+              symbol: 'SYMB',
+            },
+          ],
+          tokenList: {
+            '0xTokenAddress': {
+              symbol: 'SYMB',
+              address: '0xTokenAddress',
+            },
+          },
           addressBook: {
             [RINKEBY_CHAIN_ID]: {},
           },
           identities: {},
+          accounts: {
+            '0xAddress': {
+              address: '0xAddress',
+              balance: '0x0',
+            },
+          },
+          cachedBalances: {
+            [RINKEBY_CHAIN_ID]: {
+              '0xAddress': '0x0',
+            },
+          },
           unapprovedTxs: {
             1: {
               id: 1,
               txParams: {
-                data: '',
+                data: generateERC20TransferData({
+                  toAddress: BURN_ADDRESS,
+                  amount: '0x3a98',
+                  sendToken: {
+                    address: '0xTokenAddress',
+                    symbol: 'SYMB',
+                    decimals: 18,
+                  },
+                }),
                 from: '0xAddress',
                 to: '0xTokenAddress',
                 gas: GAS_LIMITS.BASE_TOKEN_ESTIMATE,
@@ -2260,24 +2655,18 @@ describe('Send Slice', () => {
           },
         },
         send: {
-          account: {
+          ...getInitialSendStateWithExistingTxState({
+            id: 1,
+            recipient: {
+              address: 'Address',
+              nickname: 'NickName',
+            },
+          }),
+          selectedAccount: {
             address: '0xAddress',
             balance: '0x0',
           },
-          asset: {
-            type: '',
-          },
-          gas: {
-            gasPrice: '',
-          },
-          amount: {
-            value: '',
-          },
-          userInputHexData: '',
-          recipient: {
-            address: 'Address',
-            nickname: 'NickName',
-          },
+          stage: SEND_STAGES.EDIT,
         },
       };
 
@@ -2292,118 +2681,146 @@ describe('Send Slice', () => {
 
       const store = mockStore(editTransactionState);
 
-      await store.dispatch(
-        editTransaction(
-          ASSET_TYPES.TOKEN,
-          1,
-          {
-            name: TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
-            args: {
-              _to: '0xRecipientAddress',
-              _value: ethers.BigNumber.from(15000),
-            },
-          },
-          { address: '0xAddress', symbol: 'SYMB', decimals: 18 },
-        ),
-      );
+      await store.dispatch(editExistingTransaction(ASSET_TYPES.TOKEN, 1));
       const actionResult = store.getActions();
 
-      expect(actionResult).toHaveLength(11);
-      expect(actionResult[0]).toMatchObject({
-        type: 'send/addHistoryEntry',
-        payload: 'sendFlow - user clicked edit on transaction with id 1',
-      });
-      expect(actionResult[1]).toMatchObject({
-        type: 'send/addHistoryEntry',
-        payload: `sendFlow - user set asset type to ${ASSET_TYPES.TOKEN}`,
-      });
-      expect(actionResult[2]).toMatchObject({
-        type: 'send/addHistoryEntry',
-        payload: 'sendFlow - user set asset symbol to SYMB',
-      });
-      expect(actionResult[3]).toMatchObject({
-        type: 'send/addHistoryEntry',
-        payload: 'sendFlow - user set asset address to 0xTokenAddress',
-      });
-      expect(actionResult[4].type).toStrictEqual('SHOW_LOADING_INDICATION');
-      expect(actionResult[5].type).toStrictEqual('HIDE_LOADING_INDICATION');
-      expect(actionResult[6].type).toStrictEqual('send/updateAsset');
-      expect(actionResult[6].payload).toStrictEqual({
-        balance: '0x0',
-        type: ASSET_TYPES.TOKEN,
-        error: null,
-        details: {
-          address: '0xTokenAddress',
-          decimals: 18,
-          symbol: 'SYMB',
-          standard: 'ERC20',
+      expect(actionResult).toHaveLength(9);
+      expect(actionResult[0].type).toStrictEqual('send/clearPreviousDrafts');
+      expect(actionResult[1]).toStrictEqual({
+        type: 'send/addNewDraft',
+        payload: {
+          amount: {
+            error: null,
+            value: '0x3a98',
+          },
+          asset: {
+            balance: '0x0',
+            details: null,
+            error: null,
+            type: ASSET_TYPES.NATIVE,
+          },
+          fromAccount: {
+            address: '0xAddress',
+            balance: '0x0',
+          },
+          gas: {
+            error: null,
+            gasLimit: '0x186a0',
+            gasPrice: '0x3b9aca00',
+            gasTotal: '0x0',
+            maxFeePerGas: '0x0',
+            maxPriorityFeePerGas: '0x0',
+          },
+          history: ['sendFlow - user clicked edit on transaction with id 1'],
+          id: 1,
+          recipient: {
+            address: BURN_ADDRESS,
+            error: null,
+            warning: null,
+            nickname: '',
+            recipientWarningAcknowledged: false,
+          },
+          status: SEND_STATUSES.VALID,
+          transactionType: '0x0',
+          userInputHexData:
+            editTransactionState.metamask.unapprovedTxs[1].txParams.data,
         },
       });
-      expect(actionResult[7].type).toStrictEqual(
-        'send/computeEstimatedGasLimit/pending',
+      expect(actionResult[2].type).toStrictEqual('SHOW_LOADING_INDICATION');
+      expect(actionResult[3].type).toStrictEqual('HIDE_LOADING_INDICATION');
+      expect(actionResult[4]).toMatchObject({
+        type: 'send/addHistoryEntry',
+        payload:
+          'sendFlow - user set asset to ERC20 token with symbol SYMB and address 0xTokenAddress',
+      });
+      expect(actionResult[5]).toStrictEqual({
+        type: 'send/updateAsset',
+        payload: {
+          balance: '0x0',
+          type: ASSET_TYPES.TOKEN,
+          error: null,
+          details: {
+            balance: '0x0',
+            address: '0xTokenAddress',
+            decimals: 18,
+            symbol: 'SYMB',
+            standard: 'ERC20',
+          },
+        },
+      });
+      expect(actionResult[6].type).toStrictEqual(
+        'send/initializeSendState/pending',
       );
-      expect(actionResult[8].type).toStrictEqual(
+      expect(actionResult[7].type).toStrictEqual(
         'metamask/gas/SET_CUSTOM_GAS_LIMIT',
       );
-      expect(actionResult[9].type).toStrictEqual(
-        'send/computeEstimatedGasLimit/fulfilled',
+      expect(actionResult[8].type).toStrictEqual(
+        'send/initializeSendState/fulfilled',
       );
-      expect(actionResult[10].type).toStrictEqual('send/editTransaction');
-      expect(actionResult[10].payload).toStrictEqual({
-        address: '0xrecipientaddress', // getting address from tokenData does .toLowerCase
-        amount: '0x3a98',
-        data: '',
-        from: '0xAddress',
-        gasLimit: GAS_LIMITS.BASE_TOKEN_ESTIMATE,
-        gasPrice: '0x3b9aca00',
-        id: 1,
-        nickname: '',
-      });
 
-      const action = actionResult[10];
+      const action = actionResult[1];
 
-      const result = sendReducer(initialState, action);
+      const result = sendReducer(INITIAL_SEND_STATE_FOR_EXISTING_DRAFT, action);
 
-      expect(result.gas.gasLimit).toStrictEqual(action.payload.gasLimit);
-      expect(result.gas.gasPrice).toStrictEqual(action.payload.gasPrice);
+      expect(result.currentTransactionUUID).not.toStrictEqual('test-uuid');
 
-      expect(result.amount.value).toStrictEqual(action.payload.amount);
+      const draftTransaction =
+        result.draftTransactions[result.currentTransactionUUID];
+
+      expect(draftTransaction.gas.gasLimit).toStrictEqual(
+        action.payload.gas.gasLimit,
+      );
+      expect(draftTransaction.gas.gasPrice).toStrictEqual(
+        action.payload.gas.gasPrice,
+      );
+
+      expect(draftTransaction.amount.value).toStrictEqual(
+        action.payload.amount.value,
+      );
     });
   });
 
   describe('selectors', () => {
     describe('gas selectors', () => {
       it('has a selector that gets gasLimit', () => {
-        expect(getGasLimit({ send: initialState })).toBe('0x0');
+        expect(
+          getGasLimit({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe('0x0');
       });
 
       it('has a selector that gets gasPrice', () => {
-        expect(getGasPrice({ send: initialState })).toBe('0x0');
+        expect(
+          getGasPrice({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe('0x0');
       });
 
       it('has a selector that gets gasTotal', () => {
-        expect(getGasTotal({ send: initialState })).toBe('0x0');
+        expect(
+          getGasTotal({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe('0x0');
       });
 
       it('has a selector to determine if gas fee is in error', () => {
-        expect(gasFeeIsInError({ send: initialState })).toBe(false);
+        expect(
+          gasFeeIsInError({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe(false);
         expect(
           gasFeeIsInError({
-            send: {
-              ...initialState,
+            send: getInitialSendStateWithExistingTxState({
               gas: {
-                ...initialState.gas,
                 error: 'yes',
               },
-            },
+            }),
           }),
         ).toBe(true);
       });
 
       it('has a selector that gets minimumGasLimit', () => {
-        expect(getMinimumGasLimitForSend({ send: initialState })).toBe(
-          GAS_LIMITS.SIMPLE,
-        );
+        expect(
+          getMinimumGasLimitForSend({
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          }),
+        ).toBe(GAS_LIMITS.SIMPLE);
       });
 
       describe('getGasInputMode selector', () => {
@@ -2473,7 +2890,7 @@ describe('Send Slice', () => {
           process.env.IN_TEST = false;
         });
 
-        it('returns CUSTOM if isCustomGasSet is true', () => {
+        it('returns CUSTOM if gasIsSetInModal is true', () => {
           expect(
             getGasInputMode({
               metamask: {
@@ -2481,11 +2898,8 @@ describe('Send Slice', () => {
                 featureFlags: { advancedInlineGas: true },
               },
               send: {
-                ...initialState,
-                gas: {
-                  ...initialState.send,
-                  isCustomGasSet: true,
-                },
+                ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+                gasIsSetInModal: true,
               },
             }),
           ).toBe(GAS_INPUT_MODES.CUSTOM);
@@ -2495,38 +2909,39 @@ describe('Send Slice', () => {
 
     describe('asset selectors', () => {
       it('has a selector to get the asset', () => {
-        expect(getSendAsset({ send: initialState })).toMatchObject(
-          initialState.asset,
+        expect(
+          getSendAsset({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toMatchObject(
+          getTestUUIDTx(INITIAL_SEND_STATE_FOR_EXISTING_DRAFT).asset,
         );
       });
 
       it('has a selector to get the asset address', () => {
         expect(
           getSendAssetAddress({
-            send: {
-              ...initialState,
+            send: getInitialSendStateWithExistingTxState({
               asset: {
                 balance: '0x0',
                 details: { address: '0x0' },
                 type: ASSET_TYPES.TOKEN,
               },
-            },
+            }),
           }),
         ).toBe('0x0');
       });
 
       it('has a selector that determines if asset is sendable based on ERC721 status', () => {
-        expect(getIsAssetSendable({ send: initialState })).toBe(true);
+        expect(
+          getIsAssetSendable({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe(true);
         expect(
           getIsAssetSendable({
-            send: {
-              ...initialState,
+            send: getInitialSendStateWithExistingTxState({
               asset: {
-                ...initialState,
                 type: ASSET_TYPES.TOKEN,
                 details: { isERC721: true },
               },
-            },
+            }),
           }),
         ).toBe(false);
       });
@@ -2534,65 +2949,77 @@ describe('Send Slice', () => {
 
     describe('amount selectors', () => {
       it('has a selector to get send amount', () => {
-        expect(getSendAmount({ send: initialState })).toBe('0x0');
+        expect(
+          getSendAmount({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe('0x0');
       });
 
       it('has a selector to get if there is an insufficient funds error', () => {
-        expect(getIsBalanceInsufficient({ send: initialState })).toBe(false);
         expect(
           getIsBalanceInsufficient({
-            send: {
-              ...initialState,
-              gas: { ...initialState.gas, error: INSUFFICIENT_FUNDS_ERROR },
-            },
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          }),
+        ).toBe(false);
+        expect(
+          getIsBalanceInsufficient({
+            send: getInitialSendStateWithExistingTxState({
+              gas: { error: INSUFFICIENT_FUNDS_ERROR },
+            }),
           }),
         ).toBe(true);
       });
 
       it('has a selector to get max mode state', () => {
-        expect(getSendMaxModeState({ send: initialState })).toBe(false);
+        expect(
+          getSendMaxModeState({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe(false);
         expect(
           getSendMaxModeState({
             send: {
-              ...initialState,
-              amount: { ...initialState.amount, mode: AMOUNT_MODES.MAX },
+              ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+              amountMode: AMOUNT_MODES.MAX,
             },
           }),
         ).toBe(true);
       });
 
       it('has a selector to get the draft transaction ID', () => {
-        expect(getDraftTransactionID({ send: initialState })).toBeNull();
         expect(
           getDraftTransactionID({
-            send: {
-              ...initialState,
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          }),
+        ).toBeNull();
+        expect(
+          getDraftTransactionID({
+            send: getInitialSendStateWithExistingTxState({
               id: 'ID',
-            },
+            }),
           }),
         ).toBe('ID');
       });
 
       it('has a selector to get the user entered hex data', () => {
-        expect(getSendHexData({ send: initialState })).toBeNull();
+        expect(
+          getSendHexData({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBeNull();
         expect(
           getSendHexData({
-            send: {
-              ...initialState,
+            send: getInitialSendStateWithExistingTxState({
               userInputHexData: '0x0',
-            },
+            }),
           }),
         ).toBe('0x0');
       });
 
       it('has a selector to get if there is an amount error', () => {
-        expect(sendAmountIsInError({ send: initialState })).toBe(false);
+        expect(
+          sendAmountIsInError({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe(false);
         expect(
           sendAmountIsInError({
-            send: {
-              ...initialState,
-              amount: { ...initialState.amount, error: 'any' },
-            },
+            send: getInitialSendStateWithExistingTxState({
+              amount: { error: 'any' },
+            }),
           }),
         ).toBe(true);
       });
@@ -2600,44 +3027,49 @@ describe('Send Slice', () => {
 
     describe('recipient selectors', () => {
       it('has a selector to get recipient address', () => {
-        expect(getSendTo({ send: initialState })).toBe('');
         expect(
           getSendTo({
-            send: {
-              ...initialState,
-              recipient: { ...initialState.recipient, address: '0xb' },
-            },
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+            metamask: { ensResolutionsByAddress: {} },
+          }),
+        ).toBe('');
+        expect(
+          getSendTo({
+            send: getInitialSendStateWithExistingTxState({
+              recipient: { address: '0xb' },
+            }),
+            metamask: { ensResolutionsByAddress: {} },
           }),
         ).toBe('0xb');
       });
 
       it('has a selector to check if using the my accounts option for recipient selection', () => {
         expect(
-          getIsUsingMyAccountForRecipientSearch({ send: initialState }),
+          getIsUsingMyAccountForRecipientSearch({
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          }),
         ).toBe(false);
         expect(
           getIsUsingMyAccountForRecipientSearch({
             send: {
-              ...initialState,
-              recipient: {
-                ...initialState.recipient,
-                mode: RECIPIENT_SEARCH_MODES.MY_ACCOUNTS,
-              },
+              ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+              recipientMode: RECIPIENT_SEARCH_MODES.MY_ACCOUNTS,
             },
           }),
         ).toBe(true);
       });
 
       it('has a selector to get recipient user input in input field', () => {
-        expect(getRecipientUserInput({ send: initialState })).toBe('');
+        expect(
+          getRecipientUserInput({
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          }),
+        ).toBe('');
         expect(
           getRecipientUserInput({
             send: {
-              ...initialState,
-              recipient: {
-                ...initialState.recipient,
-                userInput: 'domain.eth',
-              },
+              ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+              recipientInput: 'domain.eth',
             },
           }),
         ).toBe('domain.eth');
@@ -2646,42 +3078,47 @@ describe('Send Slice', () => {
       it('has a selector to get recipient state', () => {
         expect(
           getRecipient({
-            send: initialState,
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
             metamask: { ensResolutionsByAddress: {} },
           }),
-        ).toMatchObject(initialState.recipient);
+        ).toMatchObject(
+          getTestUUIDTx(INITIAL_SEND_STATE_FOR_EXISTING_DRAFT).recipient,
+        );
       });
     });
 
     describe('send validity selectors', () => {
       it('has a selector to get send errors', () => {
-        expect(getSendErrors({ send: initialState })).toMatchObject({
+        expect(
+          getSendErrors({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toMatchObject({
           gasFee: null,
           amount: null,
         });
         expect(
           getSendErrors({
-            send: {
-              ...initialState,
+            send: getInitialSendStateWithExistingTxState({
               gas: {
-                ...initialState.gas,
                 error: 'gasFeeTest',
               },
               amount: {
-                ...initialState.amount,
                 error: 'amountTest',
               },
-            },
+            }),
           }),
         ).toMatchObject({ gasFee: 'gasFeeTest', amount: 'amountTest' });
       });
 
       it('has a selector to get send state initialization status', () => {
-        expect(isSendStateInitialized({ send: initialState })).toBe(false);
+        expect(
+          isSendStateInitialized({
+            send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+          }),
+        ).toBe(false);
         expect(
           isSendStateInitialized({
             send: {
-              ...initialState,
+              ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
               stage: SEND_STATUSES.ADD_RECIPIENT,
             },
           }),
@@ -2689,19 +3126,28 @@ describe('Send Slice', () => {
       });
 
       it('has a selector to get send state validity', () => {
-        expect(isSendFormInvalid({ send: initialState })).toBe(false);
+        expect(
+          isSendFormInvalid({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe(false);
         expect(
           isSendFormInvalid({
-            send: { ...initialState, status: SEND_STATUSES.INVALID },
+            send: getInitialSendStateWithExistingTxState({
+              status: SEND_STATUSES.INVALID,
+            }),
           }),
         ).toBe(true);
       });
 
       it('has a selector to get send stage', () => {
-        expect(getSendStage({ send: initialState })).toBe(SEND_STAGES.INACTIVE);
+        expect(
+          getSendStage({ send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT }),
+        ).toBe(SEND_STAGES.INACTIVE);
         expect(
           getSendStage({
-            send: { ...initialState, stage: SEND_STAGES.ADD_RECIPIENT },
+            send: {
+              ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+              stage: SEND_STAGES.ADD_RECIPIENT,
+            },
           }),
         ).toBe(SEND_STAGES.ADD_RECIPIENT);
       });
diff --git a/ui/helpers/constants/common.js b/ui/helpers/constants/common.js
index 2663c2108..7e980f822 100644
--- a/ui/helpers/constants/common.js
+++ b/ui/helpers/constants/common.js
@@ -47,6 +47,8 @@ export const GAS_ESTIMATE_TYPES = {
 
 let _supportLink = 'https://support.metamask.io';
 let _supportRequestLink = 'https://metamask.zendesk.com/hc/en-us/requests/new';
+const _contractAddressLink =
+  'https://metamask.zendesk.com/hc/en-us/articles/360020028092-What-is-the-known-contract-address-warning-';
 
 ///: BEGIN:ONLY_INCLUDE_IN(flask)
 _supportLink = 'https://metamask-flask.zendesk.com/hc';
@@ -56,3 +58,4 @@ _supportRequestLink =
 
 export const SUPPORT_LINK = _supportLink;
 export const SUPPORT_REQUEST_LINK = _supportRequestLink;
+export const CONTRACT_ADDRESS_LINK = _contractAddressLink;
diff --git a/ui/helpers/constants/routes.js b/ui/helpers/constants/routes.js
index 9f5f028e1..fa816e412 100644
--- a/ui/helpers/constants/routes.js
+++ b/ui/helpers/constants/routes.js
@@ -12,6 +12,8 @@ const ALERTS_ROUTE = '/settings/alerts';
 const NETWORKS_ROUTE = '/settings/networks';
 const NETWORKS_FORM_ROUTE = '/settings/networks/form';
 const ADD_NETWORK_ROUTE = '/settings/networks/add-network';
+const ADD_POPULAR_CUSTOM_NETWORK =
+  '/settings/networks/add-popular-custom-network';
 const SNAPS_LIST_ROUTE = '/settings/snaps-list';
 const SNAPS_VIEW_ROUTE = '/settings/snaps-view';
 const CONTACT_LIST_ROUTE = '/settings/contact-list';
@@ -88,6 +90,7 @@ const CONFIRM_SEND_ETHER_PATH = '/send-ether';
 const CONFIRM_SEND_TOKEN_PATH = '/send-token';
 const CONFIRM_DEPLOY_CONTRACT_PATH = '/deploy-contract';
 const CONFIRM_APPROVE_PATH = '/approve';
+const CONFIRM_SET_APPROVAL_FOR_ALL_PATH = '/set-approval-for-all';
 const CONFIRM_TRANSFER_FROM_PATH = '/transfer-from';
 const CONFIRM_SAFE_TRANSFER_FROM_PATH = '/safe-transfer-from';
 const CONFIRM_TOKEN_METHOD_PATH = '/token-method';
@@ -113,6 +116,8 @@ const PATH_NAME_MAP = {
   [NETWORKS_ROUTE]: 'Network Settings Page',
   [NETWORKS_FORM_ROUTE]: 'Network Settings Page Form',
   [ADD_NETWORK_ROUTE]: 'Add Network From Settings Page Form',
+  [ADD_POPULAR_CUSTOM_NETWORK]:
+    'Add Network From A List Of Popular Custom Networks',
   [CONTACT_LIST_ROUTE]: 'Contact List Settings Page',
   [`${CONTACT_EDIT_ROUTE}/:address`]: 'Edit Contact Settings Page',
   [CONTACT_ADD_ROUTE]: 'Add Contact Settings Page',
@@ -141,6 +146,7 @@ const PATH_NAME_MAP = {
   [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_SEND_TOKEN_PATH}`]: 'Confirm Send Token Transaction Page',
   [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_DEPLOY_CONTRACT_PATH}`]: 'Confirm Deploy Contract Transaction Page',
   [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_APPROVE_PATH}`]: 'Confirm Approve Transaction Page',
+  [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_SET_APPROVAL_FOR_ALL_PATH}`]: 'Confirm Set Approval For All Transaction Page',
   [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_TRANSFER_FROM_PATH}`]: 'Confirm Transfer From Transaction Page',
   [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_SAFE_TRANSFER_FROM_PATH}`]: 'Confirm Safe Transfer From Transaction Page',
   [`${CONFIRM_TRANSACTION_ROUTE}/:id${SIGNATURE_REQUEST_PATH}`]: 'Signature Request Page',
@@ -202,6 +208,7 @@ export {
   CONFIRM_SEND_TOKEN_PATH,
   CONFIRM_DEPLOY_CONTRACT_PATH,
   CONFIRM_APPROVE_PATH,
+  CONFIRM_SET_APPROVAL_FOR_ALL_PATH,
   CONFIRM_TRANSFER_FROM_PATH,
   CONFIRM_SAFE_TRANSFER_FROM_PATH,
   CONFIRM_TOKEN_METHOD_PATH,
@@ -224,6 +231,7 @@ export {
   NETWORKS_ROUTE,
   NETWORKS_FORM_ROUTE,
   ADD_NETWORK_ROUTE,
+  ADD_POPULAR_CUSTOM_NETWORK,
   INITIALIZE_BACKUP_SEED_PHRASE_ROUTE,
   INITIALIZE_SEED_PHRASE_INTRO_ROUTE,
   CONNECT_ROUTE,
diff --git a/ui/helpers/constants/settings.js b/ui/helpers/constants/settings.js
index 3f05221e4..3148e7543 100644
--- a/ui/helpers/constants/settings.js
+++ b/ui/helpers/constants/settings.js
@@ -346,4 +346,11 @@ export const SETTINGS_CONSTANTS = [
     icon: 'fa fa-flask',
     featureFlag: 'COLLECTIBLES_V1',
   },
+  {
+    tabMessage: (t) => t('experimental'),
+    sectionMessage: (t) => t('showCustomNetworkList'),
+    descriptionMessage: (t) => t('showCustomNetworkListDescription'),
+    route: `${EXPERIMENTAL_ROUTE}#show-custom-network`,
+    icon: 'fa fa-flask',
+  },
 ];
diff --git a/ui/helpers/constants/transactions.js b/ui/helpers/constants/transactions.js
index b7f932054..4dc66ab79 100644
--- a/ui/helpers/constants/transactions.js
+++ b/ui/helpers/constants/transactions.js
@@ -17,6 +17,7 @@ export const PRIORITY_STATUS_HASH = {
 
 export const TOKEN_CATEGORY_HASH = {
   [TRANSACTION_TYPES.TOKEN_METHOD_APPROVE]: true,
+  [TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL]: true,
   [TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER]: true,
   [TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM]: true,
 };
diff --git a/ui/helpers/utils/settings-search.test.js b/ui/helpers/utils/settings-search.test.js
index 3ef7825d9..9f0264e1d 100644
--- a/ui/helpers/utils/settings-search.test.js
+++ b/ui/helpers/utils/settings-search.test.js
@@ -195,7 +195,7 @@ describe('Settings Search Utils', () => {
 
     it('should get good experimental section number', () => {
       expect(getNumberOfSettingsInSection(t, t('experimental'))).toStrictEqual(
-        3,
+        4,
       );
     });
 
diff --git a/ui/helpers/utils/token-util.js b/ui/helpers/utils/token-util.js
index b934ed176..784ceb772 100644
--- a/ui/helpers/utils/token-util.js
+++ b/ui/helpers/utils/token-util.js
@@ -43,7 +43,7 @@ async function getDecimalsFromContract(tokenAddress) {
   }
 }
 
-function getTokenMetadata(tokenAddress, tokenList) {
+export function getTokenMetadata(tokenAddress, tokenList) {
   const casedTokenList = Object.keys(tokenList).reduce((acc, base) => {
     return {
       ...acc,
@@ -151,6 +151,10 @@ export function getTokenValueParam(tokenData = {}) {
   return tokenData?.args?._value?.toString();
 }
 
+export function getTokenApprovedParam(tokenData = {}) {
+  return tokenData?.args?._approved;
+}
+
 export function getTokenValue(tokenParams = []) {
   const valueData = tokenParams.find((param) => param.name === '_value');
   return valueData && valueData.value;
diff --git a/ui/helpers/utils/transactions.util.js b/ui/helpers/utils/transactions.util.js
index d1380d8c0..07d37341d 100644
--- a/ui/helpers/utils/transactions.util.js
+++ b/ui/helpers/utils/transactions.util.js
@@ -116,6 +116,7 @@ export function isTokenMethodAction(type) {
   return [
     TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER,
     TRANSACTION_TYPES.TOKEN_METHOD_APPROVE,
+    TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL,
     TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM,
     TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM,
   ].includes(type);
@@ -217,6 +218,9 @@ export function getTransactionTypeTitle(t, type, nativeCurrency = 'ETH') {
     case TRANSACTION_TYPES.TOKEN_METHOD_APPROVE: {
       return t('approve');
     }
+    case TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL: {
+      return t('setApprovalForAll');
+    }
     case TRANSACTION_TYPES.SIMPLE_SEND: {
       return t('sendingNativeAsset', [nativeCurrency]);
     }
diff --git a/ui/hooks/useTransactionDisplayData.js b/ui/hooks/useTransactionDisplayData.js
index 184374496..9c6a13768 100644
--- a/ui/hooks/useTransactionDisplayData.js
+++ b/ui/hooks/useTransactionDisplayData.js
@@ -222,6 +222,12 @@ export function useTransactionDisplayData(transactionGroup) {
     title = t('approveSpendLimit', [token?.symbol || t('token')]);
     subtitle = origin;
     subtitleContainsOrigin = true;
+  } else if (type === TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL) {
+    category = TRANSACTION_GROUP_CATEGORIES.APPROVAL;
+    prefix = '';
+    title = t('setApprovalForAllTitle', [token?.symbol || t('token')]);
+    subtitle = origin;
+    subtitleContainsOrigin = true;
   } else if (type === TRANSACTION_TYPES.CONTRACT_INTERACTION) {
     category = TRANSACTION_GROUP_CATEGORIES.INTERACTION;
     const transactionTypeTitle = getTransactionTypeTitle(t, type);
diff --git a/ui/pages/add-collectible/add-collectible.js b/ui/pages/add-collectible/add-collectible.js
index 7d9112a92..758e03dc7 100644
--- a/ui/pages/add-collectible/add-collectible.js
+++ b/ui/pages/add-collectible/add-collectible.js
@@ -4,7 +4,6 @@ import { useDispatch, useSelector } from 'react-redux';
 import { util } from '@metamask/controllers';
 import { useI18nContext } from '../../hooks/useI18nContext';
 import { DEFAULT_ROUTE } from '../../helpers/constants/routes';
-
 import {
   DISPLAY,
   FONT_WEIGHT,
@@ -55,9 +54,7 @@ export default function AddCollectible() {
 
   const handleAddCollectible = async () => {
     try {
-      await dispatch(
-        addCollectibleVerifyOwnership(address, tokenId.toString()),
-      );
+      await dispatch(addCollectibleVerifyOwnership(address, tokenId));
     } catch (error) {
       const { message } = error;
       dispatch(setNewCollectibleAddedMessage(message));
@@ -99,7 +96,7 @@ export default function AddCollectible() {
   };
 
   const validateAndSetTokenId = (val) => {
-    setDisabled(!util.isValidHexAddress(address) || !val);
+    setDisabled(!util.isValidHexAddress(address) || !val || isNaN(Number(val)));
     setTokenId(val);
   };
 
@@ -149,7 +146,7 @@ export default function AddCollectible() {
           )}
           <Box margin={4}>
             <FormField
-              id="address"
+              dataTestId="address"
               titleText={t('address')}
               placeholder="0x..."
               value={address}
@@ -161,7 +158,7 @@ export default function AddCollectible() {
               autoFocus
             />
             <FormField
-              id="token-id"
+              dataTestId="token-id"
               titleText={t('tokenId')}
               placeholder={t('nftTokenIdPlaceholder')}
               value={tokenId}
@@ -170,7 +167,6 @@ export default function AddCollectible() {
                 setCollectibleAddFailed(false);
               }}
               tooltipText={t('importNFTTokenIdToolTip')}
-              numeric
             />
           </Box>
         </Box>
diff --git a/ui/pages/add-collectible/add-collectible.test.js b/ui/pages/add-collectible/add-collectible.test.js
new file mode 100644
index 000000000..be693e444
--- /dev/null
+++ b/ui/pages/add-collectible/add-collectible.test.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import { fireEvent } from '@testing-library/react';
+import configureMockStore from 'redux-mock-store';
+import { renderWithProvider } from '../../../test/jest/rendering';
+import * as Actions from '../../store/actions';
+import AddCollectible from '.';
+
+const VALID_ADDRESS = '0x312BE6a98441F9F6e3F6246B13CA19701e0AC3B9';
+const INVALID_ADDRESS = 'aoinsafasdfa';
+const VALID_TOKENID = '1201';
+const INVALID_TOKENID = 'abcde';
+
+describe('AddCollectible', () => {
+  const store = configureMockStore([])({
+    metamask: { provider: { chainId: '0x1' } },
+  });
+
+  it('should enable the "Add" button when valid entries are input into both Address and TokenId fields', () => {
+    const { getByTestId, getByText } = renderWithProvider(
+      <AddCollectible />,
+      store,
+    );
+    expect(getByText('Add')).not.toBeEnabled();
+    fireEvent.change(getByTestId('address'), {
+      target: { value: VALID_ADDRESS },
+    });
+    fireEvent.change(getByTestId('token-id'), {
+      target: { value: VALID_TOKENID },
+    });
+    expect(getByText('Add')).toBeEnabled();
+  });
+
+  it('should not enable the "Add" button when an invalid entry is input into one or both Address and TokenId fields', () => {
+    const { getByTestId, getByText } = renderWithProvider(
+      <AddCollectible />,
+      store,
+    );
+    expect(getByText('Add')).not.toBeEnabled();
+    fireEvent.change(getByTestId('address'), {
+      target: { value: INVALID_ADDRESS },
+    });
+    fireEvent.change(getByTestId('token-id'), {
+      target: { value: VALID_TOKENID },
+    });
+    expect(getByText('Add')).not.toBeEnabled();
+    fireEvent.change(getByTestId('address'), {
+      target: { value: VALID_ADDRESS },
+    });
+    expect(getByText('Add')).toBeEnabled();
+    fireEvent.change(getByTestId('token-id'), {
+      target: { value: INVALID_TOKENID },
+    });
+    expect(getByText('Add')).not.toBeEnabled();
+  });
+
+  it('should call addCollectibleVerifyOwnership action with correct values (tokenId should not be in scientific notation)', () => {
+    const { getByTestId, getByText } = renderWithProvider(
+      <AddCollectible />,
+      store,
+    );
+    fireEvent.change(getByTestId('address'), {
+      target: { value: VALID_ADDRESS },
+    });
+    const LARGE_TOKEN_ID = Number.MAX_SAFE_INTEGER + 1;
+    fireEvent.change(getByTestId('token-id'), {
+      target: { value: LARGE_TOKEN_ID },
+    });
+    const addCollectibleVerifyOwnershipSpy = jest.spyOn(
+      Actions,
+      'addCollectibleVerifyOwnership',
+    );
+
+    fireEvent.click(getByText('Add'));
+    expect(addCollectibleVerifyOwnershipSpy).toHaveBeenCalledWith(
+      '0x312BE6a98441F9F6e3F6246B13CA19701e0AC3B9',
+      '9007199254740992',
+    );
+  });
+});
diff --git a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js
index 7a6d32cce..b87c405b9 100644
--- a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js
+++ b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js
@@ -71,6 +71,9 @@ export default class ConfirmApproveContent extends Component {
     assetName: PropTypes.string,
     tokenId: PropTypes.string,
     assetStandard: PropTypes.string,
+    isSetApproveForAll: PropTypes.bool,
+    setApproveForAllArg: PropTypes.bool,
+    userAddress: PropTypes.string,
   };
 
   state = {
@@ -100,7 +103,7 @@ export default class ConfirmApproveContent extends Component {
       >
         {showHeader && (
           <div className="confirm-approve-content__card-header">
-            {!supportsEIP1559V2 && (
+            {supportsEIP1559V2 && title === t('transactionFee') ? null : (
               <>
                 <div className="confirm-approve-content__card-header__symbol">
                   {symbol}
@@ -184,7 +187,7 @@ export default class ConfirmApproveContent extends Component {
 
   renderERC721OrERC1155PermissionContent() {
     const { t } = this.context;
-    const { origin, toAddress, isContract } = this.props;
+    const { origin, toAddress, isContract, isSetApproveForAll } = this.props;
 
     const titleTokenDescription = this.getTitleTokenDescription();
 
@@ -201,7 +204,9 @@ export default class ConfirmApproveContent extends Component {
             {t('approvedAsset')}:
           </div>
           <div className="confirm-approve-content__medium-text">
-            {titleTokenDescription}
+            {isSetApproveForAll
+              ? t('allOfYour', [titleTokenDescription])
+              : titleTokenDescription}
           </div>
         </div>
         <div className="flex-row">
@@ -299,12 +304,19 @@ export default class ConfirmApproveContent extends Component {
 
   renderDataContent() {
     const { t } = this.context;
-    const { data } = this.props;
+    const { data, isSetApproveForAll, setApproveForAllArg } = this.props;
     return (
       <div className="flex-column">
         <div className="confirm-approve-content__small-text">
-          {t('functionApprove')}
+          {isSetApproveForAll
+            ? t('functionSetApprovalForAll')
+            : t('functionApprove')}
         </div>
+        {isSetApproveForAll && setApproveForAllArg !== undefined ? (
+          <div className="confirm-approve-content__small-text">
+            {`${t('parameters')}: ${setApproveForAllArg}`}
+          </div>
+        ) : null}
         <div className="confirm-approve-content__small-text confirm-approve-content__data__data-block">
           {data}
         </div>
@@ -442,6 +454,8 @@ export default class ConfirmApproveContent extends Component {
       chainId,
       assetStandard,
       tokenSymbol,
+      isSetApproveForAll,
+      userAddress,
     } = this.props;
     const { t } = this.context;
     let titleTokenDescription = t('token');
@@ -450,6 +464,7 @@ export default class ConfirmApproveContent extends Component {
         tokenAddress,
         chainId,
         null,
+        userAddress,
         {
           blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null,
         },
@@ -468,7 +483,10 @@ export default class ConfirmApproveContent extends Component {
       titleTokenDescription = unknownTokenLink;
     }
 
-    if (assetStandard === ERC20 || (tokenSymbol && !tokenId)) {
+    if (
+      assetStandard === ERC20 ||
+      (tokenSymbol && !tokenId && !isSetApproveForAll)
+    ) {
       titleTokenDescription = tokenSymbol;
     } else if (
       assetStandard === ERC721 ||
@@ -477,14 +495,15 @@ export default class ConfirmApproveContent extends Component {
       (assetName && tokenId) ||
       (tokenSymbol && tokenId)
     ) {
-      const tokenIdWrapped = tokenId ? ` (#${tokenId})` : null;
+      const tokenIdWrapped = tokenId ? ` (#${tokenId})` : '';
       if (assetName || tokenSymbol) {
-        titleTokenDescription = `${assetName ?? tokenSymbol} ${tokenIdWrapped}`;
+        titleTokenDescription = `${assetName ?? tokenSymbol}${tokenIdWrapped}`;
       } else {
         const unknownNFTBlockExplorerLink = getTokenTrackerLink(
           tokenAddress,
           chainId,
           null,
+          userAddress,
           {
             blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null,
           },
@@ -509,6 +528,41 @@ export default class ConfirmApproveContent extends Component {
     return titleTokenDescription;
   }
 
+  renderTitle() {
+    const { t } = this.context;
+    const { isSetApproveForAll, setApproveForAllArg } = this.props;
+    const titleTokenDescription = this.getTitleTokenDescription();
+
+    let title;
+
+    if (isSetApproveForAll) {
+      title = t('approveAllTokensTitle', [titleTokenDescription]);
+      if (setApproveForAllArg === false) {
+        title = t('revokeAllTokensTitle', [titleTokenDescription]);
+      }
+    }
+    return title || t('allowSpendToken', [titleTokenDescription]);
+  }
+
+  renderDescription() {
+    const { t } = this.context;
+    const { isContract, isSetApproveForAll, setApproveForAllArg } = this.props;
+    const grantee = isContract
+      ? t('contract').toLowerCase()
+      : t('account').toLowerCase();
+
+    let description = t('trustSiteApprovePermission', [grantee]);
+
+    if (isSetApproveForAll && setApproveForAllArg === false) {
+      description = t('revokeApproveForAllDescription', [
+        grantee,
+        this.getTitleTokenDescription(),
+      ]);
+    }
+
+    return description;
+  }
+
   render() {
     const { t } = this.context;
     const {
@@ -531,11 +585,10 @@ export default class ConfirmApproveContent extends Component {
       rpcPrefs,
       isContract,
       assetStandard,
+      userAddress,
     } = this.props;
     const { showFullTxDetails } = this.state;
 
-    const titleTokenDescription = this.getTitleTokenDescription();
-
     return (
       <div
         className={classnames('confirm-approve-content', {
@@ -575,14 +628,10 @@ export default class ConfirmApproveContent extends Component {
           </Box>
         </Box>
         <div className="confirm-approve-content__title">
-          {t('allowSpendToken', [titleTokenDescription])}
+          {this.renderTitle()}
         </div>
         <div className="confirm-approve-content__description">
-          {t('trustSiteApprovePermission', [
-            isContract
-              ? t('contract').toLowerCase()
-              : t('account').toLowerCase(),
-          ])}
+          {this.renderDescription()}
         </div>
         <Box className="confirm-approve-content__address-display-content">
           <Box display={DISPLAY.FLEX}>
@@ -623,7 +672,7 @@ export default class ConfirmApproveContent extends Component {
               className="confirm-approve-content__etherscan-link"
               onClick={() => {
                 const blockExplorerTokenLink = isContract
-                  ? getTokenTrackerLink(toAddress, chainId, null, null, {
+                  ? getTokenTrackerLink(toAddress, chainId, null, userAddress, {
                       blockExplorerUrl: rpcPrefs?.blockExplorerUrl ?? null,
                     })
                   : getAccountLink(
diff --git a/ui/pages/confirm-approve/confirm-approve.js b/ui/pages/confirm-approve/confirm-approve.js
index 6dca97d1b..072a4d330 100644
--- a/ui/pages/confirm-approve/confirm-approve.js
+++ b/ui/pages/confirm-approve/confirm-approve.js
@@ -8,7 +8,10 @@ import {
   updateCustomNonce,
   getNextNonce,
 } from '../../store/actions';
-import { calcTokenAmount } from '../../helpers/utils/token-util';
+import {
+  calcTokenAmount,
+  getTokenApprovedParam,
+} from '../../helpers/utils/token-util';
 import { readAddressAsContract } from '../../../shared/modules/contract-utils';
 import { GasFeeContextProvider } from '../../contexts/gasFee';
 import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
@@ -34,6 +37,7 @@ import EditGasFeePopover from '../../components/app/edit-gas-fee-popover';
 import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component';
 import Loading from '../../components/ui/loading-screen';
 import { ERC20, ERC1155, ERC721 } from '../../helpers/constants/common';
+import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils';
 import { getCustomTxParamsData } from './confirm-approve.util';
 import ConfirmApproveContent from './confirm-approve-content';
 
@@ -57,6 +61,7 @@ export default function ConfirmApprove({
   ethTransactionTotal,
   fiatTransactionTotal,
   hexTransactionTotal,
+  isSetApproveForAll,
 }) {
   const dispatch = useDispatch();
   const { txParams: { data: transactionData } = {} } = transaction;
@@ -150,6 +155,11 @@ export default function ConfirmApprove({
       })
     : null;
 
+  const parsedTransactionData = parseStandardTokenTransactionData(
+    transactionData,
+  );
+  const setApproveForAllArg = getTokenApprovedParam(parsedTransactionData);
+
   return tokenSymbol === undefined && assetName === undefined ? (
     <Loading />
   ) : (
@@ -162,6 +172,9 @@ export default function ConfirmApprove({
         contentComponent={
           <TransactionModalContextProvider>
             <ConfirmApproveContent
+              userAddress={userAddress}
+              isSetApproveForAll={isSetApproveForAll}
+              setApproveForAllArg={setApproveForAllArg}
               decimals={decimals}
               siteImage={siteImage}
               setCustomAmount={setCustomPermissionAmount}
@@ -290,4 +303,5 @@ ConfirmApprove.propTypes = {
   ethTransactionTotal: PropTypes.string,
   fiatTransactionTotal: PropTypes.string,
   hexTransactionTotal: PropTypes.string,
+  isSetApproveForAll: PropTypes.bool,
 };
diff --git a/ui/pages/confirm-send-ether/confirm-send-ether.container.js b/ui/pages/confirm-send-ether/confirm-send-ether.container.js
index eb794b6ce..80318ea46 100644
--- a/ui/pages/confirm-send-ether/confirm-send-ether.container.js
+++ b/ui/pages/confirm-send-ether/confirm-send-ether.container.js
@@ -1,7 +1,7 @@
 import { connect } from 'react-redux';
 import { compose } from 'redux';
 import { withRouter } from 'react-router-dom';
-import { editTransaction } from '../../ducks/send';
+import { editExistingTransaction } from '../../ducks/send';
 import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
 import { ASSET_TYPES } from '../../../shared/constants/transaction';
 import ConfirmSendEther from './confirm-send-ether.component';
@@ -20,7 +20,9 @@ const mapDispatchToProps = (dispatch) => {
   return {
     editTransaction: async (txData) => {
       const { id } = txData;
-      await dispatch(editTransaction(ASSET_TYPES.NATIVE, id.toString()));
+      await dispatch(
+        editExistingTransaction(ASSET_TYPES.NATIVE, id.toString()),
+      );
       dispatch(clearConfirmTransaction());
     },
   };
diff --git a/ui/pages/confirm-send-token/confirm-send-token.component.js b/ui/pages/confirm-send-token/confirm-send-token.component.js
index 6e040fb06..774fbf670 100644
--- a/ui/pages/confirm-send-token/confirm-send-token.component.js
+++ b/ui/pages/confirm-send-token/confirm-send-token.component.js
@@ -6,14 +6,15 @@ import { SEND_ROUTE } from '../../helpers/constants/routes';
 export default class ConfirmSendToken extends Component {
   static propTypes = {
     history: PropTypes.object,
-    editTransaction: PropTypes.func,
+    editExistingTransaction: PropTypes.func,
     tokenAmount: PropTypes.string,
   };
 
   handleEdit(confirmTransactionData) {
-    const { editTransaction, history } = this.props;
-    editTransaction(confirmTransactionData);
-    history.push(SEND_ROUTE);
+    const { editExistingTransaction, history } = this.props;
+    editExistingTransaction(confirmTransactionData).then(() => {
+      history.push(SEND_ROUTE);
+    });
   }
 
   render() {
diff --git a/ui/pages/confirm-send-token/confirm-send-token.container.js b/ui/pages/confirm-send-token/confirm-send-token.container.js
index d8a498424..62cee9ac3 100644
--- a/ui/pages/confirm-send-token/confirm-send-token.container.js
+++ b/ui/pages/confirm-send-token/confirm-send-token.container.js
@@ -3,7 +3,7 @@ import { compose } from 'redux';
 import { withRouter } from 'react-router-dom';
 import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
 import { showSendTokenPage } from '../../store/actions';
-import { editTransaction } from '../../ducks/send';
+import { editExistingTransaction } from '../../ducks/send';
 import { sendTokenTokenAmountAndToAddressSelector } from '../../selectors';
 import { ASSET_TYPES } from '../../../shared/constants/transaction';
 import ConfirmSendToken from './confirm-send-token.component';
@@ -18,18 +18,11 @@ const mapStateToProps = (state) => {
 
 const mapDispatchToProps = (dispatch) => {
   return {
-    editTransaction: ({ txData, tokenData, tokenProps: assetDetails }) => {
+    editExistingTransaction: async ({ txData }) => {
       const { id } = txData;
-      dispatch(
-        editTransaction(
-          ASSET_TYPES.TOKEN,
-          id.toString(),
-          tokenData,
-          assetDetails,
-        ),
-      );
-      dispatch(clearConfirmTransaction());
-      dispatch(showSendTokenPage());
+      await dispatch(editExistingTransaction(ASSET_TYPES.TOKEN, id.toString()));
+      await dispatch(clearConfirmTransaction());
+      await dispatch(showSendTokenPage());
     },
   };
 };
diff --git a/ui/pages/confirm-send-token/confirm-send-token.js b/ui/pages/confirm-send-token/confirm-send-token.js
index 8d40d36c9..a7afb7af4 100644
--- a/ui/pages/confirm-send-token/confirm-send-token.js
+++ b/ui/pages/confirm-send-token/confirm-send-token.js
@@ -4,7 +4,7 @@ import { useDispatch, useSelector } from 'react-redux';
 import { useHistory } from 'react-router-dom';
 import ConfirmTokenTransactionBase from '../confirm-token-transaction-base/confirm-token-transaction-base';
 import { SEND_ROUTE } from '../../helpers/constants/routes';
-import { editTransaction } from '../../ducks/send';
+import { editExistingTransaction } from '../../ducks/send';
 import {
   contractExchangeRateSelector,
   getCurrentCurrency,
@@ -35,27 +35,17 @@ export default function ConfirmSendToken({
   const dispatch = useDispatch();
   const history = useHistory();
 
-  const handleEditTransaction = ({
-    txData,
-    tokenData,
-    tokenProps: assetDetails,
-  }) => {
+  const handleEditTransaction = async ({ txData }) => {
     const { id } = txData;
-    dispatch(
-      editTransaction(
-        ASSET_TYPES.TOKEN,
-        id.toString(),
-        tokenData,
-        assetDetails,
-      ),
-    );
+    await dispatch(editExistingTransaction(ASSET_TYPES.TOKEN, id.toString()));
     dispatch(clearConfirmTransaction());
     dispatch(showSendTokenPage());
   };
 
   const handleEdit = (confirmTransactionData) => {
-    handleEditTransaction(confirmTransactionData);
-    history.push(SEND_ROUTE);
+    handleEditTransaction(confirmTransactionData).then(() => {
+      history.push(SEND_ROUTE);
+    });
   };
   const conversionRate = useSelector(getConversionRate);
   const nativeCurrency = useSelector(getNativeCurrency);
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 c48e0d256..a5435e42b 100644
--- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -338,6 +338,7 @@ export default class ConfirmTransactionBase extends Component {
     };
 
     const hasSimulationError = Boolean(txData.simulationFails);
+
     const renderSimulationFailureWarning =
       hasSimulationError && !userAcknowledgedGasMissing;
     const networkName = NETWORK_TO_NAME_MAP[txData.chainId];
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 e36049c80..7321d2793 100644
--- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js
+++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js
@@ -54,6 +54,7 @@ import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
 import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app';
 import { isLegacyTransaction } from '../../helpers/utils/transactions.util';
 import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas';
+import { TRANSACTION_TYPES } from '../../../shared/constants/transaction';
 import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
 import { getTokenAddressParam } from '../../helpers/utils/token-util';
 import ConfirmTransactionBase from './confirm-transaction-base.component';
@@ -112,7 +113,10 @@ const mapStateToProps = (state, ownProps) => {
 
   const { balance } = accounts[fromAddress];
   const { name: fromName } = identities[fromAddress];
-  const toAddress = propsToAddress || tokenToAddress || txParamsToAddress;
+  let toAddress = txParamsToAddress;
+  if (type !== TRANSACTION_TYPES.SIMPLE_SEND) {
+    toAddress = propsToAddress || tokenToAddress || txParamsToAddress;
+  }
 
   const tokenList = getTokenList(state);
   const useTokenDetection = getUseTokenDetection(state);
diff --git a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js
index db58cc640..4c6f964fa 100644
--- a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js
+++ b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js
@@ -14,6 +14,7 @@ import {
   DECRYPT_MESSAGE_REQUEST_PATH,
   ENCRYPTION_PUBLIC_KEY_REQUEST_PATH,
   CONFIRM_SAFE_TRANSFER_FROM_PATH,
+  CONFIRM_SET_APPROVAL_FOR_ALL_PATH,
 } from '../../helpers/constants/routes';
 import { MESSAGE_TYPE } from '../../../shared/constants/app';
 import { TRANSACTION_TYPES } from '../../../shared/constants/transaction';
@@ -47,6 +48,10 @@ export default class ConfirmTransactionSwitch extends Component {
           const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_APPROVE_PATH}`;
           return <Redirect to={{ pathname }} />;
         }
+        case TRANSACTION_TYPES.TOKEN_METHOD_SET_APPROVAL_FOR_ALL: {
+          const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_SET_APPROVAL_FOR_ALL_PATH}`;
+          return <Redirect to={{ pathname }} />;
+        }
         case TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM: {
           const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TRANSFER_FROM_PATH}`;
           return <Redirect to={{ pathname }} />;
diff --git a/ui/pages/confirm-transaction/confirm-token-transaction-switch.js b/ui/pages/confirm-transaction/confirm-token-transaction-switch.js
index 036f2b149..43b554595 100644
--- a/ui/pages/confirm-transaction/confirm-token-transaction-switch.js
+++ b/ui/pages/confirm-transaction/confirm-token-transaction-switch.js
@@ -6,6 +6,7 @@ import {
   CONFIRM_APPROVE_PATH,
   CONFIRM_SAFE_TRANSFER_FROM_PATH,
   CONFIRM_SEND_TOKEN_PATH,
+  CONFIRM_SET_APPROVAL_FOR_ALL_PATH,
   CONFIRM_TRANSACTION_ROUTE,
   CONFIRM_TRANSFER_FROM_PATH,
 } from '../../helpers/constants/routes';
@@ -66,6 +67,30 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) {
           />
         )}
       />
+      <Route
+        exact
+        path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_SET_APPROVAL_FOR_ALL_PATH}`}
+        render={() => (
+          <ConfirmApprove
+            isSetApproveForAll
+            assetStandard={assetStandard}
+            assetName={assetName}
+            userBalance={userBalance}
+            tokenSymbol={tokenSymbol}
+            decimals={decimals}
+            tokenImage={tokenImage}
+            tokenAmount={tokenAmount}
+            tokenId={tokenId}
+            userAddress={userAddress}
+            tokenAddress={tokenAddress}
+            toAddress={toAddress}
+            transaction={transaction}
+            ethTransactionTotal={ethTransactionTotal}
+            fiatTransactionTotal={fiatTransactionTotal}
+            hexTransactionTotal={hexTransactionTotal}
+          />
+        )}
+      />
       <Route
         exact
         path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_TRANSFER_FROM_PATH}`}
diff --git a/ui/pages/confirmation/confirmation.js b/ui/pages/confirmation/confirmation.js
index b824155bc..9ccbda847 100644
--- a/ui/pages/confirmation/confirmation.js
+++ b/ui/pages/confirmation/confirmation.js
@@ -25,6 +25,7 @@ import { getUnapprovedTemplatedConfirmations } from '../../selectors';
 import NetworkDisplay from '../../components/app/network-display/network-display';
 import Callout from '../../components/ui/callout';
 import SiteOrigin from '../../components/ui/site-origin';
+import { addCustomNetwork } from '../../store/actions';
 import ConfirmationFooter from './components/confirmation-footer';
 import { getTemplateValues, getTemplateAlerts } from './templates';
 
@@ -130,6 +131,7 @@ export default function ConfirmationPage() {
   const pendingConfirmation = pendingConfirmations[currentPendingConfirmation];
   const originMetadata = useOriginMetadata(pendingConfirmation?.origin) || {};
   const [alertState, dismissAlert] = useAlertState(pendingConfirmation);
+  const [stayOnPage, setStayOnPage] = useState(false);
 
   // Generating templatedValues is potentially expensive, and if done on every render
   // will result in a new object. Avoiding calling this generation unnecessarily will
@@ -146,11 +148,11 @@ export default function ConfirmationPage() {
     // confirmations reduces to a number that is less than the currently
     // viewed index, reset the index.
     if (pendingConfirmations.length === 0) {
-      history.push(DEFAULT_ROUTE);
+      !stayOnPage && history.push(DEFAULT_ROUTE);
     } else if (pendingConfirmations.length <= currentPendingConfirmation) {
       setCurrentPendingConfirmation(pendingConfirmations.length - 1);
     }
-  }, [pendingConfirmations, history, currentPendingConfirmation]);
+  }, [pendingConfirmations, history, currentPendingConfirmation, stayOnPage]);
   if (!pendingConfirmation) {
     return null;
   }
@@ -197,23 +199,25 @@ export default function ConfirmationPage() {
             />
           </Box>
         ) : null}
-        <Box
-          alignItems="center"
-          marginTop={1}
-          padding={[1, 4, 4]}
-          flexDirection={FLEX_DIRECTION.COLUMN}
-        >
-          <SiteIcon
-            icon={originMetadata.iconUrl}
-            name={originMetadata.hostname}
-            size={36}
-          />
-          <SiteOrigin
-            chip
-            siteOrigin={stripHttpsScheme(originMetadata.origin)}
-            title={stripHttpsScheme(originMetadata.origin)}
-          />
-        </Box>
+        {pendingConfirmation.origin === 'metamask' ? null : (
+          <Box
+            alignItems="center"
+            marginTop={1}
+            padding={[1, 4, 4]}
+            flexDirection={FLEX_DIRECTION.COLUMN}
+          >
+            <SiteIcon
+              icon={originMetadata.iconUrl}
+              name={originMetadata.hostname}
+              size={36}
+            />
+            <SiteOrigin
+              chip
+              siteOrigin={stripHttpsScheme(originMetadata.origin)}
+              title={stripHttpsScheme(originMetadata.origin)}
+            />
+          </Box>
+        )}
         <MetaMaskTemplateRenderer sections={templatedValues.content} />
       </div>
       <ConfirmationFooter
@@ -234,8 +238,15 @@ export default function ConfirmationPage() {
               </Callout>
             ))
         }
-        onApprove={templatedValues.onApprove}
-        onCancel={templatedValues.onCancel}
+        onApprove={() => {
+          templatedValues.onApprove.apply();
+          pendingConfirmation.origin === 'metamask' &&
+            dispatch(addCustomNetwork(pendingConfirmation.requestData));
+        }}
+        onCancel={() => {
+          templatedValues.onCancel.apply();
+          pendingConfirmation.origin === 'metamask' && setStayOnPage(true);
+        }}
         approveText={templatedValues.approvalText}
         cancelText={templatedValues.cancelText}
       />
diff --git a/ui/pages/confirmation/templates/add-ethereum-chain.js b/ui/pages/confirmation/templates/add-ethereum-chain.js
index c417a1111..5e4594eab 100644
--- a/ui/pages/confirmation/templates/add-ethereum-chain.js
+++ b/ui/pages/confirmation/templates/add-ethereum-chain.js
@@ -1,7 +1,12 @@
 import { ethErrors } from 'eth-rpc-errors';
+import React from 'react';
 import {
   SEVERITIES,
   TYPOGRAPHY,
+  TEXT_ALIGN,
+  JUSTIFY_CONTENT,
+  DISPLAY,
+  COLORS,
 } from '../../../helpers/constants/design-system';
 import fetchWithCache from '../../../helpers/utils/fetch-with-cache';
 
@@ -79,6 +84,11 @@ async function getAlerts(pendingApproval) {
   );
   let validated = Boolean(matchedChain);
 
+  const originIsMetaMask = pendingApproval.origin === 'metamask';
+  if (originIsMetaMask && validated) {
+    return [];
+  }
+
   if (matchedChain) {
     if (
       matchedChain.nativeCurrency?.decimals !== 18 ||
@@ -104,12 +114,39 @@ async function getAlerts(pendingApproval) {
 }
 
 function getValues(pendingApproval, t, actions) {
+  const originIsMetaMask = pendingApproval.origin === 'metamask';
+
   return {
     content: [
+      {
+        hide: !originIsMetaMask,
+        element: 'Box',
+        key: 'network-box',
+        props: {
+          textAlign: TEXT_ALIGN.CENTER,
+          display: DISPLAY.FLEX,
+          justifyContent: JUSTIFY_CONTENT.CENTER,
+          marginTop: 4,
+          marginBottom: 2,
+        },
+        children: [
+          {
+            element: 'Chip',
+            key: 'network-chip',
+            props: {
+              label: pendingApproval.requestData.chainName,
+              backgroundColor: COLORS.BACKGROUND_ALTERNATIVE,
+              leftIconUrl: pendingApproval.requestData.imageUrl,
+            },
+          },
+        ],
+      },
       {
         element: 'Typography',
         key: 'title',
-        children: t('addEthereumChainConfirmationTitle'),
+        children: originIsMetaMask
+          ? t('wantToAddThisNetwork')
+          : t('addEthereumChainConfirmationTitle'),
         props: {
           variant: TYPOGRAPHY.H3,
           align: 'center',
@@ -127,7 +164,7 @@ function getValues(pendingApproval, t, actions) {
           variant: TYPOGRAPHY.H7,
           align: 'center',
           boxProps: {
-            margin: [0, 0, 4],
+            margin: originIsMetaMask ? [0, 8, 4] : [0, 0, 4],
           },
         },
       },
@@ -138,7 +175,55 @@ function getValues(pendingApproval, t, actions) {
           {
             element: 'b',
             key: 'bolded-text',
-            children: `${t('addEthereumChainConfirmationRisks')} `,
+            props: {
+              style: { display: originIsMetaMask && '-webkit-box' },
+            },
+            children: [
+              `${t('addEthereumChainConfirmationRisks')} `,
+              {
+                hide: !originIsMetaMask,
+                element: 'Tooltip',
+                key: 'tooltip-info',
+                props: {
+                  position: 'bottom',
+                  interactive: true,
+                  trigger: 'mouseenter',
+                  html: (
+                    <div
+                      style={{
+                        width: '180px',
+                        margin: '16px',
+                        textAlign: 'left',
+                      }}
+                    >
+                      {t('someNetworksMayPoseSecurity')}{' '}
+                      <a
+                        key="zendesk_page_link"
+                        href="https://metamask.zendesk.com/hc/en-us/articles/4417500466971"
+                        rel="noreferrer"
+                        target="_blank"
+                        style={{ color: 'var(--color-primary-default)' }}
+                      >
+                        {t('learnMoreUpperCase')}
+                      </a>
+                    </div>
+                  ),
+                },
+                children: [
+                  {
+                    element: 'i',
+                    key: 'info-circle',
+                    props: {
+                      className: 'fas fa-info-circle',
+                      style: {
+                        marginLeft: '4px',
+                        color: 'var(--color-icon-default)',
+                      },
+                    },
+                  },
+                ],
+              },
+            ],
           },
           {
             element: 'MetaMaskTranslation',
@@ -164,7 +249,7 @@ function getValues(pendingApproval, t, actions) {
           variant: TYPOGRAPHY.H7,
           align: 'center',
           boxProps: {
-            margin: 0,
+            margin: originIsMetaMask ? [0, 8] : 0,
           },
         },
       },
@@ -205,7 +290,7 @@ function getValues(pendingApproval, t, actions) {
         pendingApproval.id,
         ethErrors.provider.userRejectedRequest().serialize(),
       ),
-    networkDisplay: true,
+    networkDisplay: !originIsMetaMask,
   };
 }
 
diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js
index cd728b85b..470cc6d78 100644
--- a/ui/pages/home/home.component.js
+++ b/ui/pages/home/home.component.js
@@ -26,9 +26,7 @@ import {
   TYPOGRAPHY,
   FONT_WEIGHT,
   DISPLAY,
-  ///: BEGIN:ONLY_INCLUDE_IN(flask)
   COLORS,
-  ///: END:ONLY_INCLUDE_IN
 } from '../../helpers/constants/design-system';
 
 import {
@@ -143,6 +141,9 @@ export default class Home extends PureComponent {
     closeNotificationPopup: PropTypes.func.isRequired,
     newTokensImported: PropTypes.string,
     setNewTokensImported: PropTypes.func.isRequired,
+    newCustomNetworkAdded: PropTypes.object,
+    setNewCustomNetworkAdded: PropTypes.func,
+    setRpcTarget: PropTypes.func,
   };
 
   state = {
@@ -280,6 +281,9 @@ export default class Home extends PureComponent {
       setNewCollectibleAddedMessage,
       newTokensImported,
       setNewTokensImported,
+      newCustomNetworkAdded,
+      setNewCustomNetworkAdded,
+      setRpcTarget,
     } = this.props;
     return (
       <MultipleNotifications>
@@ -479,6 +483,53 @@ export default class Home extends PureComponent {
             key="home-infuraBlockedNotification"
           />
         ) : null}
+        {Object.keys(newCustomNetworkAdded).length !== 0 && (
+          <Popover className="home__new-network-added">
+            <i className="fa fa-check-circle fa-2x home__new-network-added__check-circle" />
+            <Typography
+              variant={TYPOGRAPHY.H4}
+              margin={[5, 9, 0, 9]}
+              fontWeight={FONT_WEIGHT.BOLD}
+            >
+              {t('networkAddedSuccessfully')}
+            </Typography>
+            <Box margin={[8, 8, 5, 8]}>
+              <Button
+                type="primary"
+                className="home__new-network-added__switch-to-button"
+                onClick={() => {
+                  setRpcTarget(
+                    newCustomNetworkAdded.rpcUrl,
+                    newCustomNetworkAdded.chainId,
+                    newCustomNetworkAdded.ticker,
+                    newCustomNetworkAdded.chainName,
+                  );
+                  setNewCustomNetworkAdded();
+                }}
+              >
+                <Typography
+                  variant={TYPOGRAPHY.H6}
+                  fontWeight={FONT_WEIGHT.NORMAL}
+                  color={COLORS.PRIMARY_INVERSE}
+                >
+                  {t('switchToNetwork', [newCustomNetworkAdded.chainName])}
+                </Typography>
+              </Button>
+              <Button
+                type="secondary"
+                onClick={() => setNewCustomNetworkAdded()}
+              >
+                <Typography
+                  variant={TYPOGRAPHY.H6}
+                  fontWeight={FONT_WEIGHT.NORMAL}
+                  color={COLORS.PRIMARY_DEFAULT}
+                >
+                  {t('dismiss')}
+                </Typography>
+              </Button>
+            </Box>
+          </Popover>
+        )}
       </MultipleNotifications>
     );
   }
diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js
index 09254e101..1c7f5dfed 100644
--- a/ui/pages/home/home.container.js
+++ b/ui/pages/home/home.container.js
@@ -37,11 +37,16 @@ import {
   setNewNetworkAdded,
   setNewCollectibleAddedMessage,
   setNewTokensImported,
+  setRpcTarget,
   ///: BEGIN:ONLY_INCLUDE_IN(flask)
   removeSnapError,
   ///: END:ONLY_INCLUDE_IN
 } from '../../store/actions';
-import { setThreeBoxLastUpdated, hideWhatsNewPopup } from '../../ducks/app/app';
+import {
+  setThreeBoxLastUpdated,
+  hideWhatsNewPopup,
+  setNewCustomNetworkAdded,
+} from '../../ducks/app/app';
 import { getWeb3ShimUsageAlertEnabledness } from '../../ducks/metamask/metamask';
 import { getSwapsFeatureIsLive } from '../../ducks/swaps/swaps';
 import { getEnvironmentType } from '../../../app/scripts/lib/util';
@@ -138,6 +143,7 @@ const mapStateToProps = (state) => {
     isSigningQRHardwareTransaction,
     newCollectibleAddedMessage: getNewCollectibleAddedMessage(state),
     newTokensImported: getNewTokensImported(state),
+    newCustomNetworkAdded: appState.newCustomNetworkAdded,
   };
 };
 
@@ -180,6 +186,12 @@ const mapDispatchToProps = (dispatch) => ({
   setNewTokensImported: (newTokens) => {
     dispatch(setNewTokensImported(newTokens));
   },
+  setNewCustomNetworkAdded: () => {
+    dispatch(setNewCustomNetworkAdded({}));
+  },
+  setRpcTarget: (rpcUrl, chainId, ticker, nickname) => {
+    dispatch(setRpcTarget(rpcUrl, chainId, ticker, nickname));
+  },
 });
 
 export default compose(
diff --git a/ui/pages/home/index.scss b/ui/pages/home/index.scss
index dbecff153..11154b246 100644
--- a/ui/pages/home/index.scss
+++ b/ui/pages/home/index.scss
@@ -207,4 +207,18 @@
       margin-inline-start: 32px;
     }
   }
+
+  &__new-network-added {
+    border-radius: 10px;
+    text-align: center;
+
+    &__check-circle {
+      color: var(--color-success-default);
+      margin-top: 20px;
+    }
+
+    &__switch-to-button {
+      margin-bottom: 16px;
+    }
+  }
 }
diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.component.js b/ui/pages/send/send-content/add-recipient/add-recipient.component.js
index fa2dbbfef..b0f791560 100644
--- a/ui/pages/send/send-content/add-recipient/add-recipient.component.js
+++ b/ui/pages/send/send-content/add-recipient/add-recipient.component.js
@@ -32,6 +32,7 @@ export default class AddRecipient extends Component {
       error: PropTypes.string,
       warning: PropTypes.string,
     }),
+    updateRecipientUserInput: PropTypes.func,
   };
 
   constructor(props) {
@@ -70,6 +71,7 @@ export default class AddRecipient extends Component {
       `sendFlow - User clicked recipient from ${type}. address: ${address}, nickname ${nickname}`,
     );
     this.props.updateRecipient({ address, nickname });
+    this.props.updateRecipientUserInput(address);
   };
 
   searchForContacts = () => {
diff --git a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js
index a163a13a0..45b85d799 100644
--- a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js
+++ b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js
@@ -3,9 +3,13 @@ import configureMockStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
 
 import { fireEvent } from '@testing-library/react';
-import { initialState, SEND_STATUSES } from '../../../../../ducks/send';
+import { AMOUNT_MODES, SEND_STATUSES } from '../../../../../ducks/send';
 import { renderWithProvider } from '../../../../../../test/jest';
 import { GAS_ESTIMATE_TYPES } from '../../../../../../shared/constants/gas';
+import {
+  getInitialSendStateWithExistingTxState,
+  INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+} from '../../../../../../test/jest/mocks';
 import AmountMaxButton from './amount-max-button';
 
 const middleware = [thunk];
@@ -22,7 +26,7 @@ describe('AmountMaxButton Component', () => {
               EIPS: {},
             },
           },
-          send: initialState,
+          send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
         }),
       );
       expect(getByText('Max')).toBeTruthy();
@@ -36,12 +40,14 @@ describe('AmountMaxButton Component', () => {
             EIPS: {},
           },
         },
-        send: { ...initialState, status: SEND_STATUSES.VALID },
+        send: getInitialSendStateWithExistingTxState({
+          status: SEND_STATUSES.VALID,
+        }),
       });
       const { getByText } = renderWithProvider(<AmountMaxButton />, store);
 
       const expectedActions = [
-        { type: 'send/updateAmountMode', payload: 'MAX' },
+        { type: 'send/updateAmountMode', payload: AMOUNT_MODES.MAX },
       ];
 
       fireEvent.click(getByText('Max'), { bubbles: true });
@@ -58,9 +64,10 @@ describe('AmountMaxButton Component', () => {
           },
         },
         send: {
-          ...initialState,
-          status: SEND_STATUSES.VALID,
-          amount: { ...initialState.amount, mode: 'MAX' },
+          ...getInitialSendStateWithExistingTxState({
+            status: SEND_STATUSES.VALID,
+          }),
+          amountMode: AMOUNT_MODES.MAX,
         },
       });
       const { getByText } = renderWithProvider(<AmountMaxButton />, store);
diff --git a/ui/pages/send/send-content/send-content.component.js b/ui/pages/send/send-content/send-content.component.js
index 3b54b4a78..a856624d1 100644
--- a/ui/pages/send/send-content/send-content.component.js
+++ b/ui/pages/send/send-content/send-content.component.js
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import PageContainerContent from '../../../components/ui/page-container/page-container-content.component';
 import Dialog from '../../../components/ui/dialog';
+import ActionableMessage from '../../../components/ui/actionable-message';
 import NicknamePopovers from '../../../components/app/modals/nickname-popovers';
 import {
   ETH_GAS_PRICE_FETCH_WARNING_KEY,
@@ -10,6 +11,7 @@ import {
   INSUFFICIENT_FUNDS_FOR_GAS_ERROR_KEY,
 } from '../../../helpers/constants/error-keys';
 import { ASSET_TYPES } from '../../../../shared/constants/transaction';
+import { CONTRACT_ADDRESS_LINK } from '../../../helpers/constants/common';
 import SendAmountRow from './send-amount-row';
 import SendHexDataRow from './send-hex-data-row';
 import SendAssetRow from './send-asset-row';
@@ -38,6 +40,9 @@ export default class SendContent extends Component {
     asset: PropTypes.object,
     to: PropTypes.string,
     assetError: PropTypes.string,
+    recipient: PropTypes.object,
+    acknowledgeRecipientWarning: PropTypes.func,
+    recipientWarningAcknowledged: PropTypes.bool,
   };
 
   render() {
@@ -51,6 +56,8 @@ export default class SendContent extends Component {
       getIsBalanceInsufficient,
       asset,
       assetError,
+      recipient,
+      recipientWarningAcknowledged,
     } = this.props;
 
     let gasError;
@@ -66,6 +73,10 @@ export default class SendContent extends Component {
       asset.type !== ASSET_TYPES.TOKEN &&
       asset.type !== ASSET_TYPES.COLLECTIBLE;
 
+    const showKnownRecipientWarning =
+      recipient.warning === 'knownAddressRecipient';
+    const hideAddContactDialog = recipient.warning === 'loading';
+
     return (
       <PageContainerContent>
         <div className="send-v2__form">
@@ -76,7 +87,12 @@ export default class SendContent extends Component {
             : null}
           {error ? this.renderError(error) : null}
           {warning ? this.renderWarning() : null}
-          {this.maybeRenderAddContact()}
+          {showKnownRecipientWarning && !recipientWarningAcknowledged
+            ? this.renderRecipientWarning()
+            : null}
+          {showKnownRecipientWarning || hideAddContactDialog
+            ? null
+            : this.maybeRenderAddContact()}
           <SendAssetRow />
           <SendAmountRow />
           {networkOrAccountNotSupports1559 ? <SendGasRow /> : null}
@@ -104,6 +120,7 @@ export default class SendContent extends Component {
         >
           {t('newAccountDetectedDialogMessage')}
         </Dialog>
+
         {showNicknamePopovers ? (
           <NicknamePopovers
             onClose={() => this.setState({ showNicknamePopovers: false })}
@@ -124,6 +141,36 @@ export default class SendContent extends Component {
     );
   }
 
+  renderRecipientWarning() {
+    const { acknowledgeRecipientWarning } = this.props;
+    const { t } = this.context;
+    return (
+      <div className="send__warning-container">
+        <ActionableMessage
+          type="danger"
+          useIcon
+          iconFillColor="#d73a49"
+          primaryActionV2={{
+            label: t('tooltipApproveButton'),
+            onClick: acknowledgeRecipientWarning,
+          }}
+          message={t('sendingToTokenContractWarning', [
+            <a
+              key="contractWarningSupport"
+              target="_blank"
+              rel="noopener noreferrer"
+              className="send__warning-container__link"
+              href={CONTRACT_ADDRESS_LINK}
+            >
+              {t('learnMoreUpperCase')}
+            </a>,
+          ])}
+          roundedButtons
+        />
+      </div>
+    );
+  }
+
   renderError(error) {
     const { t } = this.context;
     return (
diff --git a/ui/pages/send/send-content/send-content.component.test.js b/ui/pages/send/send-content/send-content.component.test.js
index 7b06b50e3..ec8ed3ecf 100644
--- a/ui/pages/send/send-content/send-content.component.test.js
+++ b/ui/pages/send/send-content/send-content.component.test.js
@@ -16,6 +16,32 @@ describe('SendContent Component', () => {
     gasIsExcessive: false,
     networkAndAccountSupports1559: true,
     asset: { type: 'NATIVE' },
+    recipient: {
+      mode: 'CONTACT_LIST',
+      userInput: '0x31A2764925BD47CCBd57b2F277702dB46e9C5F66',
+      address: '0x31A2764925BD47CCBd57b2F277702dB46e9C5F66',
+      nickname: 'John Doe',
+      error: null,
+      warning: null,
+    },
+    tokenAddressList: {
+      '0x32e6c34cd57087abbd59b5a4aecc4cb495924356': {
+        name: 'BitBase',
+        symbol: 'BTBS',
+        decimals: 18,
+        address: '0x32E6C34Cd57087aBBD59B5A4AECC4cB495924356',
+        iconUrl: 'BTBS.svg',
+        occurrences: null,
+      },
+      '0x3fa400483487a489ec9b1db29c4129063eec4654': {
+        name: 'Cryptokek.com',
+        symbol: 'KEK',
+        decimals: 18,
+        address: '0x3fa400483487A489EC9b1dB29C4129063EEC4654',
+        iconUrl: 'cryptokek.svg',
+        occurrences: null,
+      },
+    },
   };
 
   beforeEach(() => {
@@ -150,7 +176,7 @@ describe('SendContent Component', () => {
       true,
     );
     expect(
-      PageContainerContentChild.childAt(1).find(
+      PageContainerContentChild.childAt(2).find(
         'send-v2__asset-dropdown__single-asset',
       ),
     ).toHaveLength(0);
diff --git a/ui/pages/send/send-content/send-content.container.js b/ui/pages/send/send-content/send-content.container.js
index d3e508e9f..53fca7530 100644
--- a/ui/pages/send/send-content/send-content.container.js
+++ b/ui/pages/send/send-content/send-content.container.js
@@ -11,6 +11,9 @@ import {
   getSendTo,
   getSendAsset,
   getAssetError,
+  getRecipient,
+  acknowledgeRecipientWarning,
+  getRecipientWarningAcknowledgement,
 } from '../../../ducks/send';
 
 import SendContent from './send-content.component';
@@ -18,6 +21,10 @@ import SendContent from './send-content.component';
 function mapStateToProps(state) {
   const ownedAccounts = accountsWithSendEtherInfoSelector(state);
   const to = getSendTo(state);
+  const recipient = getRecipient(state);
+  const recipientWarningAcknowledged = getRecipientWarningAcknowledgement(
+    state,
+  );
   return {
     isOwnedAccount: Boolean(
       ownedAccounts.find(
@@ -34,7 +41,15 @@ function mapStateToProps(state) {
     getIsBalanceInsufficient: getIsBalanceInsufficient(state),
     asset: getSendAsset(state),
     assetError: getAssetError(state),
+    recipient,
+    recipientWarningAcknowledged,
   };
 }
 
-export default connect(mapStateToProps)(SendContent);
+function mapDispatchToProps(dispatch) {
+  return {
+    acknowledgeRecipientWarning: () => dispatch(acknowledgeRecipientWarning()),
+  };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(SendContent);
diff --git a/ui/pages/send/send-header/send-header.component.js b/ui/pages/send/send-header/send-header.component.js
index f4528a6c4..d71b6ef99 100644
--- a/ui/pages/send/send-header/send-header.component.js
+++ b/ui/pages/send/send-header/send-header.component.js
@@ -5,6 +5,7 @@ import PageContainerHeader from '../../../components/ui/page-container/page-cont
 import { getMostRecentOverviewPage } from '../../../ducks/history/history';
 import { useI18nContext } from '../../../hooks/useI18nContext';
 import {
+  getDraftTransactionExists,
   getSendAsset,
   getSendStage,
   resetSendState,
@@ -19,15 +20,18 @@ export default function SendHeader() {
   const stage = useSelector(getSendStage);
   const asset = useSelector(getSendAsset);
   const t = useI18nContext();
-
+  const draftTransactionExists = useSelector(getDraftTransactionExists);
   const onClose = () => {
     dispatch(resetSendState());
     history.push(mostRecentOverviewPage);
   };
 
-  let title = asset.type === ASSET_TYPES.NATIVE ? t('send') : t('sendTokens');
+  let title = asset?.type === ASSET_TYPES.NATIVE ? t('send') : t('sendTokens');
 
-  if (stage === SEND_STAGES.ADD_RECIPIENT || stage === SEND_STAGES.INACTIVE) {
+  if (
+    draftTransactionExists === false ||
+    [SEND_STAGES.ADD_RECIPIENT, SEND_STAGES.INACTIVE].includes(stage)
+  ) {
     title = t('sendTo');
   } else if (stage === SEND_STAGES.EDIT) {
     title = t('edit');
diff --git a/ui/pages/send/send-header/send-header.component.test.js b/ui/pages/send/send-header/send-header.component.test.js
index 5ec8bdc69..a6eaa19d9 100644
--- a/ui/pages/send/send-header/send-header.component.test.js
+++ b/ui/pages/send/send-header/send-header.component.test.js
@@ -3,9 +3,13 @@ import configureMockStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
 
 import { fireEvent } from '@testing-library/react';
-import { initialState, SEND_STAGES } from '../../../ducks/send';
+import { SEND_STAGES } from '../../../ducks/send';
 import { renderWithProvider } from '../../../../test/jest';
 import { ASSET_TYPES } from '../../../../shared/constants/transaction';
+import {
+  getInitialSendStateWithExistingTxState,
+  INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+} from '../../../../test/jest/mocks';
 import SendHeader from './send-header.component';
 
 const middleware = [thunk];
@@ -26,7 +30,7 @@ describe('SendHeader Component', () => {
       const { getByText, rerender } = renderWithProvider(
         <SendHeader />,
         configureMockStore(middleware)({
-          send: initialState,
+          send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
           gas: { basicEstimateStatus: 'LOADING' },
           history: { mostRecentOverviewPage: 'activity' },
         }),
@@ -35,7 +39,10 @@ describe('SendHeader Component', () => {
       rerender(
         <SendHeader />,
         configureMockStore(middleware)({
-          send: { ...initialState, stage: SEND_STAGES.ADD_RECIPIENT },
+          send: {
+            ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+            stage: SEND_STAGES.ADD_RECIPIENT,
+          },
           gas: { basicEstimateStatus: 'LOADING' },
           history: { mostRecentOverviewPage: 'activity' },
         }),
@@ -48,9 +55,12 @@ describe('SendHeader Component', () => {
         <SendHeader />,
         configureMockStore(middleware)({
           send: {
-            ...initialState,
+            ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
             stage: SEND_STAGES.DRAFT,
-            asset: { ...initialState.asset, type: ASSET_TYPES.NATIVE },
+            asset: {
+              ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT.asset,
+              type: ASSET_TYPES.NATIVE,
+            },
           },
           gas: { basicEstimateStatus: 'LOADING' },
           history: { mostRecentOverviewPage: 'activity' },
@@ -64,9 +74,12 @@ describe('SendHeader Component', () => {
         <SendHeader />,
         configureMockStore(middleware)({
           send: {
-            ...initialState,
+            ...getInitialSendStateWithExistingTxState({
+              asset: {
+                type: ASSET_TYPES.TOKEN,
+              },
+            }),
             stage: SEND_STAGES.DRAFT,
-            asset: { ...initialState.asset, type: ASSET_TYPES.TOKEN },
           },
           gas: { basicEstimateStatus: 'LOADING' },
           history: { mostRecentOverviewPage: 'activity' },
@@ -80,7 +93,7 @@ describe('SendHeader Component', () => {
         <SendHeader />,
         configureMockStore(middleware)({
           send: {
-            ...initialState,
+            ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
             stage: SEND_STAGES.EDIT,
           },
           gas: { basicEstimateStatus: 'LOADING' },
@@ -96,7 +109,7 @@ describe('SendHeader Component', () => {
       const { getByText } = renderWithProvider(
         <SendHeader />,
         configureMockStore(middleware)({
-          send: initialState,
+          send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
           gas: { basicEstimateStatus: 'LOADING' },
           history: { mostRecentOverviewPage: 'activity' },
         }),
@@ -108,7 +121,10 @@ describe('SendHeader Component', () => {
       const { getByText } = renderWithProvider(
         <SendHeader />,
         configureMockStore(middleware)({
-          send: { ...initialState, stage: SEND_STAGES.EDIT },
+          send: {
+            ...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
+            stage: SEND_STAGES.EDIT,
+          },
           gas: { basicEstimateStatus: 'LOADING' },
           history: { mostRecentOverviewPage: 'activity' },
         }),
@@ -118,7 +134,7 @@ describe('SendHeader Component', () => {
 
     it('resets send state when clicked', () => {
       const store = configureMockStore(middleware)({
-        send: initialState,
+        send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
         gas: { basicEstimateStatus: 'LOADING' },
         history: { mostRecentOverviewPage: 'activity' },
       });
diff --git a/ui/pages/send/send.js b/ui/pages/send/send.js
index 79980b25c..ad10615a7 100644
--- a/ui/pages/send/send.js
+++ b/ui/pages/send/send.js
@@ -1,24 +1,26 @@
-import React, { useEffect, useCallback, useContext } from 'react';
+import React, { useEffect, useCallback, useContext, useRef } from 'react';
 import { useDispatch, useSelector } from 'react-redux';
 import { useHistory, useLocation } from 'react-router-dom';
 import {
   addHistoryEntry,
+  getDraftTransactionExists,
   getIsUsingMyAccountForRecipientSearch,
   getRecipient,
   getRecipientUserInput,
   getSendStage,
-  initializeSendState,
   resetRecipientInput,
   resetSendState,
   SEND_STAGES,
+  startNewDraftTransaction,
   updateRecipient,
   updateRecipientUserInput,
 } from '../../ducks/send';
-import { getCurrentChainId, isCustomPriceExcessive } from '../../selectors';
+import { isCustomPriceExcessive } from '../../selectors';
 import { getSendHexDataFeatureFlagState } from '../../ducks/metamask/metamask';
 import { showQrScanner } from '../../store/actions';
 import { MetaMetricsContext } from '../../contexts/metametrics';
 import { EVENT } from '../../../shared/constants/metametrics';
+import { ASSET_TYPES } from '../../../shared/constants/transaction';
 import SendHeader from './send-header';
 import AddRecipient from './send-content/add-recipient';
 import SendContent from './send-content';
@@ -30,7 +32,7 @@ const sendSliceIsCustomPriceExcessive = (state) =>
 
 export default function SendTransactionScreen() {
   const history = useHistory();
-  const chainId = useSelector(getCurrentChainId);
+  const startedNewDraftTransaction = useRef(false);
   const stage = useSelector(getSendStage);
   const gasIsExcessive = useSelector(sendSliceIsCustomPriceExcessive);
   const isUsingMyAccountsForRecipientSearch = useSelector(
@@ -39,6 +41,7 @@ export default function SendTransactionScreen() {
   const recipient = useSelector(getRecipient);
   const showHexData = useSelector(getSendHexDataFeatureFlagState);
   const userInput = useSelector(getRecipientUserInput);
+  const draftTransactionExists = useSelector(getDraftTransactionExists);
   const location = useLocation();
   const trackEvent = useContext(MetaMetricsContext);
 
@@ -48,12 +51,26 @@ export default function SendTransactionScreen() {
     dispatch(resetSendState());
   }, [dispatch]);
 
+  /**
+   * It is possible to route to this page directly, either by typing in the url
+   * or by clicking the browser back button after progressing to the confirm
+   * screen. In the case where a draft transaction does not yet exist, this
+   * hook is responsible for creating it. We will assume that this is a native
+   * asset send.
+   */
   useEffect(() => {
-    if (chainId !== undefined) {
-      dispatch(initializeSendState());
-      window.addEventListener('beforeunload', cleanup);
+    if (
+      draftTransactionExists === false &&
+      startedNewDraftTransaction.current === false
+    ) {
+      startedNewDraftTransaction.current = true;
+      dispatch(startNewDraftTransaction({ type: ASSET_TYPES.NATIVE }));
     }
-  }, [chainId, dispatch, cleanup]);
+  }, [draftTransactionExists, dispatch]);
+
+  useEffect(() => {
+    window.addEventListener('beforeunload', cleanup);
+  }, [cleanup]);
 
   useEffect(() => {
     if (location.search === '?scan=true') {
@@ -75,7 +92,10 @@ export default function SendTransactionScreen() {
 
   let content;
 
-  if ([SEND_STAGES.EDIT, SEND_STAGES.DRAFT].includes(stage)) {
+  if (
+    draftTransactionExists &&
+    [SEND_STAGES.EDIT, SEND_STAGES.DRAFT].includes(stage)
+  ) {
     content = (
       <>
         <SendContent
@@ -96,10 +116,11 @@ export default function SendTransactionScreen() {
         userInput={userInput}
         className="send__to-row"
         onChange={(address) => dispatch(updateRecipientUserInput(address))}
-        onValidAddressTyped={(address) => {
+        onValidAddressTyped={async (address) => {
           dispatch(
             addHistoryEntry(`sendFlow - Valid address typed ${address}`),
           );
+          await dispatch(updateRecipientUserInput(address));
           dispatch(updateRecipient({ address, nickname: '' }));
         }}
         internalSearch={isUsingMyAccountsForRecipientSearch}
@@ -111,7 +132,6 @@ export default function SendTransactionScreen() {
               `sendFlow - User pasted ${text} into address field`,
             ),
           );
-          return dispatch(updateRecipient({ address: text, nickname: '' }));
         }}
         onReset={() => dispatch(resetRecipientInput())}
         scanQrCode={() => {
diff --git a/ui/pages/send/send.scss b/ui/pages/send/send.scss
index 8d45dfcb7..2735ba89a 100644
--- a/ui/pages/send/send.scss
+++ b/ui/pages/send/send.scss
@@ -35,6 +35,15 @@
     margin: 1rem;
   }
 
+  &__warning-container {
+    padding-left: 16px;
+    padding-right: 16px;
+
+    &__link {
+      color: var(--primary-1);
+    }
+  }
+
   &__to-row {
     margin: 0;
     padding: 0.5rem;
diff --git a/ui/pages/send/send.test.js b/ui/pages/send/send.test.js
index e2d16be9f..aa4b57f5f 100644
--- a/ui/pages/send/send.test.js
+++ b/ui/pages/send/send.test.js
@@ -3,15 +3,30 @@ import configureMockStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
 
 import { useLocation } from 'react-router-dom';
-import { initialState, SEND_STAGES } from '../../ducks/send';
+import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
 import { ensInitialState } from '../../ducks/ens';
 import { renderWithProvider } from '../../../test/jest';
 import { RINKEBY_CHAIN_ID } from '../../../shared/constants/network';
 import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
+import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT } from '../../../test/jest/mocks';
 import Send from './send';
 
 const middleware = [thunk];
 
+jest.mock('../../ducks/send/send', () => {
+  const original = jest.requireActual('../../ducks/send/send');
+  return {
+    ...original,
+    // We don't really need to start a draft transaction, and the mock store
+    // does not update as a result of action calls so instead we just ensure
+    // that the action WOULD be called.
+    startNewDraftTransaction: jest.fn(() => ({
+      type: 'TEST_START_NEW_DRAFT',
+      payload: null,
+    })),
+  };
+});
+
 jest.mock('react-router-dom', () => {
   const original = jest.requireActual('react-router-dom');
   return {
@@ -34,7 +49,7 @@ jest.mock(
 );
 
 const baseStore = {
-  send: initialState,
+  send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
   ENS: ensInitialState,
   gas: {
     customData: { limit: null, price: null },
@@ -79,6 +94,25 @@ const baseStore = {
       '0x0': { balance: '0x0', address: '0x0' },
     },
     identities: { '0x0': { address: '0x0' } },
+    tokenAddress: '0x32e6c34cd57087abbd59b5a4aecc4cb495924356',
+    tokenList: {
+      '0x32e6c34cd57087abbd59b5a4aecc4cb495924356': {
+        name: 'BitBase',
+        symbol: 'BTBS',
+        decimals: 18,
+        address: '0x32E6C34Cd57087aBBD59B5A4AECC4cB495924356',
+        iconUrl: 'BTBS.svg',
+        occurrences: null,
+      },
+      '0x3fa400483487a489ec9b1db29c4129063eec4654': {
+        name: 'Cryptokek.com',
+        symbol: 'KEK',
+        decimals: 18,
+        address: '0x3fa400483487A489EC9b1dB29C4129063EEC4654',
+        iconUrl: 'cryptokek.svg',
+        occurrences: null,
+      },
+    },
   },
   appState: {
     sendInputCurrencySwitched: false,
@@ -87,7 +121,7 @@ const baseStore = {
 
 describe('Send Page', () => {
   describe('Send Flow Initialization', () => {
-    it('should initialize the send, ENS, and gas slices on render', () => {
+    it('should initialize the ENS slice on render', () => {
       const store = configureMockStore(middleware)(baseStore);
       renderWithProvider(<Send />, store);
       const actions = store.getActions();
@@ -96,9 +130,6 @@ describe('Send Page', () => {
           expect.objectContaining({
             type: 'ENS/enableEnsLookup',
           }),
-          expect.objectContaining({
-            type: 'send/initializeSendState/pending',
-          }),
         ]),
       );
     });
@@ -113,9 +144,6 @@ describe('Send Page', () => {
           expect.objectContaining({
             type: 'ENS/enableEnsLookup',
           }),
-          expect.objectContaining({
-            type: 'send/initializeSendState/pending',
-          }),
           expect.objectContaining({
             type: 'UI_MODAL_OPEN',
             payload: { name: 'QR_SCANNER' },
@@ -146,6 +174,25 @@ describe('Send Page', () => {
       const { queryByText } = renderWithProvider(<Send />, store);
       expect(queryByText('Next')).toBeNull();
     });
+
+    it('should render correctly even when a draftTransaction does not exist', () => {
+      const modifiedStore = {
+        ...baseStore,
+        send: {
+          ...baseStore.send,
+          currentTransactionUUID: null,
+        },
+      };
+      const store = configureMockStore(middleware)(modifiedStore);
+      const { getByPlaceholderText } = renderWithProvider(<Send />, store);
+      // Ensure that the send flow renders on the add recipient screen when
+      // there is no draft transaction.
+      expect(
+        getByPlaceholderText('Search, public address (0x), or ENS'),
+      ).toBeTruthy();
+      // Ensure we start a new draft transaction when its missing.
+      expect(startNewDraftTransaction).toHaveBeenCalledTimes(1);
+    });
   });
 
   describe('Send and Edit Flow (draft)', () => {
diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.js b/ui/pages/settings/experimental-tab/experimental-tab.component.js
index dc30b54b2..741445a31 100644
--- a/ui/pages/settings/experimental-tab/experimental-tab.component.js
+++ b/ui/pages/settings/experimental-tab/experimental-tab.component.js
@@ -26,6 +26,8 @@ export default class ExperimentalTab extends PureComponent {
     setEIP1559V2Enabled: PropTypes.func,
     theme: PropTypes.string,
     setTheme: PropTypes.func,
+    customNetworkListEnabled: PropTypes.bool,
+    setCustomNetworkListEnabled: PropTypes.func,
   };
 
   settingsRefs = Array(
@@ -284,6 +286,45 @@ export default class ExperimentalTab extends PureComponent {
     );
   }
 
+  renderCustomNetworkListToggle() {
+    const { t } = this.context;
+    const {
+      customNetworkListEnabled,
+      setCustomNetworkListEnabled,
+    } = this.props;
+
+    return (
+      <div ref={this.settingsRefs[5]} className="settings-page__content-row">
+        <div className="settings-page__content-item">
+          <span>{t('showCustomNetworkList')}</span>
+          <div className="settings-page__content-description">
+            {t('showCustomNetworkListDescription')}
+          </div>
+        </div>
+        <div className="settings-page__content-item">
+          <div className="settings-page__content-item-col">
+            <ToggleButton
+              value={customNetworkListEnabled}
+              onToggle={(value) => {
+                this.context.trackEvent({
+                  category: EVENT.CATEGORIES.SETTINGS,
+                  event: 'Enabled/Disable CustomNetworkList',
+                  properties: {
+                    action: 'Enabled/Disable CustomNetworkList',
+                    legacy_event: true,
+                  },
+                });
+                setCustomNetworkListEnabled(!value);
+              }}
+              offLabel={t('off')}
+              onLabel={t('on')}
+            />
+          </div>
+        </div>
+      </div>
+    );
+  }
+
   render() {
     return (
       <div className="settings-page__body">
@@ -295,6 +336,8 @@ export default class ExperimentalTab extends PureComponent {
         {this.renderCollectibleDetectionToggle()}
         {this.renderEIP1559V2EnabledToggle()}
         {this.renderTheme()}
+        {process.env.ADD_POPULAR_NETWORKS &&
+          this.renderCustomNetworkListToggle()}
       </div>
     );
   }
diff --git a/ui/pages/settings/experimental-tab/experimental-tab.container.js b/ui/pages/settings/experimental-tab/experimental-tab.container.js
index 1fb4124ee..244de6198 100644
--- a/ui/pages/settings/experimental-tab/experimental-tab.container.js
+++ b/ui/pages/settings/experimental-tab/experimental-tab.container.js
@@ -7,6 +7,7 @@ import {
   setOpenSeaEnabled,
   setEIP1559V2Enabled,
   setTheme,
+  setCustomNetworkListEnabled,
 } from '../../../store/actions';
 import {
   getUseTokenDetection,
@@ -14,6 +15,7 @@ import {
   getOpenSeaEnabled,
   getEIP1559V2Enabled,
   getTheme,
+  getIsCustomNetworkListEnabled,
 } from '../../../selectors';
 import ExperimentalTab from './experimental-tab.component';
 
@@ -26,6 +28,7 @@ const mapStateToProps = (state) => {
     openSeaEnabled: getOpenSeaEnabled(state),
     eip1559V2Enabled: getEIP1559V2Enabled(state),
     theme: getTheme(state),
+    customNetworkListEnabled: getIsCustomNetworkListEnabled(state),
   };
 };
 
@@ -40,6 +43,8 @@ const mapDispatchToProps = (dispatch) => {
     setOpenSeaEnabled: (val) => dispatch(setOpenSeaEnabled(val)),
     setEIP1559V2Enabled: (val) => dispatch(setEIP1559V2Enabled(val)),
     setTheme: (val) => dispatch(setTheme(val)),
+    setCustomNetworkListEnabled: (val) =>
+      dispatch(setCustomNetworkListEnabled(val)),
   };
 };
 
diff --git a/ui/pages/settings/networks-tab/networks-form/networks-form.js b/ui/pages/settings/networks-tab/networks-form/networks-form.js
index ef846c638..22c28a170 100644
--- a/ui/pages/settings/networks-tab/networks-form/networks-form.js
+++ b/ui/pages/settings/networks-tab/networks-form/networks-form.js
@@ -522,7 +522,6 @@ const NetworksForm = ({
         onConfirm: () => {
           resetForm();
           dispatch(setSelectedSettingsRpcUrl(''));
-          history.push(NETWORKS_ROUTE);
         },
       }),
     );
diff --git a/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.js b/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.js
index 1420e3082..1fad5fdc4 100644
--- a/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.js
+++ b/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.js
@@ -1,18 +1,31 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { useHistory } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 import { useI18nContext } from '../../../../hooks/useI18nContext';
-import { ADD_NETWORK_ROUTE } from '../../../../helpers/constants/routes';
+import {
+  ADD_NETWORK_ROUTE,
+  ADD_POPULAR_CUSTOM_NETWORK,
+} from '../../../../helpers/constants/routes';
 import Button from '../../../../components/ui/button';
+import { getIsCustomNetworkListEnabled } from '../../../../selectors';
 
 const NetworksFormSubheader = ({ addNewNetwork }) => {
   const t = useI18nContext();
   const history = useHistory();
+  const addPopularNetworkFeatureToggledOn = useSelector(
+    getIsCustomNetworkListEnabled,
+  );
+
   return addNewNetwork ? (
     <div className="networks-tab__subheader">
       <span className="networks-tab__sub-header-text">{t('networks')}</span>
+      <span className="networks-tab__sub-header-text">{'  >  '}</span>
+      <div className="networks-tab__sub-header-text">{t('addANetwork')}</div>
       <span>{'  >  '}</span>
-      <div className="networks-tab__subheader--break">{t('addANetwork')}</div>
+      <div className="networks-tab__subheader--break">
+        {t('addANetworkManually')}
+      </div>
     </div>
   ) : (
     <div className="settings-page__sub-header">
@@ -22,7 +35,9 @@ const NetworksFormSubheader = ({ addNewNetwork }) => {
           type="primary"
           onClick={(event) => {
             event.preventDefault();
-            history.push(ADD_NETWORK_ROUTE);
+            addPopularNetworkFeatureToggledOn
+              ? history.push(ADD_POPULAR_CUSTOM_NETWORK)
+              : history.push(ADD_NETWORK_ROUTE);
           }}
         >
           {t('addANetwork')}
diff --git a/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.test.js b/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.test.js
index 099261049..44b5b1768 100644
--- a/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.test.js
+++ b/ui/pages/settings/networks-tab/networks-tab-subheader/networks-tab-subheader.test.js
@@ -1,5 +1,6 @@
 import React from 'react';
 import configureMockStore from 'redux-mock-store';
+import { waitFor } from '@testing-library/react';
 import { renderWithProvider } from '../../../../../test/jest/rendering';
 import NetworksTabSubheader from '.';
 
@@ -36,11 +37,11 @@ describe('NetworksTabSubheader Component', () => {
     expect(getByRole('button', { text: 'Add a network' })).toBeDefined();
   });
   it('should render add network form subheader correctly', () => {
-    const { queryByText } = renderComponent({
+    const { queryByText, getAllByText } = renderComponent({
       addNewNetwork: true,
     });
     expect(queryByText('Networks')).toBeInTheDocument();
-    expect(queryByText('>')).toBeInTheDocument();
+    waitFor(() => expect(getAllByText('>')).toBeInTheDocument());
     expect(queryByText('Add a network')).toBeInTheDocument();
   });
 });
diff --git a/ui/pages/settings/networks-tab/networks-tab.js b/ui/pages/settings/networks-tab/networks-tab.js
index c73787566..35814d419 100644
--- a/ui/pages/settings/networks-tab/networks-tab.js
+++ b/ui/pages/settings/networks-tab/networks-tab.js
@@ -1,11 +1,12 @@
 import React, { useEffect } from 'react';
 import PropTypes from 'prop-types';
 import classnames from 'classnames';
-import { useLocation } from 'react-router-dom';
+import { useLocation, useHistory } from 'react-router-dom';
 import { useDispatch, useSelector } from 'react-redux';
 import { useI18nContext } from '../../../hooks/useI18nContext';
 import {
   ADD_NETWORK_ROUTE,
+  ADD_POPULAR_CUSTOM_NETWORK,
   NETWORKS_FORM_ROUTE,
 } from '../../../helpers/constants/routes';
 import { setSelectedSettingsRpcUrl } from '../../../store/actions';
@@ -14,6 +15,7 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util';
 import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
 import {
   getFrequentRpcListDetail,
+  getIsCustomNetworkListEnabled,
   getNetworksTabSelectedRpcUrl,
   getProvider,
 } from '../../../selectors';
@@ -36,6 +38,7 @@ const NetworksTab = ({ addNewNetwork }) => {
   const t = useI18nContext();
   const dispatch = useDispatch();
   const { pathname } = useLocation();
+  const history = useHistory();
 
   const environmentType = getEnvironmentType();
   const isFullScreen = environmentType === ENVIRONMENT_TYPE_FULLSCREEN;
@@ -45,6 +48,9 @@ const NetworksTab = ({ addNewNetwork }) => {
   const frequentRpcListDetail = useSelector(getFrequentRpcListDetail);
   const provider = useSelector(getProvider);
   const networksTabSelectedRpcUrl = useSelector(getNetworksTabSelectedRpcUrl);
+  const addPopularNetworkFeatureToggledOn = useSelector(
+    getIsCustomNetworkListEnabled,
+  );
 
   const frequentRpcNetworkListDetails = frequentRpcListDetail.map((rpc) => {
     return {
@@ -118,9 +124,16 @@ const NetworksTab = ({ addNewNetwork }) => {
               <div className="networks-tab__networks-list-popup-footer">
                 <Button
                   type="primary"
-                  onClick={(event) => {
-                    event.preventDefault();
-                    global.platform.openExtensionInBrowser(ADD_NETWORK_ROUTE);
+                  onClick={() => {
+                    if (addPopularNetworkFeatureToggledOn) {
+                      history.push(ADD_POPULAR_CUSTOM_NETWORK);
+                    } else {
+                      isFullScreen
+                        ? history.push(ADD_NETWORK_ROUTE)
+                        : global.platform.openExtensionInBrowser(
+                            ADD_NETWORK_ROUTE,
+                          );
+                    }
                   }}
                 >
                   {t('addNetwork')}
diff --git a/ui/pages/settings/settings.component.js b/ui/pages/settings/settings.component.js
index c3f76d53f..64995b15c 100644
--- a/ui/pages/settings/settings.component.js
+++ b/ui/pages/settings/settings.component.js
@@ -23,9 +23,11 @@ import {
   CONTACT_VIEW_ROUTE,
   EXPERIMENTAL_ROUTE,
   ADD_NETWORK_ROUTE,
+  ADD_POPULAR_CUSTOM_NETWORK,
 } from '../../helpers/constants/routes';
 
 import { getSettingsRoutes } from '../../helpers/utils/settings-search';
+import AddNetwork from '../../components/app/add-network/add-network';
 import SettingsTab from './settings-tab';
 import AlertsTab from './alerts-tab';
 import NetworksTab from './networks-tab';
@@ -124,7 +126,6 @@ class SettingsPage extends PureComponent {
             )}
 
             {this.renderTitle()}
-
             <div
               className="settings-page__header__title-container__close-button"
               onClick={() => {
@@ -343,9 +344,15 @@ class SettingsPage extends PureComponent {
           render={() => <NetworksTab addNewNetwork />}
         />
         <Route
+          exact
           path={NETWORKS_ROUTE}
           render={() => <NetworksTab addNewNetwork={false} />}
         />
+        <Route
+          exact
+          path={ADD_POPULAR_CUSTOM_NETWORK}
+          render={() => <AddNetwork />}
+        />
         <Route exact path={SECURITY_ROUTE} component={SecurityTab} />
         <Route exact path={EXPERIMENTAL_ROUTE} component={ExperimentalTab} />
         <Route exact path={CONTACT_LIST_ROUTE} component={ContactListTab} />
diff --git a/ui/pages/settings/settings.container.js b/ui/pages/settings/settings.container.js
index 78a470b94..735e66507 100644
--- a/ui/pages/settings/settings.container.js
+++ b/ui/pages/settings/settings.container.js
@@ -27,6 +27,7 @@ import {
   ADD_NETWORK_ROUTE,
   SNAPS_LIST_ROUTE,
   SNAPS_VIEW_ROUTE,
+  ADD_POPULAR_CUSTOM_NETWORK,
 } from '../../helpers/constants/routes';
 import Settings from './settings.component';
 
@@ -46,6 +47,7 @@ const ROUTES_TO_I18N_KEYS = {
   [ADD_NETWORK_ROUTE]: 'networks',
   [SECURITY_ROUTE]: 'securityAndPrivacy',
   [EXPERIMENTAL_ROUTE]: 'experimental',
+  [ADD_POPULAR_CUSTOM_NETWORK]: 'addNetwork',
 };
 
 const mapStateToProps = (state, ownProps) => {
@@ -64,6 +66,9 @@ const mapStateToProps = (state, ownProps) => {
     Boolean(pathname.match(NETWORKS_FORM_ROUTE)) ||
     Boolean(pathname.match(ADD_NETWORK_ROUTE));
   const addNewNetwork = Boolean(pathname.match(ADD_NETWORK_ROUTE));
+  const isAddPopularCustomNetwork = Boolean(
+    pathname.match(ADD_POPULAR_CUSTOM_NETWORK),
+  );
 
   const isPopup = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP;
   const pathnameI18nKey = ROUTES_TO_I18N_KEYS[pathname];
@@ -77,6 +82,8 @@ const mapStateToProps = (state, ownProps) => {
     backRoute = NETWORKS_ROUTE;
   } else if (isSnapViewPage) {
     backRoute = SNAPS_LIST_ROUTE;
+  } else if (isAddPopularCustomNetwork) {
+    backRoute = NETWORKS_ROUTE;
   }
 
   let initialBreadCrumbRoute;
diff --git a/ui/selectors/custom-gas.js b/ui/selectors/custom-gas.js
index ea72bf9ae..3edc9b20b 100644
--- a/ui/selectors/custom-gas.js
+++ b/ui/selectors/custom-gas.js
@@ -8,7 +8,7 @@ import { decEthToConvertedCurrency as ethTotalToConvertedCurrency } from '../hel
 import { formatETHFee } from '../helpers/utils/formatters';
 import { calcGasTotal } from '../pages/send/send.utils';
 
-import { getGasPrice } from '../ducks/send';
+import { getGasLimit, getGasPrice } from '../ducks/send';
 import {
   GAS_ESTIMATE_TYPES as GAS_FEE_CONTROLLER_ESTIMATE_TYPES,
   GAS_LIMITS,
@@ -321,8 +321,9 @@ export function getRenderableEstimateDataForSmallButtonsFromGWEI(state) {
     return [];
   }
   const showFiat = getShouldShowFiat(state);
+
   const gasLimit =
-    state.send.gas.gasLimit || getCustomGasLimit(state) || GAS_LIMITS.SIMPLE;
+    getGasLimit(state) ?? getCustomGasLimit(state) ?? GAS_LIMITS.SIMPLE;
   const { conversionRate } = state.metamask;
   const currentCurrency = getCurrentCurrency(state);
   const gasFeeEstimates = getGasFeeEstimates(state);
diff --git a/ui/selectors/custom-gas.test.js b/ui/selectors/custom-gas.test.js
index d41ec27c3..047335282 100644
--- a/ui/selectors/custom-gas.test.js
+++ b/ui/selectors/custom-gas.test.js
@@ -1,4 +1,5 @@
 import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../shared/constants/gas';
+import { getInitialSendStateWithExistingTxState } from '../../test/jest/mocks';
 import {
   getCustomGasLimit,
   getCustomGasPrice,
@@ -11,7 +12,9 @@ import {
 describe('custom-gas selectors', () => {
   describe('getCustomGasPrice()', () => {
     it('should return gas.customData.price', () => {
-      const mockState = { gas: { customData: { price: 'mockPrice' } } };
+      const mockState = {
+        gas: { customData: { price: 'mockPrice' } },
+      };
       expect(getCustomGasPrice(mockState)).toStrictEqual('mockPrice');
     });
   });
@@ -200,11 +203,11 @@ describe('custom-gas selectors', () => {
             EIPS: {},
           },
         },
-        send: {
+        send: getInitialSendStateWithExistingTxState({
           gas: {
             gasPrice: '0x28bed0160',
           },
-        },
+        }),
         gas: {
           customData: { price: null },
         },
@@ -222,11 +225,11 @@ describe('custom-gas selectors', () => {
             EIPS: {},
           },
         },
-        send: {
+        send: getInitialSendStateWithExistingTxState({
           gas: {
             gasPrice: '0x30e4f9b400',
           },
-        },
+        }),
         gas: {
           customData: { price: null },
         },
@@ -330,11 +333,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x1',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
       {
@@ -379,11 +382,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x4',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
       {
@@ -428,11 +431,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x4',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
       {
@@ -477,11 +480,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x1',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
     ];
@@ -542,11 +545,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x1',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
       {
@@ -591,11 +594,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x1',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
       {
@@ -640,11 +643,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x4',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
       {
@@ -689,11 +692,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x4',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
       {
@@ -738,11 +741,11 @@ describe('custom-gas selectors', () => {
               chainId: '0x1',
             },
           },
-          send: {
+          send: getInitialSendStateWithExistingTxState({
             gas: {
               gasLimit: GAS_LIMITS.SIMPLE,
             },
-          },
+          }),
         },
       },
     ];
diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js
index 76ed8a004..5256c3756 100644
--- a/ui/selectors/selectors.js
+++ b/ui/selectors/selectors.js
@@ -1062,3 +1062,13 @@ export function getDetectedTokensInCurrentNetwork(state) {
 export function getNewTokensImported(state) {
   return state.appState.newTokensImported;
 }
+
+/**
+ * To get the `customNetworkListEnabled` value which determines whether we use the custom network list
+ *
+ * @param {*} state
+ * @returns Boolean
+ */
+export function getIsCustomNetworkListEnabled(state) {
+  return state.metamask.customNetworkListEnabled;
+}
diff --git a/ui/store/actionConstants.js b/ui/store/actionConstants.js
index 2a5528e72..c9e673afa 100644
--- a/ui/store/actionConstants.js
+++ b/ui/store/actionConstants.js
@@ -92,6 +92,7 @@ export const SET_SELECTED_SETTINGS_RPC_URL = 'SET_SELECTED_SETTINGS_RPC_URL';
 export const SET_NEW_NETWORK_ADDED = 'SET_NEW_NETWORK_ADDED';
 export const SET_NEW_COLLECTIBLE_ADDED_MESSAGE =
   'SET_NEW_COLLECTIBLE_ADDED_MESSAGE';
+export const SET_NEW_CUSTOM_NETWORK_ADDED = 'SET_NEW_CUSTOM_NETWORK_ADDED';
 
 export const LOADING_METHOD_DATA_STARTED = 'LOADING_METHOD_DATA_STARTED';
 export const LOADING_METHOD_DATA_FINISHED = 'LOADING_METHOD_DATA_FINISHED';
diff --git a/ui/store/actions.js b/ui/store/actions.js
index b8f738882..c177c1e0c 100644
--- a/ui/store/actions.js
+++ b/ui/store/actions.js
@@ -27,7 +27,11 @@ import {
   getNotifications,
   ///: END:ONLY_INCLUDE_IN
 } from '../selectors';
-import { computeEstimatedGasLimit, resetSendState } from '../ducks/send';
+import {
+  computeEstimatedGasLimit,
+  initializeSendState,
+  resetSendState,
+} from '../ducks/send';
 import { switchedToUnconnectedAccount } from '../ducks/alerts/unconnected-account';
 import { getUnconnectedAccountAlertEnabledness } from '../ducks/metamask/metamask';
 import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
@@ -42,6 +46,7 @@ import { isEqualCaseInsensitive } from '../../shared/modules/string-utils';
 ///: BEGIN:ONLY_INCLUDE_IN(flask)
 import { NOTIFICATIONS_EXPIRATION_DELAY } from '../helpers/constants/notifications';
 ///: END:ONLY_INCLUDE_IN
+import { setNewCustomNetworkAdded } from '../ducks/app/app';
 import * as actionConstants from './actionConstants';
 
 let background = null;
@@ -740,7 +745,7 @@ export function updateEditableParams(txId, editableParams) {
       log.error(error.message);
       throw error;
     }
-
+    await forceUpdateMetamaskState(dispatch);
     return updatedTransaction;
   };
 }
@@ -1442,6 +1447,11 @@ export function updateMetamaskState(newState) {
         type: actionConstants.CHAIN_CHANGED,
         payload: newProvider.chainId,
       });
+      // We dispatch this action to ensure that the send state stays up to date
+      // after the chain changes. This async thunk will fail gracefully in the
+      // event that we are not yet on the send flow with a draftTransaction in
+      // progress.
+      dispatch(initializeSendState({ chainHasChanged: true }));
     }
     dispatch({
       type: actionConstants.UPDATE_METAMASK_STATE,
@@ -3728,6 +3738,18 @@ export function setEnableEIP1559V2NoticeDismissed() {
   return promisifiedBackground.setEnableEIP1559V2NoticeDismissed(true);
 }
 
+export function setCustomNetworkListEnabled(customNetworkListEnabled) {
+  return async () => {
+    try {
+      await promisifiedBackground.setCustomNetworkListEnabled(
+        customNetworkListEnabled,
+      );
+    } catch (error) {
+      log.error(error);
+    }
+  };
+}
+
 // QR Hardware Wallets
 export async function submitQRHardwareCryptoHDKey(cbor) {
   await promisifiedBackground.submitQRHardwareCryptoHDKey(cbor);
@@ -3754,3 +3776,29 @@ export function cancelQRHardwareSignRequest() {
     await promisifiedBackground.cancelQRHardwareSignRequest();
   };
 }
+
+export function addCustomNetwork(customRpc) {
+  return async (dispatch) => {
+    try {
+      dispatch(setNewCustomNetworkAdded(customRpc));
+      await promisifiedBackground.addCustomNetwork(customRpc);
+    } catch (error) {
+      log.error(error);
+      dispatch(displayWarning('Had a problem changing networks!'));
+    }
+  };
+}
+
+export function requestUserApproval(customRpc, originIsMetaMask) {
+  return async (dispatch) => {
+    try {
+      await promisifiedBackground.requestUserApproval(
+        customRpc,
+        originIsMetaMask,
+      );
+    } catch (error) {
+      log.error(error);
+      dispatch(displayWarning('Had a problem changing networks!'));
+    }
+  };
+}
diff --git a/yarn.lock b/yarn.lock
index 64be75773..59206be9f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2895,9 +2895,9 @@
     web3-provider-engine "^16.0.3"
 
 "@metamask/design-tokens@^1.6.0", "@metamask/design-tokens@^1.6.5":
-  version "1.6.5"
-  resolved "https://registry.yarnpkg.com/@metamask/design-tokens/-/design-tokens-1.6.5.tgz#e585b67f73ce301e0218d98ba89e079f7e81c412"
-  integrity sha512-5eCrUHXrIivXX1xx6kwNtM9s/ejhrPYSATSniFc7YKS9z+TkCK4/n52owOBnDIbrL8W3XxQIiaaqQAM+NQad4w==
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/@metamask/design-tokens/-/design-tokens-1.7.0.tgz#fab069c0101da9e25d35ae051df2ff6bb5ff7a38"
+  integrity sha512-ejakgcsnTlLQmMrKb8XixXgExsYuMjlv71lkqJXeT0wa2oe4skVhB2dZul7Y9W4vYvQzTkwsW2NLfaj273eeEw==
 
 "@metamask/eslint-config-jest@^9.0.0":
   version "9.0.0"
@@ -8017,10 +8017,10 @@ chrome-trace-event@^1.0.2:
   dependencies:
     tslib "^1.9.0"
 
-chromedriver@^102.0.0:
-  version "102.0.0"
-  resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-102.0.0.tgz#02844c39ee33d1e88ac8c48fbe28cb8423e970a4"
-  integrity sha512-xer/0g1Oarkjc2e+4nyoLgZT4kJHYhcj3PcxD1nEoGJQYEllTjprN1uDpSb4BkgMGo0ydfIS1VDkszrr/J9OOg==
+chromedriver@^103.0.0:
+  version "103.0.0"
+  resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-103.0.0.tgz#2ef086d62076e3ff6df6cfb84895d11d2c18d9cf"
+  integrity sha512-7BHf6HWt0PeOHCzWO8qlnD13sARzr5AKTtG8Csn+czsuAsajwPxdLNtry5GPh8HYFyl+i0M+yg3bT43AGfgU9w==
   dependencies:
     "@testim/chrome-version" "^1.1.2"
     axios "^0.27.2"