diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index 68cad1c32..000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: Bug Report -about: Using MetaMask, but it's not working as you expect? - ---- - - - -**Describe the bug** -A clear and concise description of what the bug is. - -**Steps to reproduce (REQUIRED)** -Steps to reproduce the behavior, libraries used with version number, and/or any setup information to easily reproduce: - -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Browser details (please complete the following information):** - - OS: [e.g. OS X, Windows] - - Hardware Wallet: [e.g. Trezor Firmware version 1.8.3, Ledger Nano S Firmware version 1.6.0] - - Browser: [e.g. Chrome Version 79.0.3945.79 (Official Build) (64-bit), Firefox Browser 71.0 (64-bit)] - - MetaMask Version: [e.g. 5.0.2 - find it in Settings > About] - -**Additional context (Error Messages, etc.)** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 000000000..cb38aaf16 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,97 @@ +name: Bug Report +description: Using MetaMask, but it's not working as you expect? +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + ## **Before Submitting:** + + * Please search to make sure this issue has not been opened already. + * If this is a question about how to integrate MetaMask with your project, please ask in our [Community forum](https://community.metamask.io/c/developer-questions/) instead. This will get your question answered more quickly and make it easier for other devs to find the answer in the future. + - type: textarea + id: what-happened + attributes: + label: Describe the bug + description: What happened? What did you expect to happen? Please include screenshots if applicable! + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: List all steps needed to reproduce the problem + value: | + 1. + validations: + required: true + - type: textarea + id: error + attributes: + label: Error messages or log output + description: Please copy and paste any relevant error messages or log output. This will be automatically formatted, so there is no need for backticks. + render: shell + - type: input + id: version + attributes: + label: Version + description: What version of MetaMask are you running? You can find the version in "Settings" > "About" + validations: + required: true + - type: dropdown + id: build + attributes: + label: Build type + description: Are you using a testing or development build of MetaMask? If so, please select the type of build you are using. + options: + - Beta + - Flask + - Other (please specify exactly where you obtained this build in "Additional Context" section) + - type: dropdown + id: browsers + attributes: + label: Browser + description: Which browsers have you seen the problem on? + multiple: true + options: + - Chrome + - Firefox + - Microsoft Edge + - Brave + - Other (please elaborate in the "Additional Context" section) + validations: + required: true + - type: dropdown + id: os + attributes: + label: Operating system + description: Which operating systems have you seen the problem on? + multiple: true + options: + - Windows + - MacOS + - Linux + - Other (please elaborate in the "Additional Context" section) + validations: + required: true + - type: dropdown + id: hardware-wallet + attributes: + label: Hardware wallet + description: Are you using any of these hardware wallets? Please include the firmware version in the "Additional context" section below for any that you select here. + multiple: true + options: + - Ledger + - Trezor + - Keystone + - GridPlus Lattice1 + - Other (please elaborate in the "Additional Context" section) + - type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context about the problem here, e.g. related issues, additional error messages or logs, or any potentially relevant details about the environment or situation the bug occurred in. diff --git a/.github/workflows/crowdin_action.yml b/.github/workflows/crowdin_action.yml index fce3edce5..ca2e00677 100644 --- a/.github/workflows/crowdin_action.yml +++ b/.github/workflows/crowdin_action.yml @@ -26,6 +26,6 @@ jobs: upload_translations: true download_translations: true env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.METAMASKBOT_CROWDIN_TOKEN }} CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index c33171c0e..9512aa32e 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Προσθήκη σημειώματος" }, - "addNFT": { - "message": "Προσθήκη NFT" - }, "addNetwork": { "message": "Προσθήκη Δικτύου" }, @@ -1296,9 +1293,6 @@ "high": { "message": "Επιθετική" }, - "highGasSettingToolTipDialog": { - "message": "Υψηλή πιθανότητα, ακόμη και σε ευμετάβλητες αγορές" - }, "highGasSettingToolTipMessage": { "message": "Χρησιμοποιήστε $1 για να καλύψετε απότομες αυξήσεις της κίνησης του δικτύου λόγω των δημοφιλών ξεκινημάτων NFT.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "Ιστορικό" }, - "id": { - "message": "Αναγνωριστικό" - }, "import": { "message": "Εισαγωγή", "description": "Button to import an account from a selected file" diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 4923b9650..fde592a29 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Add memo" }, - "addNFT": { - "message": "Add NFT" - }, "addNetwork": { "message": "Add Network" }, @@ -458,6 +455,10 @@ "close": { "message": "Close" }, + "collectibleAddressError": { + "message": "This token is an NFT. Add on the $1", + "description": "$1 is a clickable link with text defined by the 'importNFTPage' key" + }, "confirm": { "message": "Confirm" }, @@ -595,6 +596,9 @@ "contractInteraction": { "message": "Contract Interaction" }, + "convertTokenToNFTDescription": { + "message": "We've detected that this asset is an NFT. Metamask now has full native support for NFTs. Would you like to remove it from your token list and add it as an NFT?" + }, "copiedExclamation": { "message": "Copied!" }, @@ -652,6 +656,15 @@ "currentlyUnavailable": { "message": "Unavailable on this network" }, + "curveHighGasEstimate": { + "message": "Aggressive gas estimate graph" + }, + "curveLowGasEstimate": { + "message": "Low gas estimate graph" + }, + "curveMediumGasEstimate": { + "message": "Market gas estimate graph" + }, "custom": { "message": "Advanced" }, @@ -659,7 +672,7 @@ "message": "Customize Gas" }, "customGasSettingToolTipMessage": { - "message": "Use $1 to customise the gas price. This can be confusing if you aren’t familiar. Interact at your own risk.", + "message": "Use $1 to customize the gas price. This can be confusing if you aren’t familiar. Interact at your own risk.", "description": "$1 is key 'advanced' (text: 'Advanced') separated here so that it can be passed in with bold fontweight" }, "customGasSubTitle": { @@ -933,6 +946,22 @@ "enableAutoDetect": { "message": " Enable Autodetect" }, + "enableEIP1559V2": { + "message": "Enable Enhanced Gas Fee UI" + }, + "enableEIP1559V2AlertMessage": { + "message": "We've updated how gas fee estimation and customization works." + }, + "enableEIP1559V2ButtonText": { + "message": "Turn on Enhanced Gas Fee UI in Settings" + }, + "enableEIP1559V2Description": { + "message": "We've updated how gas estimation and customization works. Turn on if you'd like to use the new gas experience. $1", + "description": "$1 here is Learn More link" + }, + "enableEIP1559V2Header": { + "message": "New gas experience" + }, "enableFromSettings": { "message": " Enable it from Settings." }, @@ -1190,6 +1219,9 @@ "gasLimitInfoTooltipContent": { "message": "Gas limit is the maximum amount of units of gas you are willing to spend." }, + "gasLimitRecommended": { + "message": "Recommended gas limit is $1. If the gas limit is less than that, it may fail." + }, "gasLimitTooLow": { "message": "Gas limit must be at least 21000" }, @@ -1337,11 +1369,8 @@ "high": { "message": "Aggressive" }, - "highGasSettingToolTipDialog": { - "message": "High probability, even in volatile markets" - }, "highGasSettingToolTipMessage": { - "message": "Use $1 to cover surges in network traffic due to things like popular NFT drops.", + "message": "High probability, even in volatile markets. Use $1 to cover surges in network traffic due to things like popular NFT drops.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" }, "highLowercase": { @@ -1350,9 +1379,6 @@ "history": { "message": "History" }, - "id": { - "message": "ID" - }, "import": { "message": "Import", "description": "Button to import an account from a selected file" @@ -1386,6 +1412,18 @@ "importMyWallet": { "message": "Import My Wallet" }, + "importNFT": { + "message": "Import NFT" + }, + "importNFTAddressToolTip": { + "message": "On OpenSea, for example, on the NFT's page under Details, there is a blue hyperlinked value labeled 'Contract Address'. If you click on this, it will take you to the contract's address on Etherscan; at the top-left of that page, there should be an icon labeled 'Contract', and to the right, a long string of letters and numbers. This is the address of the contract that created your NFT. Click on the 'copy' icon to the right of the address, and you'll have it on your clipboard." + }, + "importNFTPage": { + "message": "Import NFT page" + }, + "importNFTTokenIdToolTip": { + "message": "A collectible's ID is a unique identifier since no two NFTs are alike. Again, on OpenSea this number is under 'Details'. Make a note of it, or copy it onto your clipboard." + }, "importNFTs": { "message": "Import NFTs" }, @@ -1439,6 +1477,9 @@ "invalidAddressRecipientNotEthNetwork": { "message": "Not ETH network, set to lowercase" }, + "invalidAssetType": { + "message": "This asset is an NFT and needs to be re-added on the Import NFTs page found under the NFTs tab" + }, "invalidBlockExplorerURL": { "message": "Invalid Block Explorer URL" }, @@ -1619,7 +1660,7 @@ "message": "low" }, "lowPriorityMessage": { - "message": "Future transactions will queue after this one. This price was last seen was some time ago." + "message": "Future transactions will queue after this one." }, "mainnet": { "message": "Ethereum Mainnet" @@ -1891,7 +1932,7 @@ "description": "The next nonce according to MetaMask's internal logic" }, "nftTokenIdPlaceholder": { - "message": "Enter the collectible ID" + "message": "Enter the Token ID" }, "nfts": { "message": "NFTs" @@ -2155,7 +2196,7 @@ "message": "Passwords Don't Match" }, "pastePrivateKey": { - "message": "Paste your private key string here:", + "message": "Enter your private key string here:", "description": "For importing an account from a private key" }, "pending": { @@ -2467,7 +2508,7 @@ "message": "Separate each word with a single space" }, "seedPhrasePlaceholderPaste": { - "message": "Paste Secret Recovery Phrase from clipboard" + "message": "Enter your Secret Recovery Phrase" }, "seedPhraseReq": { "message": "Secret Recovery Phrases contain 12, 15, 18, 21, or 24 words" @@ -3508,6 +3549,9 @@ "message": "$1 of $2 pending", "description": "$1 and $2 are intended to be two numbers, where $2 is a total number of pending confirmations, and $1 is a count towards that total" }, + "yes": { + "message": "Yes" + }, "yesLetsTry": { "message": "Yes, let's try" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index a01a75db8..7d3fe77dc 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Ajouter un mémo" }, - "addNFT": { - "message": "Ajouter un NFT" - }, "addNetwork": { "message": "Ajouter un réseau" }, @@ -1296,9 +1293,6 @@ "high": { "message": "Agressif" }, - "highGasSettingToolTipDialog": { - "message": "Probabilité élevée, même sur des marchés volatils" - }, "highGasSettingToolTipMessage": { "message": "Utilisez $1 pour couvrir les envolées du trafic réseau dues à des événements tels que les chutes de NFT populaires.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "Historique" }, - "id": { - "message": "ID" - }, "import": { "message": "Importer", "description": "Button to import an account from a selected file" diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 580fbdd35..c70180b6c 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "मेमो जोड़ें" }, - "addNFT": { - "message": "NFT जोड़ें" - }, "addNetwork": { "message": "नेटवर्क जोड़ें" }, @@ -1296,9 +1293,6 @@ "high": { "message": "आक्रामक" }, - "highGasSettingToolTipDialog": { - "message": "उच्च संभावना, अस्थिर बाजारों में भी" - }, "highGasSettingToolTipMessage": { "message": "लोकप्रिय NFT ड्रॉप जैसी चीज़ों की वजह से नेटवर्क ट्रैफिक में वृद्धि को कवर करने के लिए $1 का उपयोग करें।", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "इतिहास" }, - "id": { - "message": "ID" - }, "import": { "message": "आयात करें", "description": "Button to import an account from a selected file" diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index 18ac64d24..624d4f4b3 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Tambahkan memo" }, - "addNFT": { - "message": "Tambahkan NFT" - }, "addNetwork": { "message": "Tambahkan Jaringan" }, @@ -1296,9 +1293,6 @@ "high": { "message": "Agresif" }, - "highGasSettingToolTipDialog": { - "message": "Probabilitas tinggi, bahkan di pasar yang tidak stabil" - }, "highGasSettingToolTipMessage": { "message": "Gunakan $1 untuk menutupi lonjakan lalu lintas jaringan karena hal-hal seperti penurunan NFT populer.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "Riwayat" }, - "id": { - "message": "ID" - }, "import": { "message": "Impor", "description": "Button to import an account from a selected file" diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index 381b4c943..aa098370c 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "メモを追加" }, - "addNFT": { - "message": "NFTを追加" - }, "addNetwork": { "message": "ネットワークの追加" }, @@ -1296,9 +1293,6 @@ "high": { "message": "積極的" }, - "highGasSettingToolTipDialog": { - "message": "変動の激しい市場においても高い確率" - }, "highGasSettingToolTipMessage": { "message": "人気のNFTドロップなどによるネットワークトラフィックの急増に備えるため、$1を使用してください。", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "履歴" }, - "id": { - "message": "ID" - }, "import": { "message": "インポート", "description": "Button to import an account from a selected file" diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index d2ab73621..d6663d856 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "메모 추가" }, - "addNFT": { - "message": "NFT 추가" - }, "addNetwork": { "message": "네트워크 추가" }, @@ -1296,9 +1293,6 @@ "high": { "message": "공격적" }, - "highGasSettingToolTipDialog": { - "message": "변동성이 큰 시장에서도 높은 확률" - }, "highGasSettingToolTipMessage": { "message": "인기 있는 NFT의 하락 등으로 인한 네트워크 트래픽 급증을 커버하려면 $1을 사용하세요.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "기록" }, - "id": { - "message": "ID" - }, "import": { "message": "가져오기", "description": "Button to import an account from a selected file" diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index e438e24b0..0b2d31fd2 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Добавить примечание" }, - "addNFT": { - "message": "Добавить NFT" - }, "addNetwork": { "message": "Добавить сеть" }, @@ -1296,9 +1293,6 @@ "high": { "message": "Агрессивный" }, - "highGasSettingToolTipDialog": { - "message": "Высокая вероятность даже на волатильных рынках" - }, "highGasSettingToolTipMessage": { "message": "Используйте $1, чтобы компенсировать скачки сетевого трафика из-за таких событий, как дропы популярных NFT.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "История" }, - "id": { - "message": "Ид." - }, "import": { "message": "Импорт", "description": "Button to import an account from a selected file" diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index 5f9eb4d03..7b9460350 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Magdagdag ng memo" }, - "addNFT": { - "message": "Magdagdag ng NFT" - }, "addNetwork": { "message": "Magdagdag ng Network" }, @@ -1296,9 +1293,6 @@ "high": { "message": "Agresibo" }, - "highGasSettingToolTipDialog": { - "message": "Mataas na probabilidad, kahit sa mga pabagu-bagong market" - }, "highGasSettingToolTipMessage": { "message": "Gamitin ang $1 upang pagtakpan ang mga surge sa network traffic dahil sa mga bagay tulad ng popular na pagbagsak ng NFT.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "History" }, - "id": { - "message": "ID" - }, "import": { "message": "Mag-import", "description": "Button to import an account from a selected file" diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 2fc5cba1c..fb58a4f41 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Not ekleyin" }, - "addNFT": { - "message": "NFT ekleyin" - }, "addNetwork": { "message": "Ağ ekleyin" }, @@ -1296,9 +1293,6 @@ "high": { "message": "Agresif" }, - "highGasSettingToolTipDialog": { - "message": "Dalgalı piyasalarda dahi yüksek olasılık" - }, "highGasSettingToolTipMessage": { "message": "Popüler NFT düşüşleri gibi şeyler nedeniyle ağ trafiğindeki dalgalanmaları kapsayacak şekilde $1 kullanın.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "Geçmiş" }, - "id": { - "message": "Kimlik" - }, "import": { "message": "Al", "description": "Button to import an account from a selected file" diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index 060c5e485..b3d64447e 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "Thêm bản ghi nhớ" }, - "addNFT": { - "message": "Thêm NFT" - }, "addNetwork": { "message": "Thêm mạng" }, @@ -1296,9 +1293,6 @@ "high": { "message": "Linh hoạt" }, - "highGasSettingToolTipDialog": { - "message": "Có khả năng cao, ngay cả trong thị trường biến động" - }, "highGasSettingToolTipMessage": { "message": "Sử dụng $1 để bù đắp khi lưu lượng mạng lưới tăng vọt trong những trường hợp như phát hành NFT nổi tiếng.", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "Lịch sử" }, - "id": { - "message": "ID" - }, "import": { "message": "Nhập", "description": "Button to import an account from a selected file" diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index d1fb4e637..a2d28f57e 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -140,9 +140,6 @@ "addMemo": { "message": "添加备忘录" }, - "addNFT": { - "message": "添加NFT" - }, "addNetwork": { "message": "添加网络" }, @@ -1296,9 +1293,6 @@ "high": { "message": "进取" }, - "highGasSettingToolTipDialog": { - "message": "高概率,即使在不稳定的市场中也是如此。" - }, "highGasSettingToolTipMessage": { "message": "使用$1来覆盖网络流量因像流行的 NFT 丢弃而出现的剧增。", "description": "$1 is key 'high' (text: 'Aggressive') separated here so that it can be passed in with bold fontweight" @@ -1309,9 +1303,6 @@ "history": { "message": "历史记录" }, - "id": { - "message": "ID" - }, "import": { "message": "导入", "description": "Button to import an account from a selected file" diff --git a/app/images/curve-high.svg b/app/images/curve-high.svg index f5c918636..bd6d8d0ce 100644 --- a/app/images/curve-high.svg +++ b/app/images/curve-high.svg @@ -1 +1,25 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/images/curve-low.svg b/app/images/curve-low.svg index fee21216e..9f98826ea 100644 --- a/app/images/curve-low.svg +++ b/app/images/curve-low.svg @@ -1 +1,20 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + diff --git a/app/images/curve-medium.svg b/app/images/curve-medium.svg index c3cc1d2aa..9a454bc99 100644 --- a/app/images/curve-medium.svg +++ b/app/images/curve-medium.svg @@ -1 +1,16 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + diff --git a/app/images/videos/recovery-onboarding/subtitles/de.vtt b/app/images/videos/recovery-onboarding/subtitles/de.vtt new file mode 100644 index 000000000..9b5612285 --- /dev/null +++ b/app/images/videos/recovery-onboarding/subtitles/de.vtt @@ -0,0 +1,116 @@ +WEBVTT + +1 +00:00:00.780 --> 00:00:04.580 +MetaMask ist eine neue Möglichkeit, sich mit +Webseiten und Anwendungen zu verbinden. + +2 +00:00:04.580 --> 00:00:08.860 +Auf traditionellen Webseiten ist eine +zentrale Datenbank für die Steuerung und + +3 +00:00:08.860 --> 00:00:10.179 +Wiederherstellung der Konten zuständig. + +4 +00:00:10.179 --> 00:00:15.050 +Bei MetaMask gehört all diese Macht dem +Besitzer eines „Hauptschlüssels“. + +5 +00:00:15.050 --> 00:00:18.460 +Wer den Schlüssel besitzt, kontrolliert +das Wallet und damit die Konten. + +6 +00:00:18.460 --> 00:00:21.110 +Der geheime Satz zur Wiederherstellung +Deines Wallets ist der „Hauptschlüssel“. + +7 +00:00:21.110 --> 00:00:26.070 +Es ist eine Reihe von 12 Wörtern, welche generiert werden, +wenn MetaMask zum ersten Mal eingerichtet wird, diese erlauben dir + +8 +00:00:26.070 --> 00:00:30.120 +Deinen „Hauptschlüssel“ zu Deinem Wallet wieder herzustellen, +solltest Du jemals den Zugriff darauf verlieren. + +9 +00:00:30.120 --> 00:00:33.451 +Es ist wichtig, dass Du Dein Wallet sicherst, +indem Du Deinen geheimen + +10 +00:00:33.451 --> 00:00:37.510 +Wiederherstellungssatz sicher +und geheim verwahrst. + +11 +00:00:37.510 --> 00:00:41.429 +Wenn jemand Zugriff darauf erhält, hat er +den „Hauptschlüssel“ für Dein Wallet und kann + +12 +00:00:41.429 --> 00:00:45.190 +frei auf Deine Konten zugreifen und alle Gelder stehlen. + +13 +00:00:45.190 --> 00:00:50.109 +Um Dein MetaMask-Wallet zu sichern, musst Du Deinen +geheimen Wiederherstellungssatz sicher speichern. + +14 +00:00:50.109 --> 00:00:54.930 +Du kannst ihn aufschreiben, irgendwo verstecken, +in ein Schließfach legen + +15 +00:00:54.930 --> 00:00:57.729 +oder in einen sicheren Passwort-Manager speichern. + +16 +00:00:57.729 --> 00:01:01.050 +Einige Benutzer gravieren ihren +Satz sogar auf eine Metallplatte. + +17 +00:01:01.050 --> 00:01:04.440 +Niemand, nicht einmal das Team von MetaMask, +kann Dir dabei helfen + +18 +00:01:04.440 --> 00:01:07.820 +Dein Wallet wiederherzustellen, wenn Du Deinen +geheimen Wiederherstellungssatz verlierst. + +19 +00:01:07.820 --> 00:01:12.072 +Wenn Du Deinen geheimen Wiederherstellungssatz noch nicht +aufgeschrieben und an einem sicheren Ort aufbewahrt hast, + +20 +00:01:12.072 --> 00:01:15.492 +mach es jetzt. Wir warten solange. + +21 +00:01:15.500 --> 00:01:20.780 +Und denk daran, teilen Deinen geheimen Wiederherstellungssatz +niemals: nicht einmal mit uns. + +22 +00:01:20.780 --> 00:01:24.910 +Wenn dich jemals jemand danach fragt, +versucht er dich zu betrügen. + +23 +00:01:24.910 --> 00:01:26.250 +Das ist alles! + +24 +00:01:26.250 --> 00:01:31.020 +Jetzt weist Du, was ein geheimer Wiederherstellungssatz ist +und wie Du dafür sorgst, das Dein Wallete sicher bleibt. diff --git a/app/phishing.html b/app/phishing.html index 30accb9bf..ae9d125da 100644 --- a/app/phishing.html +++ b/app/phishing.html @@ -1,7 +1,7 @@ - Ethereum Phishing Detection - MetaMask + MetaMask Phishing Detection @@ -37,7 +37,7 @@

- Ethereum Phishing Detection + MetaMask Phishing Detection

diff --git a/app/scripts/controllers/app-state.js b/app/scripts/controllers/app-state.js index 4d9ef9ebe..6ca948aa0 100644 --- a/app/scripts/controllers/app-state.js +++ b/app/scripts/controllers/app-state.js @@ -31,10 +31,12 @@ export default class AppStateController extends EventEmitter { recoveryPhraseReminderHasBeenShown: false, recoveryPhraseReminderLastShown: new Date().getTime(), collectiblesDetectionNoticeDismissed: false, + enableEIP1559V2NoticeDismissed: false, showTestnetMessageInDropdown: true, trezorModel: null, ...initState, qrHardware: {}, + collectiblesDropdownState: {}, }); this.timer = null; @@ -270,4 +272,26 @@ export default class AppStateController extends EventEmitter { collectiblesDetectionNoticeDismissed, }); } + + /** + * A setter for the `enableEIP1559V2NoticeDismissed` property + * + * @param enableEIP1559V2NoticeDismissed + */ + setEnableEIP1559V2NoticeDismissed(enableEIP1559V2NoticeDismissed) { + this.store.updateState({ + enableEIP1559V2NoticeDismissed, + }); + } + + /** + * A setter for the `collectiblesDropdownState` property + * + * @param collectiblesDropdownState + */ + updateCollectibleDropDownState(collectiblesDropdownState) { + this.store.updateState({ + collectiblesDropdownState, + }); + } } diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index 4352a09cf..e88b8c721 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -1,11 +1,13 @@ -import { merge, omit } from 'lodash'; +import { merge, omit, omitBy } from 'lodash'; import { ObservableStore } from '@metamask/obs-store'; import { bufferToHex, keccak } from 'ethereumjs-util'; +import { generateUUID } from 'pubnub'; import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app'; import { METAMETRICS_ANONYMOUS_ID, METAMETRICS_BACKGROUND_PAGE_OBJECT, } from '../../../shared/constants/metametrics'; +import { SECOND } from '../../../shared/constants/time'; const defaultCaptureException = (err) => { // throw error on clean stack so its captured by platform integrations (eg sentry) @@ -27,15 +29,18 @@ const exceptionsToFilter = { * @typedef {import('../../../shared/constants/metametrics').SegmentInterface} SegmentInterface * @typedef {import('../../../shared/constants/metametrics').MetaMetricsPagePayload} MetaMetricsPagePayload * @typedef {import('../../../shared/constants/metametrics').MetaMetricsPageOptions} MetaMetricsPageOptions + * @typedef {import('../../../shared/constants/metametrics').MetaMetricsEventFragment} MetaMetricsEventFragment */ /** * @typedef {Object} MetaMetricsControllerState - * @property {?string} metaMetricsId - The user's metaMetricsId that will be + * @property {string} [metaMetricsId] - The user's metaMetricsId that will be * attached to all non-anonymized event payloads - * @property {?boolean} participateInMetaMetrics - The user's preference for + * @property {boolean} [participateInMetaMetrics] - The user's preference for * participating in the MetaMetrics analytics program. This setting controls * whether or not events are tracked + * @property {{[string]: MetaMetricsEventFragment}} [fragments] - Object keyed + * by UUID with stored fragments as values. */ export default class MetaMetricsController { @@ -81,10 +86,15 @@ export default class MetaMetricsController { this.version = environment === 'production' ? version : `${version}-${environment}`; + const abandonedFragments = omitBy(initState?.fragments, 'persist'); + this.store = new ObservableStore({ participateInMetaMetrics: null, metaMetricsId: null, ...initState, + fragments: { + ...initState?.fragments, + }, }); preferencesStore.subscribe(({ currentLocale }) => { @@ -96,6 +106,32 @@ export default class MetaMetricsController { this.network = getNetworkIdentifier(); }); this.segment = segment; + + // Track abandoned fragments that weren't properly cleaned up. + // Abandoned fragments are those that were stored in persistent memory + // and are available at controller instance creation, but do not have the + // 'persist' flag set. This means anytime the extension is unlocked, any + // fragments that are not marked as persistent will be purged and the + // failure event will be emitted. + Object.values(abandonedFragments).forEach((fragment) => { + this.finalizeEventFragment(fragment.id, { abandoned: true }); + }); + + // Close out event fragments that were created but not progressed. An + // interval is used to routinely check if a fragment has not been updated + // within the fragment's timeout window. When creating a new event fragment + // a timeout can be specified that will cause an abandoned event to be + // tracked if the event isn't progressed within that amount of time. + setInterval(() => { + Object.values(this.store.getState().fragments).forEach((fragment) => { + if ( + fragment.timeout && + Date.now() - fragment.lastUpdated / 1000 > fragment.timeout + ) { + this.finalizeEventFragment(fragment.id, { abandoned: true }); + } + }); + }, SECOND * 30); } generateMetaMetricsId() { @@ -109,6 +145,141 @@ export default class MetaMetricsController { ); } + /** + * Create an event fragment in state and returns the event fragment object. + * + * @param {MetaMetricsEventFragment} options - Fragment settings and properties + * to initiate the fragment with. + * @returns {MetaMetricsEventFragment} + */ + createEventFragment(options) { + if (!options.successEvent || !options.category) { + throw new Error( + `Must specify success event and category. Success event was: ${ + options.event + }. Category was: ${options.category}. Payload keys were: ${Object.keys( + options, + )}. ${ + typeof options.properties === 'object' + ? `Payload property keys were: ${Object.keys(options.properties)}` + : '' + }`, + ); + } + const { fragments } = this.store.getState(); + + const id = options.uniqueIdentifier ?? generateUUID(); + const fragment = { + id, + ...options, + lastUpdated: Date.now(), + }; + this.store.updateState({ + fragments: { + ...fragments, + [id]: fragment, + }, + }); + + if (options.initialEvent) { + this.trackEvent({ + event: fragment.initialEvent, + category: fragment.category, + properties: fragment.properties, + sensitiveProperties: fragment.sensitiveProperties, + page: fragment.page, + referrer: fragment.referrer, + revenue: fragment.revenue, + value: fragment.value, + currency: fragment.currency, + environmentType: fragment.environmentType, + }); + } + + return fragment; + } + + /** + * Returns the fragment stored in memory with provided id or undefined if it + * does not exist. + * + * @param {string} id - id of fragment to retrieve + * @returns {[MetaMetricsEventFragment]} + */ + getEventFragmentById(id) { + const { fragments } = this.store.getState(); + + const fragment = fragments[id]; + + return fragment; + } + + /** + * Updates an event fragment in state + * + * @param {string} id - The fragment id to update + * @param {MetaMetricsEventFragment} payload - Fragment settings and + * properties to initiate the fragment with. + */ + updateEventFragment(id, payload) { + const { fragments } = this.store.getState(); + + const fragment = fragments[id]; + + if (!fragment) { + throw new Error(`Event fragment with id ${id} does not exist.`); + } + + this.store.updateState({ + fragments: { + ...fragments, + [id]: merge(fragments[id], { + ...payload, + lastUpdated: Date.now(), + }), + }, + }); + } + + /** + * Finalizes a fragment, tracking either a success event or failure Event + * and then removes the fragment from state. + * + * @param {string} id - UUID of the event fragment to be closed + * @param {object} options + * @param {boolean} [options.abandoned] - if true track the failure + * event instead of the success event + * @param {MetaMetricsContext.page} [options.page] - page the final event + * occurred on. This will override whatever is set on the fragment + * @param {MetaMetricsContext.referrer} [options.referrer] - Dapp that + * originated the fragment. This is for fallback only, the fragment referrer + * property will take precedence. + */ + finalizeEventFragment(id, { abandoned = false, page, referrer } = {}) { + const fragment = this.store.getState().fragments[id]; + if (!fragment) { + throw new Error(`Funnel with id ${id} does not exist.`); + } + + const eventName = abandoned ? fragment.failureEvent : fragment.successEvent; + + this.trackEvent({ + event: eventName, + category: fragment.category, + properties: fragment.properties, + sensitiveProperties: fragment.sensitiveProperties, + page: page ?? fragment.page, + referrer: fragment.referrer ?? referrer, + revenue: fragment.revenue, + value: fragment.value, + currency: fragment.currency, + environmentType: fragment.environmentType, + }); + const { fragments } = this.store.getState(); + delete fragments[id]; + this.store.updateState({ fragments }); + } + /** * Setter for the `participateInMetaMetrics` property * diff --git a/app/scripts/controllers/metametrics.test.js b/app/scripts/controllers/metametrics.test.js index 9918d6319..a5c2dc1f5 100644 --- a/app/scripts/controllers/metametrics.test.js +++ b/app/scripts/controllers/metametrics.test.js @@ -81,6 +81,28 @@ function getMockPreferencesStore({ currentLocale = LOCALE } = {}) { }; } +const SAMPLE_PERSISTED_EVENT = { + id: 'testid', + persist: true, + category: 'Unit Test', + successEvent: 'sample persisted event success', + failureEvent: 'sample persisted event failure', + properties: { + test: true, + }, +}; + +const SAMPLE_NON_PERSISTED_EVENT = { + id: 'testid2', + persist: false, + category: 'Unit Test', + successEvent: 'sample non-persisted event success', + failureEvent: 'sample non-persisted event failure', + properties: { + test: true, + }, +}; + function getMetaMetricsController({ participateInMetaMetrics = true, metaMetricsId = TEST_META_METRICS_ID, @@ -105,12 +127,29 @@ function getMetaMetricsController({ initState: { participateInMetaMetrics, metaMetricsId, + fragments: { + testid: SAMPLE_PERSISTED_EVENT, + testid2: SAMPLE_NON_PERSISTED_EVENT, + }, }, }); } describe('MetaMetricsController', function () { describe('constructor', function () { it('should properly initialize', function () { + const mock = sinon.mock(segment); + mock + .expects('track') + .once() + .withArgs({ + event: 'sample non-persisted event failure', + userId: TEST_META_METRICS_ID, + context: DEFAULT_TEST_CONTEXT, + properties: { + ...DEFAULT_EVENT_PROPERTIES, + test: true, + }, + }); const metaMetricsController = getMetaMetricsController(); assert.strictEqual(metaMetricsController.version, VERSION); assert.strictEqual(metaMetricsController.network, NETWORK); @@ -127,6 +166,10 @@ describe('MetaMetricsController', function () { metaMetricsController.locale, LOCALE.replace('_', '-'), ); + assert.deepStrictEqual(metaMetricsController.state.fragments, { + testid: SAMPLE_PERSISTED_EVENT, + }); + mock.verify(); }); it('should update when network changes', function () { diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index c51e7d46a..037359453 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -167,6 +167,15 @@ export default class PreferencesController { this.store.updateState({ advancedGasFee: val }); } + /** + * Setter for the `eip1559V2Enabled` property + * + * @param {object} val - holds the eip1559V2Enabled that the user set as experimental settings. + */ + setEIP1559V2Enabled(val) { + this.store.updateState({ eip1559V2Enabled: val }); + } + /** * 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 555d892e3..771fc0ee7 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -25,6 +25,7 @@ import { TRANSACTION_STATUSES, TRANSACTION_TYPES, TRANSACTION_ENVELOPE_TYPES, + TRANSACTION_EVENTS, } from '../../../../shared/constants/transaction'; import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../ui/helpers/constants/transactions'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; @@ -54,13 +55,10 @@ const hstInterface = new ethers.utils.Interface(abi); const MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory -export const TRANSACTION_EVENTS = { - ADDED: 'Transaction Added', - APPROVED: 'Transaction Approved', - FINALIZED: 'Transaction Finalized', - REJECTED: 'Transaction Rejected', - SUBMITTED: 'Transaction Submitted', -}; +/** + * @typedef {import('../../../../shared/constants/transaction').TransactionMeta} TransactionMeta + * @typedef {import('../../../../shared/constants/transaction').TransactionMetaMetricsEventString} TransactionMetaMetricsEventString + */ /** * @typedef {Object} CustomGasSettings @@ -118,6 +116,10 @@ export default class TransactionController extends EventEmitter { this._trackMetaMetricsEvent = opts.trackMetaMetricsEvent; this._getParticipateInMetrics = opts.getParticipateInMetrics; this._getEIP1559GasFeeEstimates = opts.getEIP1559GasFeeEstimates; + this.createEventFragment = opts.createEventFragment; + this.updateEventFragment = opts.updateEventFragment; + this.finalizeEventFragment = opts.finalizeEventFragment; + this.getEventFragmentById = opts.getEventFragmentById; this.memStore = new ObservableStore({}); this.query = new EthQuery(this.provider); @@ -441,7 +443,8 @@ export default class TransactionController extends EventEmitter { } if (eip1559Compatibility) { - if (process.env.EIP_1559_V2 && Boolean(advancedGasFeeDefaultValues)) { + const { eip1559V2Enabled } = this.preferencesStore.getState(); + if (eip1559V2Enabled && Boolean(advancedGasFeeDefaultValues)) { txMeta.userFeeLevel = CUSTOM_GAS_ESTIMATE; txMeta.txParams.maxFeePerGas = decGWEIToHexWEI( advancedGasFeeDefaultValues.maxBaseFee, @@ -458,7 +461,7 @@ export default class TransactionController extends EventEmitter { // then we set maxFeePerGas and maxPriorityFeePerGas to the suggested gasPrice. txMeta.txParams.maxFeePerGas = txMeta.txParams.gasPrice; txMeta.txParams.maxPriorityFeePerGas = txMeta.txParams.gasPrice; - if (process.env.EIP_1559_V2) { + if (eip1559V2Enabled) { txMeta.userFeeLevel = PRIORITY_LEVELS.DAPP_SUGGESTED; } else { txMeta.userFeeLevel = CUSTOM_GAS_ESTIMATE; @@ -472,7 +475,7 @@ export default class TransactionController extends EventEmitter { txMeta.origin === 'metamask' ) { txMeta.userFeeLevel = GAS_RECOMMENDATIONS.MEDIUM; - } else if (process.env.EIP_1559_V2) { + } else if (eip1559V2Enabled) { txMeta.userFeeLevel = PRIORITY_LEVELS.DAPP_SUGGESTED; } else { txMeta.userFeeLevel = CUSTOM_GAS_ESTIMATE; @@ -538,6 +541,7 @@ export default class TransactionController extends EventEmitter { if (defaultGasLimit && !txMeta.txParams.gas) { txMeta.txParams.gas = defaultGasLimit; + txMeta.originalGasEstimate = defaultGasLimit; } return txMeta; } @@ -661,9 +665,8 @@ export default class TransactionController extends EventEmitter { * which is defined by specifying a numerator. 11 is a 10% bump, 12 would be * a 20% bump, and so on. * - * @param {import( - * '../../../../shared/constants/transaction' - * ).TransactionMeta} originalTxMeta - Original transaction to use as base + * @param {TransactionMeta} originalTxMeta - Original transaction to use as + * base * @param {CustomGasSettings} [customGasSettings] - overrides for the gas * fields to use instead of the multiplier * @param {number} [incrementNumerator] - Numerator from which to generate a @@ -1119,6 +1122,28 @@ export default class TransactionController extends EventEmitter { this.txStateManager.updateTransaction(txMeta, 'transactions#setTxHash'); } + /** + * Convenience method for the UI to easily create event fragments when the + * fragment does not exist in state. + * + * @param {number} transactionId - The transaction id to create the event + * fragment for + * @param {valueOf} event - event type to create + */ + createTransactionEventFragment(transactionId, event) { + const txMeta = this.txStateManager.getTransaction(transactionId); + const { + properties, + sensitiveProperties, + } = this._buildEventFragmentProperties(txMeta); + this._createTransactionEventFragment( + txMeta, + event, + properties, + sensitiveProperties, + ); + } + // // PRIVATE METHODS // @@ -1449,20 +1474,7 @@ export default class TransactionController extends EventEmitter { } } - /** - * Extracts relevant properties from a transaction meta - * object and uses them to create and send metrics for various transaction - * events. - * - * @param {Object} txMeta - the txMeta object - * @param {string} event - the name of the transaction event - * @param {Object} extraParams - optional props and values to include in sensitiveProperties - */ - _trackTransactionMetricsEvent(txMeta, event, extraParams = {}) { - if (!txMeta) { - return; - } - + _buildEventFragmentProperties(txMeta, extraParams) { const { type, time, @@ -1500,27 +1512,202 @@ export default class TransactionController extends EventEmitter { const gasParamsInGwei = this._getGasValuesInGWEI(gasParams); - this._trackMetaMetricsEvent({ + const properties = { + chain_id: chainId, + referrer, + source, + network, + type, + }; + + const sensitiveProperties = { + status, + transaction_envelope_type: isEIP1559Transaction(txMeta) + ? TRANSACTION_ENVELOPE_TYPE_NAMES.FEE_MARKET + : TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, + first_seen: time, + gas_limit: gasLimit, + ...gasParamsInGwei, + ...extraParams, + }; + + return { properties, sensitiveProperties }; + } + + /** + * Helper method that checks for the presence of an existing fragment by id + * appropriate for the type of event that triggered fragment creation. If the + * appropriate fragment exists, then nothing is done. If it does not exist a + * new event fragment is created with the appropriate payload. + * + * @param {TransactionMeta} txMeta - Transaction meta object + * @param {TransactionMetaMetricsEventString} event - The event type that + * triggered fragment creation + * @param {Object} properties - properties to include in the fragment + * @param {Object} [sensitiveProperties] - sensitive properties to include in + * the fragment + */ + _createTransactionEventFragment( + txMeta, + event, + properties, + sensitiveProperties, + ) { + const isSubmitted = [ + TRANSACTION_EVENTS.FINALIZED, + TRANSACTION_EVENTS.SUBMITTED, + ].includes(event); + const uniqueIdentifier = `transaction-${ + isSubmitted ? 'submitted' : 'added' + }-${txMeta.id}`; + + const fragment = this.getEventFragmentById(uniqueIdentifier); + if (typeof fragment !== 'undefined') { + return; + } + + switch (event) { + // When a transaction is added to the controller, we know that the user + // will be presented with a confirmation screen. The user will then + // either confirm or reject that transaction. Each has an associated + // event we want to track. While we don't necessarily need an event + // fragment to model this, having one allows us to record additional + // properties onto the event from the UI. For example, when the user + // edits the transactions gas params we can record that property and + // then get analytics on the number of transactions in which gas edits + // occur. + case TRANSACTION_EVENTS.ADDED: + this.createEventFragment({ + category: 'Transactions', + initialEvent: TRANSACTION_EVENTS.ADDED, + successEvent: TRANSACTION_EVENTS.APPROVED, + failureEvent: TRANSACTION_EVENTS.REJECTED, + properties, + sensitiveProperties, + persist: true, + uniqueIdentifier, + }); + break; + // If for some reason an approval or rejection occurs without the added + // fragment existing in memory, we create the added fragment but without + // the initialEvent firing. This is to prevent possible duplication of + // events. A good example why this might occur is if the user had + // unapproved transactions in memory when updating to the version that + // includes this change. A migration would have also helped here but this + // implementation hardens against other possible bugs where a fragment + // does not exist. + case TRANSACTION_EVENTS.APPROVED: + case TRANSACTION_EVENTS.REJECTED: + this.createEventFragment({ + category: 'Transactions', + successEvent: TRANSACTION_EVENTS.APPROVED, + failureEvent: TRANSACTION_EVENTS.REJECTED, + properties, + sensitiveProperties, + persist: true, + uniqueIdentifier, + }); + break; + // When a transaction is submitted it will always result in updating + // to a finalized state (dropped, failed, confirmed) -- eventually. + // However having a fragment started at this stage allows augmenting + // analytics data with user interactions such as speeding up and + // canceling the transactions. From this controllers perspective a new + // transaction with a new id is generated for speed up and cancel + // transactions, but from the UI we could augment the previous ID with + // supplemental data to show user intent. Such as when they open the + // cancel UI but don't submit. We can record that this happened and add + // properties to the transaction event. + case TRANSACTION_EVENTS.SUBMITTED: + this.createEventFragment({ + category: 'Transactions', + initialEvent: TRANSACTION_EVENTS.SUBMITTED, + successEvent: TRANSACTION_EVENTS.FINALIZED, + properties, + sensitiveProperties, + persist: true, + uniqueIdentifier, + }); + break; + // If for some reason a transaction is finalized without the submitted + // fragment existing in memory, we create the submitted fragment but + // without the initialEvent firing. This is to prevent possible + // duplication of events. A good example why this might occur is if th + // user had pending transactions in memory when updating to the version + // that includes this change. A migration would have also helped here but + // this implementation hardens against other possible bugs where a + // fragment does not exist. + case TRANSACTION_EVENTS.FINALIZED: + this.createEventFragment({ + category: 'Transactions', + successEvent: TRANSACTION_EVENTS.FINALIZED, + properties, + sensitiveProperties, + persist: true, + uniqueIdentifier, + }); + break; + default: + break; + } + } + + /** + * Extracts relevant properties from a transaction meta + * object and uses them to create and send metrics for various transaction + * events. + * + * @param {Object} txMeta - the txMeta object + * @param {TransactionMetaMetricsEventString} event - the name of the transaction event + * @param {Object} extraParams - optional props and values to include in sensitiveProperties + */ + _trackTransactionMetricsEvent(txMeta, event, extraParams = {}) { + if (!txMeta) { + return; + } + const { + properties, + sensitiveProperties, + } = this._buildEventFragmentProperties(txMeta, extraParams); + + // Create event fragments for event types that spawn fragments, and ensure + // existence of fragments for event types that act upon them. + this._createTransactionEventFragment( + txMeta, event, - category: 'Transactions', - properties: { - chain_id: chainId, - referrer, - source, - network, - type, - }, - sensitiveProperties: { - status, - transaction_envelope_type: isEIP1559Transaction(txMeta) - ? TRANSACTION_ENVELOPE_TYPE_NAMES.FEE_MARKET - : TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, - first_seen: time, - gas_limit: gasLimit, - ...gasParamsInGwei, - ...extraParams, - }, - }); + properties, + sensitiveProperties, + ); + + let id; + + switch (event) { + // If the user approves a transaction, finalize the transaction added + // event fragment. + case TRANSACTION_EVENTS.APPROVED: + id = `transaction-added-${txMeta.id}`; + this.updateEventFragment(id, { properties, sensitiveProperties }); + this.finalizeEventFragment(id); + break; + // If the user rejects a transaction, finalize the transaction added + // event fragment. with the abandoned flag set. + case TRANSACTION_EVENTS.REJECTED: + id = `transaction-added-${txMeta.id}`; + this.updateEventFragment(id, { properties, sensitiveProperties }); + this.finalizeEventFragment(id, { + abandoned: true, + }); + break; + // When a transaction is finalized, also finalize the transaction + // submitted event fragment. + case TRANSACTION_EVENTS.FINALIZED: + id = `transaction-submitted-${txMeta.id}`; + this.updateEventFragment(id, { properties, sensitiveProperties }); + this.finalizeEventFragment(`transaction-submitted-${txMeta.id}`); + break; + default: + break; + } } _getTransactionCompletionTime(submittedTime) { diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index c2f7a344b..81d113362 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -13,6 +13,7 @@ import { TRANSACTION_STATUSES, TRANSACTION_TYPES, TRANSACTION_ENVELOPE_TYPES, + TRANSACTION_EVENTS, } from '../../../../shared/constants/transaction'; import { SECOND } from '../../../../shared/constants/time'; @@ -22,7 +23,7 @@ import { } from '../../../../shared/constants/gas'; import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../ui/helpers/constants/transactions'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; -import TransactionController, { TRANSACTION_EVENTS } from '.'; +import TransactionController from '.'; const noop = () => true; const currentNetworkId = '42'; @@ -35,17 +36,21 @@ const VALID_ADDRESS = '0x0000000000000000000000000000000000000000'; const VALID_ADDRESS_TWO = '0x0000000000000000000000000000000000000001'; describe('Transaction Controller', function () { - let txController, provider, providerResultStub, fromAccount; + let txController, provider, providerResultStub, fromAccount, fragmentExists; beforeEach(function () { + fragmentExists = false; providerResultStub = { // 1 gwei eth_gasPrice: '0x0de0b6b3a7640000', // by default, all accounts are external accounts (not contracts) eth_getCode: '0x', }; - provider = createTestProviderTools({ scaffold: providerResultStub }) - .provider; + provider = createTestProviderTools({ + scaffold: providerResultStub, + networkId: currentNetworkId, + chainId: currentNetworkId, + }).provider; fromAccount = getTestAccounts()[0]; const blockTrackerStub = new EventEmitter(); @@ -70,6 +75,11 @@ describe('Transaction Controller', function () { getCurrentChainId: () => currentChainId, getParticipateInMetrics: () => false, trackMetaMetricsEvent: () => undefined, + createEventFragment: () => undefined, + updateEventFragment: () => undefined, + finalizeEventFragment: () => undefined, + getEventFragmentById: () => + fragmentExists === false ? undefined : { id: 0 }, getEIP1559GasFeeEstimates: () => undefined, }); txController.nonceTracker.getNonceLock = () => @@ -1536,66 +1546,325 @@ describe('Transaction Controller', function () { describe('#_trackTransactionMetricsEvent', function () { let trackMetaMetricsEventSpy; + let createEventFragmentSpy; + let finalizeEventFragmentSpy; beforeEach(function () { trackMetaMetricsEventSpy = sinon.spy( txController, '_trackMetaMetricsEvent', ); + + createEventFragmentSpy = sinon.spy(txController, 'createEventFragment'); + + finalizeEventFragmentSpy = sinon.spy( + txController, + 'finalizeEventFragment', + ); }); afterEach(function () { trackMetaMetricsEventSpy.restore(); + createEventFragmentSpy.restore(); + finalizeEventFragmentSpy.restore(); }); - it('should call _trackMetaMetricsEvent with the correct payload (user source)', function () { - const txMeta = { - id: 1, - status: TRANSACTION_STATUSES.UNAPPROVED, - txParams: { - from: fromAccount.address, - to: '0x1678a085c290ebd122dc42cba69373b5953b831d', - gasPrice: '0x77359400', - gas: '0x7b0d', - nonce: '0x4b', - }, - type: TRANSACTION_TYPES.SIMPLE_SEND, - origin: 'metamask', - chainId: currentChainId, - time: 1624408066355, - metamaskNetworkId: currentNetworkId, - }; - const expectedPayload = { - event: 'Transaction Added', - category: 'Transactions', - properties: { - chain_id: '0x2a', - network: '42', - referrer: 'metamask', - source: 'user', + describe('On transaction created by the user', function () { + let txMeta; + before(function () { + txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, type: TRANSACTION_TYPES.SIMPLE_SEND, - }, - sensitiveProperties: { - gas_price: '2', - gas_limit: '0x7b0d', - first_seen: 1624408066355, - transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, - status: 'unapproved', - }, - }; + origin: 'metamask', + chainId: currentChainId, + time: 1624408066355, + metamaskNetworkId: currentNetworkId, + }; + }); - txController._trackTransactionMetricsEvent( - txMeta, - TRANSACTION_EVENTS.ADDED, - ); - assert.equal(trackMetaMetricsEventSpy.callCount, 1); - assert.deepEqual( - trackMetaMetricsEventSpy.getCall(0).args[0], - expectedPayload, - ); + it('should create an event fragment when transaction added', function () { + const expectedPayload = { + initialEvent: 'Transaction Added', + successEvent: 'Transaction Approved', + failureEvent: 'Transaction Rejected', + uniqueIdentifier: 'transaction-added-1', + category: 'Transactions', + persist: true, + properties: { + chain_id: '0x2a', + network: '42', + referrer: 'metamask', + source: 'user', + type: TRANSACTION_TYPES.SIMPLE_SEND, + }, + sensitiveProperties: { + gas_price: '2', + gas_limit: '0x7b0d', + first_seen: 1624408066355, + transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, + status: 'unapproved', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.ADDED, + ); + assert.equal(createEventFragmentSpy.callCount, 1); + assert.equal(finalizeEventFragmentSpy.callCount, 0); + assert.deepEqual( + createEventFragmentSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('Should finalize the transaction added fragment as abandoned if user rejects transaction', function () { + fragmentExists = true; + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.REJECTED, + ); + assert.equal(createEventFragmentSpy.callCount, 0); + assert.equal(finalizeEventFragmentSpy.callCount, 1); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[0], + 'transaction-added-1', + ); + assert.deepEqual(finalizeEventFragmentSpy.getCall(0).args[1], { + abandoned: true, + }); + }); + + it('Should finalize the transaction added fragment if user approves transaction', function () { + fragmentExists = true; + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.APPROVED, + ); + assert.equal(createEventFragmentSpy.callCount, 0); + assert.equal(finalizeEventFragmentSpy.callCount, 1); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[0], + 'transaction-added-1', + ); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[1], + undefined, + ); + }); + + it('should create an event fragment when transaction is submitted', function () { + const expectedPayload = { + initialEvent: 'Transaction Submitted', + successEvent: 'Transaction Finalized', + uniqueIdentifier: 'transaction-submitted-1', + category: 'Transactions', + persist: true, + properties: { + chain_id: '0x2a', + network: '42', + referrer: 'metamask', + source: 'user', + type: TRANSACTION_TYPES.SIMPLE_SEND, + }, + sensitiveProperties: { + gas_price: '2', + gas_limit: '0x7b0d', + first_seen: 1624408066355, + transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, + status: 'unapproved', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.SUBMITTED, + ); + assert.equal(createEventFragmentSpy.callCount, 1); + assert.equal(finalizeEventFragmentSpy.callCount, 0); + assert.deepEqual( + createEventFragmentSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('Should finalize the transaction submitted fragment when transaction finalizes', function () { + fragmentExists = true; + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.FINALIZED, + ); + assert.equal(createEventFragmentSpy.callCount, 0); + assert.equal(finalizeEventFragmentSpy.callCount, 1); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[0], + 'transaction-submitted-1', + ); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[1], + undefined, + ); + }); }); - it('should call _trackMetaMetricsEvent with the correct payload (dapp source)', function () { + describe('On transaction suggested by dapp', function () { + let txMeta; + before(function () { + txMeta = { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + txParams: { + from: fromAccount.address, + to: '0x1678a085c290ebd122dc42cba69373b5953b831d', + gasPrice: '0x77359400', + gas: '0x7b0d', + nonce: '0x4b', + }, + type: TRANSACTION_TYPES.SIMPLE_SEND, + origin: 'other', + chainId: currentChainId, + time: 1624408066355, + metamaskNetworkId: currentNetworkId, + }; + }); + + it('should create an event fragment when transaction added', function () { + const expectedPayload = { + initialEvent: 'Transaction Added', + successEvent: 'Transaction Approved', + failureEvent: 'Transaction Rejected', + uniqueIdentifier: 'transaction-added-1', + category: 'Transactions', + persist: true, + properties: { + chain_id: '0x2a', + network: '42', + referrer: 'other', + source: 'dapp', + type: TRANSACTION_TYPES.SIMPLE_SEND, + }, + sensitiveProperties: { + gas_price: '2', + gas_limit: '0x7b0d', + first_seen: 1624408066355, + transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, + status: 'unapproved', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.ADDED, + ); + assert.equal(createEventFragmentSpy.callCount, 1); + assert.equal(finalizeEventFragmentSpy.callCount, 0); + assert.deepEqual( + createEventFragmentSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('Should finalize the transaction added fragment as abandoned if user rejects transaction', function () { + fragmentExists = true; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.REJECTED, + ); + assert.equal(createEventFragmentSpy.callCount, 0); + assert.equal(finalizeEventFragmentSpy.callCount, 1); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[0], + 'transaction-added-1', + ); + assert.deepEqual(finalizeEventFragmentSpy.getCall(0).args[1], { + abandoned: true, + }); + }); + + it('Should finalize the transaction added fragment if user approves transaction', function () { + fragmentExists = true; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.APPROVED, + ); + assert.equal(createEventFragmentSpy.callCount, 0); + assert.equal(finalizeEventFragmentSpy.callCount, 1); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[0], + 'transaction-added-1', + ); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[1], + undefined, + ); + }); + + it('should create an event fragment when transaction is submitted', function () { + const expectedPayload = { + initialEvent: 'Transaction Submitted', + successEvent: 'Transaction Finalized', + uniqueIdentifier: 'transaction-submitted-1', + category: 'Transactions', + persist: true, + properties: { + chain_id: '0x2a', + network: '42', + referrer: 'other', + source: 'dapp', + type: TRANSACTION_TYPES.SIMPLE_SEND, + }, + sensitiveProperties: { + gas_price: '2', + gas_limit: '0x7b0d', + first_seen: 1624408066355, + transaction_envelope_type: TRANSACTION_ENVELOPE_TYPE_NAMES.LEGACY, + status: 'unapproved', + }, + }; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.SUBMITTED, + ); + assert.equal(createEventFragmentSpy.callCount, 1); + assert.equal(finalizeEventFragmentSpy.callCount, 0); + assert.deepEqual( + createEventFragmentSpy.getCall(0).args[0], + expectedPayload, + ); + }); + + it('Should finalize the transaction submitted fragment when transaction finalizes', function () { + fragmentExists = true; + + txController._trackTransactionMetricsEvent( + txMeta, + TRANSACTION_EVENTS.FINALIZED, + ); + assert.equal(createEventFragmentSpy.callCount, 0); + assert.equal(finalizeEventFragmentSpy.callCount, 1); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[0], + 'transaction-submitted-1', + ); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[1], + undefined, + ); + }); + }); + + it('should create missing fragments when events happen out of order or are missing', function () { const txMeta = { id: 1, status: TRANSACTION_STATUSES.UNAPPROVED, @@ -1612,9 +1881,13 @@ describe('Transaction Controller', function () { time: 1624408066355, metamaskNetworkId: currentNetworkId, }; + const expectedPayload = { - event: 'Transaction Added', + successEvent: 'Transaction Approved', + failureEvent: 'Transaction Rejected', + uniqueIdentifier: 'transaction-added-1', category: 'Transactions', + persist: true, properties: { chain_id: '0x2a', network: '42', @@ -1630,16 +1903,21 @@ describe('Transaction Controller', function () { status: 'unapproved', }, }; - txController._trackTransactionMetricsEvent( txMeta, - TRANSACTION_EVENTS.ADDED, + TRANSACTION_EVENTS.APPROVED, ); - assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.equal(createEventFragmentSpy.callCount, 1); assert.deepEqual( - trackMetaMetricsEventSpy.getCall(0).args[0], + createEventFragmentSpy.getCall(0).args[0], expectedPayload, ); + assert.equal(finalizeEventFragmentSpy.callCount, 1); + assert.deepEqual( + finalizeEventFragmentSpy.getCall(0).args[0], + 'transaction-added-1', + ); + assert.deepEqual(finalizeEventFragmentSpy.getCall(0).args[1], undefined); }); it('should call _trackMetaMetricsEvent with the correct payload (extra params)', function () { @@ -1660,7 +1938,11 @@ describe('Transaction Controller', function () { metamaskNetworkId: currentNetworkId, }; const expectedPayload = { - event: 'Transaction Added', + initialEvent: 'Transaction Added', + successEvent: 'Transaction Approved', + failureEvent: 'Transaction Rejected', + uniqueIdentifier: 'transaction-added-1', + persist: true, category: 'Transactions', properties: { network: '42', @@ -1688,9 +1970,10 @@ describe('Transaction Controller', function () { foo: 'bar', }, ); - assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.equal(createEventFragmentSpy.callCount, 1); + assert.equal(finalizeEventFragmentSpy.callCount, 0); assert.deepEqual( - trackMetaMetricsEventSpy.getCall(0).args[0], + createEventFragmentSpy.getCall(0).args[0], expectedPayload, ); }); @@ -1716,7 +1999,11 @@ describe('Transaction Controller', function () { metamaskNetworkId: currentNetworkId, }; const expectedPayload = { - event: 'Transaction Added', + initialEvent: 'Transaction Added', + successEvent: 'Transaction Approved', + failureEvent: 'Transaction Rejected', + uniqueIdentifier: 'transaction-added-1', + persist: true, category: 'Transactions', properties: { chain_id: '0x2a', @@ -1747,9 +2034,10 @@ describe('Transaction Controller', function () { foo: 'bar', }, ); - assert.equal(trackMetaMetricsEventSpy.callCount, 1); + assert.equal(createEventFragmentSpy.callCount, 1); + assert.equal(finalizeEventFragmentSpy.callCount, 0); assert.deepEqual( - trackMetaMetricsEventSpy.getCall(0).args[0], + createEventFragmentSpy.getCall(0).args[0], expectedPayload, ); }); diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index aa753be82..74ba5dca0 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -117,6 +117,8 @@ export default class TransactionStateManager extends EventEmitter { time: new Date().getTime(), status: TRANSACTION_STATUSES.UNAPPROVED, metamaskNetworkId: netId, + originalGasEstimate: opts.txParams?.gas, + userEditedGasLimit: false, chainId, loadingDefaults: true, dappSuggestedGasFees, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 528c36d55..c8d2931a1 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -220,22 +220,22 @@ export default class MetamaskController extends EventEmitter { onNetworkStateChange: this.networkController.store.subscribe.bind( this.networkController.store, ), - getAssetName: this.assetsContractController.getAssetName.bind( + getERC721AssetName: this.assetsContractController.getERC721AssetName.bind( this.assetsContractController, ), - getAssetSymbol: this.assetsContractController.getAssetSymbol.bind( + getERC721AssetSymbol: this.assetsContractController.getERC721AssetSymbol.bind( this.assetsContractController, ), - getCollectibleTokenURI: this.assetsContractController.getCollectibleTokenURI.bind( + getERC721TokenURI: this.assetsContractController.getERC721TokenURI.bind( this.assetsContractController, ), - getOwnerOf: this.assetsContractController.getOwnerOf.bind( + getERC721OwnerOf: this.assetsContractController.getERC721OwnerOf.bind( this.assetsContractController, ), - balanceOfERC1155Collectible: this.assetsContractController.balanceOfERC1155Collectible.bind( + getERC1155BalanceOf: this.assetsContractController.getERC1155BalanceOf.bind( this.assetsContractController, ), - uriERC1155Collectible: this.assetsContractController.uriERC1155Collectible.bind( + getERC1155TokenURI: this.assetsContractController.getERC1155TokenURI.bind( this.assetsContractController, ), }, @@ -243,6 +243,8 @@ export default class MetamaskController extends EventEmitter { initState.CollectiblesController, ); + this.collectiblesController.setApiKey(process.env.OPENSEA_KEY); + process.env.COLLECTIBLES_V1 && (this.collectibleDetectionController = new CollectibleDetectionController( { @@ -577,6 +579,18 @@ export default class MetamaskController extends EventEmitter { ), provider: this.provider, blockTracker: this.blockTracker, + createEventFragment: this.metaMetricsController.createEventFragment.bind( + this.metaMetricsController, + ), + updateEventFragment: this.metaMetricsController.updateEventFragment.bind( + this.metaMetricsController, + ), + finalizeEventFragment: this.metaMetricsController.finalizeEventFragment.bind( + this.metaMetricsController, + ), + getEventFragmentById: this.metaMetricsController.getEventFragmentById.bind( + this.metaMetricsController, + ), trackMetaMetricsEvent: this.metaMetricsController.trackEvent.bind( this.metaMetricsController, ), @@ -636,8 +650,7 @@ export default class MetamaskController extends EventEmitter { this.collectiblesController.checkAndUpdateSingleCollectibleOwnershipStatus( knownCollectible, false, - // TODO add this when checkAndUpdateSingleCollectibleOwnershipStatus is updated - // { userAddress, chainId }, + { userAddress, chainId }, ); } } @@ -1040,6 +1053,7 @@ export default class MetamaskController extends EventEmitter { appStateController, collectiblesController, collectibleDetectionController, + assetsContractController, currencyRateController, detectTokensController, ensController, @@ -1188,6 +1202,14 @@ export default class MetamaskController extends EventEmitter { setAdvancedGasFee: preferencesController.setAdvancedGasFee.bind( preferencesController, ), + setEIP1559V2Enabled: preferencesController.setEIP1559V2Enabled.bind( + preferencesController, + ), + + // AssetsContractController + getTokenStandardAndDetails: assetsContractController.getTokenStandardAndDetails.bind( + assetsContractController, + ), // CollectiblesController addCollectible: collectiblesController.addCollectible.bind( @@ -1246,6 +1268,12 @@ export default class MetamaskController extends EventEmitter { setCollectiblesDetectionNoticeDismissed: appStateController.setCollectiblesDetectionNoticeDismissed.bind( appStateController, ), + setEnableEIP1559V2NoticeDismissed: appStateController.setEnableEIP1559V2NoticeDismissed.bind( + appStateController, + ), + updateCollectibleDropDownState: appStateController.updateCollectibleDropDownState.bind( + appStateController, + ), // EnsController tryReverseResolveAddress: ensController.reverseResolveAddress.bind( ensController, @@ -1270,6 +1298,9 @@ export default class MetamaskController extends EventEmitter { addUnapprovedTransaction: txController.addUnapprovedTransaction.bind( txController, ), + createTransactionEventFragment: txController.createTransactionEventFragment.bind( + txController, + ), // messageManager signMessage: this.signMessage.bind(this), @@ -1402,6 +1433,15 @@ export default class MetamaskController extends EventEmitter { trackMetaMetricsPage: metaMetricsController.trackPage.bind( metaMetricsController, ), + createEventFragment: metaMetricsController.createEventFragment.bind( + metaMetricsController, + ), + updateEventFragment: metaMetricsController.updateEventFragment.bind( + metaMetricsController, + ), + finalizeEventFragment: metaMetricsController.finalizeEventFragment.bind( + metaMetricsController, + ), // approval controller resolvePendingApproval: approvalController.accept.bind( diff --git a/development/build/transforms/remove-fenced-code.js b/development/build/transforms/remove-fenced-code.js index ee205e0a7..71236e179 100644 --- a/development/build/transforms/remove-fenced-code.js +++ b/development/build/transforms/remove-fenced-code.js @@ -200,9 +200,19 @@ const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u; * a boolean indicating whether they were modified. */ function removeFencedCode(filePath, typeOfCurrentBuild, fileContent) { - const matchedLines = [...fileContent.matchAll(linesWithFenceRegex)]; + // Do not modify the file if we detect an inline sourcemap. For reasons + // yet to be determined, the transform receives every file twice while in + // watch mode, the second after Babel has transpiled the file. Babel adds + // inline source maps to the file, something we will never do in our own + // source files, so we use the existence of inline source maps to determine + // whether we should ignore the file. + if (/^\/\/# sourceMappingURL=/gmu.test(fileContent)) { + return [fileContent, false]; + } // If we didn't match any lines, return the unmodified file contents. + const matchedLines = [...fileContent.matchAll(linesWithFenceRegex)]; + if (matchedLines.length === 0) { return [fileContent, false]; } diff --git a/development/build/transforms/remove-fenced-code.test.js b/development/build/transforms/remove-fenced-code.test.js index 4397b4bf6..b3ff511e3 100644 --- a/development/build/transforms/remove-fenced-code.test.js +++ b/development/build/transforms/remove-fenced-code.test.js @@ -610,6 +610,17 @@ describe('build/transforms/remove-fenced-code', () => { }); }); + it('ignores files with inline source maps', () => { + // This is so that there isn't an unnecessary second execution of + // removeFencedCode with a transpiled version of the same file + const input = getTestData().validInputs.extraContentWithFences.concat( + '\n//# sourceMappingURL=as32e32wcwc2234f2ew32cnin4243f4nv9nsdoivnxzoivnd', + ); + expect( + removeFencedCode(mockFileName, BuildType.flask, input), + ).toStrictEqual([input, false]); + }); + // We can't do this until there's more than one command it.todo('rejects directive pairs with mismatched commands'); }); diff --git a/development/run-ganache.sh b/development/run-ganache.sh index 0afdad98d..218457663 100755 --- a/development/run-ganache.sh +++ b/development/run-ganache.sh @@ -4,7 +4,7 @@ set -e set -u set -o pipefail -ganache_cli="$(yarn bin)/ganache-cli" +ganache_cli="$(yarn bin)/ganache" seed_phrase="${GANACHE_SEED_PHRASE:-phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent}" _term () { @@ -23,7 +23,7 @@ trap _term SIGTERM trap _int SIGINT # shellcheck disable=SC2086 -$ganache_cli --noVMErrorsOnRPCResponse --networkId 1337 --mnemonic "$seed_phrase" ${GANACHE_ARGS:-} & +$ganache_cli --chain.vmErrorsOnRPCResponse false --networkId 1337 --mnemonic "$seed_phrase" ${GANACHE_ARGS:-} & child=$! wait "$child" diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 82cc9b762..0b8705b65 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -955,7 +955,10 @@ }, "abstract-leveldown": { "packages": { + "buffer": true, + "immediate": true, "is-buffer": true, + "level-supports": true, "process": true, "xtend": true } @@ -3042,6 +3045,11 @@ "xtend": true } }, + "level-supports": { + "packages": { + "xtend": true + } + }, "levelup": { "packages": { "assert": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 82cc9b762..0b8705b65 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -955,7 +955,10 @@ }, "abstract-leveldown": { "packages": { + "buffer": true, + "immediate": true, "is-buffer": true, + "level-supports": true, "process": true, "xtend": true } @@ -3042,6 +3045,11 @@ "xtend": true } }, + "level-supports": { + "packages": { + "xtend": true + } + }, "levelup": { "packages": { "assert": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 82cc9b762..0b8705b65 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -955,7 +955,10 @@ }, "abstract-leveldown": { "packages": { + "buffer": true, + "immediate": true, "is-buffer": true, + "level-supports": true, "process": true, "xtend": true } @@ -3042,6 +3045,11 @@ "xtend": true } }, + "level-supports": { + "packages": { + "xtend": true + } + }, "levelup": { "packages": { "assert": true, diff --git a/package.json b/package.json index c6f2905ee..8c3d422b4 100644 --- a/package.json +++ b/package.json @@ -108,9 +108,9 @@ "@keystonehq/metamask-airgapped-keyring": "0.2.1", "@material-ui/core": "^4.11.0", "@metamask/contract-metadata": "^1.31.0", - "@metamask/controllers": "^24.0.0", + "@metamask/controllers": "^25.0.0", "@metamask/eth-ledger-bridge-keyring": "^0.10.0", - "@metamask/eth-token-tracker": "^3.0.1", + "@metamask/eth-token-tracker": "^4.0.0", "@metamask/etherscan-link": "^2.1.0", "@metamask/jazzicon": "^2.0.0", "@metamask/logo": "^3.1.1", @@ -291,8 +291,7 @@ "fancy-log": "^1.3.3", "fast-glob": "^3.2.2", "fs-extra": "^8.1.0", - "ganache-cli": "^6.12.1", - "ganache-core": "^2.13.1", + "ganache": "^v7.0.0-rc.0", "geckodriver": "^1.21.0", "globby": "^11.0.4", "gulp": "^4.0.2", @@ -386,7 +385,8 @@ "node-hid": false, "usb": false, "blake-hash": false, - "protobufjs": false + "protobufjs": false, + "@trufflesuite/bigint-buffer": false } } } diff --git a/shared/constants/hardware-wallets.js b/shared/constants/hardware-wallets.js index 105287800..195a1b488 100644 --- a/shared/constants/hardware-wallets.js +++ b/shared/constants/hardware-wallets.js @@ -17,6 +17,13 @@ export const DEVICE_NAMES = { LATTICE: 'lattice', }; +export const KEYRING_NAMES = { + LEDGER: 'Ledger', + TREZOR: 'Trezor', + QR: 'QR', + LATTICE: 'Lattice1', +}; + /** * Used for setting the users preference for ledger transport type */ diff --git a/shared/constants/metametrics.js b/shared/constants/metametrics.js index e0f503234..7f5076ffd 100644 --- a/shared/constants/metametrics.js +++ b/shared/constants/metametrics.js @@ -82,6 +82,46 @@ * segment source that marks the event data as not conforming to our schema */ +/** + * @typedef {Object} MetaMetricsEventFragment + * @property {string} successEvent - The event name to fire when the fragment + * is closed in an affirmative action. + * @property {string} [failureEvent] - The event name to fire when the fragment + * is closed with a rejection. + * @property {string} [initialEvent] - An event name to fire immediately upon + * fragment creation. This is useful for building funnels in mixpanel and for + * reduction of code duplication. + * @property {string} category - the event category to use for both the success + * and failure events + * @property {boolean} [persist] - Should this fragment be persisted in + * state and progressed after the extension is locked and unlocked. + * @property {number} [timeout] - Time in seconds the event should be persisted + * for. After the timeout the fragment will be closed as abandoned. if not + * supplied the fragment is stored indefinitely. + * @property {number} [lastUpdated] - Date.now() when the fragment was last + * updated. Used to determine if the timeout has expired and the fragment + * should be closed. + * @property {object} [properties] - Object of custom values to track, keys in + * this object must be in snake_case. + * @property {object} [sensitiveProperties] - Object of sensitive values to + * track. Keys in this object must be in snake_case. These properties will be + * sent in an additional event that excludes the user's metaMetricsId + * @property {number} [revenue] - amount of currency that event creates in + * revenue for MetaMask if fragment is successful. + * @property {string} [currency] - ISO 4127 format currency for events with + * revenue, defaults to US dollars + * @property {number} [value] - Abstract business "value" attributable to + * customers who successfully complete this fragment + * @property {MetaMetricsPageObject} [page] - the page/route that the event + * occurred on + * @property {MetaMetricsReferrerObject} [referrer] - the origin of the dapp + * that initiated the event fragment. + * @property {string} [uniqueIdentifier] - optional argument to override the + * automatic generation of UUID for the event fragment. This is useful when + * tracking events for subsystems that already generate UUIDs so to avoid + * unnecessary lookups and reduce accidental duplication. + */ + /** * Represents the shape of data sent to the segment.track method. * diff --git a/shared/constants/transaction.js b/shared/constants/transaction.js index 23d607fa6..929e2bf71 100644 --- a/shared/constants/transaction.js +++ b/shared/constants/transaction.js @@ -226,6 +226,10 @@ export const TRANSACTION_GROUP_CATEGORIES = { * TransactionMeta object. * @property {string} origin - A string representing the interface that * suggested the transaction. + * @property {string} originalGasEstimate - A string representing the original + * gas estimation on the transaction metadata. + * @property {boolean} userEditedGasLimit - A boolean representing when the + * user manually edited the gas limit. * @property {Object} nonceDetails - A metadata object containing information * used to derive the suggested nonce, useful for debugging nonce issues. * @property {string} rawTx - A hex string of the final signed transaction, @@ -236,3 +240,49 @@ export const TRANSACTION_GROUP_CATEGORIES = { * the network, in Unix epoch time (ms). * @property {TxError} [err] - The error encountered during the transaction */ + +/** + * Defines the possible types + * + * @typedef {Object} TransactionMetaMetricsEvents + * @property {'Transaction Added'} ADDED - All transactions, except incoming + * ones, are added to the controller state in an unapproved status. When this + * happens we fire the Transaction Added event to show that the transaction + * has been added to the user's MetaMask. + * @property {'Transaction Approved'} APPROVED - When an unapproved transaction + * is in the controller state, MetaMask will render a confirmation screen for + * that transaction. If the user approves the transaction we fire this event + * to indicate that the user has approved the transaction for submission to + * the network. + * @property {'Transaction Rejected'} REJECTED - When an unapproved transaction + * is in the controller state, MetaMask will render a confirmation screen for + * that transaction. If the user rejects the transaction we fire this event + * to indicate that the user has rejected the transaction. It will be removed + * from state as a result. + * @property {'Transaction Submitted'} SUBMITTED - After a transaction is + * approved by the user, it is then submitted to the network for inclusion in + * a block. When this happens we fire the Transaction Submitted event to + * indicate that MetaMask is submitting a transaction at the user's request. + * @property {'Transaction Finalized'} FINALIZED - All transactions that are + * submitted will finalized (eventually) by either being dropped, failing + * or being confirmed. When this happens we track this event, along with the + * status. + */ + +/** + * This type will work anywhere you expect a string that can be one of the + * above transaction event types. + * + * @typedef {TransactionMetaMetricsEvents[keyof TransactionMetaMetricsEvents]} TransactionMetaMetricsEventString + */ + +/** + * @type {TransactionMetaMetricsEvents} + */ +export const TRANSACTION_EVENTS = { + ADDED: 'Transaction Added', + APPROVED: 'Transaction Approved', + FINALIZED: 'Transaction Finalized', + REJECTED: 'Transaction Rejected', + SUBMITTED: 'Transaction Submitted', +}; diff --git a/test/e2e/fixtures/eip-1559-v2-dapp/state.json b/test/e2e/fixtures/eip-1559-v2-dapp/state.json new file mode 100644 index 000000000..3e29d737e --- /dev/null +++ b/test/e2e/fixtures/eip-1559-v2-dapp/state.json @@ -0,0 +1,145 @@ +{ + "data": { + "AppStateController": { + "mkrMigrationReminderTimestamp": null, + "swapsWelcomeMessageHasBeenShown": true + }, + "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 + } + } + }, + "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, + "eip1559V2Enabled": true, + "currentLocale": "en", + "featureFlags": { + "showIncomingTransactions": true, + "transactionTime": false + }, + "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 + }, + "TransactionController": { + "transactions": {} + }, + "config": {}, + "firstTimeInfo": { + "date": 1575697234195, + "version": "7.7.0" + } + }, + "meta": { + "version": 40 + } +} diff --git a/test/e2e/fixtures/eip-1559-v2/state.json b/test/e2e/fixtures/eip-1559-v2/state.json new file mode 100644 index 000000000..452c896c3 --- /dev/null +++ b/test/e2e/fixtures/eip-1559-v2/state.json @@ -0,0 +1,214 @@ +{ + "data": { + "AppStateController": { + "mkrMigrationReminderTimestamp": null, + "swapsWelcomeMessageHasBeenShown": true + }, + "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 + } + } + }, + "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, + "eip1559V2Enabled": true, + "currentLocale": "en", + "featureFlags": { + "showIncomingTransactions": true, + "transactionTime": false + }, + "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 + }, + "TransactionController": { + "transactions": { + "4046084157914634": { + "chainId": "0x539", + "primaryTransaction": { + "chainId": "0x539", + "id": 4046084157914634, + "loadingDefaults": true, + "metamaskNetworkId": "1337", + "origin": "metamask", + "status": "unapproved", + "time": 1617228030067, + "txParams": { + "from": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1", + "gas": "0x61a8", + "maxFeePerGas": "0x59682f0c", + "maxPriorityFeePerGas": "0x59682f00", + "type": "0x2", + "to": "0x2f318C334780961FB129D2a6c30D0763d9a5C970", + "value": "0x1e87F85809dc0000" + }, + "type": "sentEther" + }, + "history": [ + { + "chainId": "0x539", + "id": 4046084157914634, + "loadingDefaults": true, + "metamaskNetworkId": "1337", + "origin": "metamask", + "status": "unapproved", + "time": 1617228030067, + "txParams": { + "from": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1", + "gas": "0x61a8", + "maxFeePerGas": "0x59682f0c", + "maxPriorityFeePerGas": "0x59682f00", + "type": "0x2", + "to": "0x2f318C334780961FB129D2a6c30D0763d9a5C970", + "value": "0x1e87F85809dc0000" + }, + "type": "simpleSend" + }, + [ + { + "note": "Added new unapproved transaction.", + "op": "replace", + "path": "/loadingDefaults", + "timestamp": 1617228030069, + "value": false + } + ] + ], + "id": 4046084157914634, + "loadingDefaults": false, + "metamaskNetworkId": "1337", + "origin": "metamask", + "status": "unapproved", + "time": 1617228030067, + "txParams": { + "from": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1", + "gas": "0x61a8", + "maxFeePerGas": "0x59682f0c", + "maxPriorityFeePerGas": "0x59682f00", + "type": "0x2", + "to": "0x2f318C334780961FB129D2a6c30D0763d9a5C970", + "value": "0x1e87F85809dc0000" + }, + "type": "simpleSend" + } + } + }, + "config": {}, + "firstTimeInfo": { + "date": 1575697234195, + "version": "7.7.0" + } + }, + "meta": { + "version": 40 + } +} diff --git a/test/e2e/fixtures/navigate-transactions/state.json b/test/e2e/fixtures/navigate-transactions/state.json index ff0d8a05d..16aae0325 100644 --- a/test/e2e/fixtures/navigate-transactions/state.json +++ b/test/e2e/fixtures/navigate-transactions/state.json @@ -485,6 +485,46 @@ "usePhishDetect": true, "useTokenDetection": true }, + "MetaMetricsController": { + "fragments": { + "transaction-added-7911313280012623": { + "category": "Transactions", + "initialEvent": "Transaction Added", + "successEvent": "Transaction Approved", + "failureEvent": "Transaction Rejected", + "properties": {}, + "persist": true, + "uniqueIdentifier": "transaction-added-7911313280012623" + }, + "transaction-added-7911313280012624": { + "category": "Transactions", + "initialEvent": "Transaction Added", + "successEvent": "Transaction Approved", + "failureEvent": "Transaction Rejected", + "properties": {}, + "persist": true, + "uniqueIdentifier": "transaction-added-7911313280012624" + }, + "transaction-added-7911313280012625": { + "category": "Transactions", + "initialEvent": "Transaction Added", + "successEvent": "Transaction Approved", + "failureEvent": "Transaction Rejected", + "properties": {}, + "persist": true, + "uniqueIdentifier": "transaction-added-7911313280012625" + }, + "transaction-added-7911313280012626": { + "category": "Transactions", + "initialEvent": "Transaction Added", + "successEvent": "Transaction Approved", + "failureEvent": "Transaction Rejected", + "properties": {}, + "persist": true, + "uniqueIdentifier": "transaction-added-7911313280012626" + } + } + }, "TransactionController": { "transactions": { "7911313280012623": { diff --git a/test/e2e/fixtures/send-edit-v2/state.json b/test/e2e/fixtures/send-edit-v2/state.json new file mode 100644 index 000000000..fe1a4390c --- /dev/null +++ b/test/e2e/fixtures/send-edit-v2/state.json @@ -0,0 +1,214 @@ +{ + "data": { + "AppStateController": { + "mkrMigrationReminderTimestamp": null, + "swapsWelcomeMessageHasBeenShown": true + }, + "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 + } + } + }, + "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, + "eip1559V2Enabled": true, + "currentLocale": "en", + "featureFlags": { + "showIncomingTransactions": true, + "transactionTime": false + }, + "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 + }, + "TransactionController": { + "transactions": { + "4046084157914634": { + "chainId": "0x539", + "primaryTransaction": { + "chainId": "0x539", + "id": 4046084157914634, + "loadingDefaults": true, + "metamaskNetworkId": "1337", + "origin": "metamask", + "status": "unapproved", + "time": 1617228030067, + "txParams": { + "from": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1", + "gas": "0x61a8", + "maxFeePerGas": "0x59682f0c", + "maxPriorityFeePerGas": "0x59682f00", + "type": "0x2", + "to": "0x2f318C334780961FB129D2a6c30D0763d9a5C970", + "value": "0xde0b6b3a7640000" + }, + "type": "sentEther" + }, + "history": [ + { + "chainId": "0x539", + "id": 4046084157914634, + "loadingDefaults": true, + "metamaskNetworkId": "1337", + "origin": "metamask", + "status": "unapproved", + "time": 1617228030067, + "txParams": { + "from": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1", + "gas": "0x61a8", + "maxFeePerGas": "0x59682f0c", + "maxPriorityFeePerGas": "0x59682f00", + "type": "0x2", + "to": "0x2f318C334780961FB129D2a6c30D0763d9a5C970", + "value": "0xde0b6b3a7640000" + }, + "type": "simpleSend" + }, + [ + { + "note": "Added new unapproved transaction.", + "op": "replace", + "path": "/loadingDefaults", + "timestamp": 1617228030069, + "value": false + } + ] + ], + "id": 4046084157914634, + "loadingDefaults": false, + "metamaskNetworkId": "1337", + "origin": "metamask", + "status": "unapproved", + "time": 1617228030067, + "txParams": { + "from": "0x5cfe73b6021e818b776b421b1c4db2474086a7e1", + "gas": "0x61a8", + "maxFeePerGas": "0x59682f0c", + "maxPriorityFeePerGas": "0x59682f00", + "type": "0x2", + "to": "0x2f318C334780961FB129D2a6c30D0763d9a5C970", + "value": "0xde0b6b3a7640000" + }, + "type": "simpleSend" + } + } + }, + "config": {}, + "firstTimeInfo": { + "date": 1575697234195, + "version": "7.7.0" + } + }, + "meta": { + "version": 40 + } +} diff --git a/test/e2e/fixtures/send-edit/state.json b/test/e2e/fixtures/send-edit/state.json index 8392faac9..f538e00bf 100644 --- a/test/e2e/fixtures/send-edit/state.json +++ b/test/e2e/fixtures/send-edit/state.json @@ -129,6 +129,19 @@ "useNonceField": false, "usePhishDetect": true }, + "MetaMetricsController": { + "fragments": { + "transaction-added-4046084157914634": { + "category": "Transactions", + "initialEvent": "Transaction Added", + "successEvent": "Transaction Approved", + "failureEvent": "Transaction Rejected", + "properties": {}, + "persist": true, + "uniqueIdentifier": "transaction-added-4046084157914634" + } + } + }, "TransactionController": { "transactions": { "4046084157914634": { diff --git a/test/e2e/ganache.js b/test/e2e/ganache.js index 8f9974da1..5d45f6208 100644 --- a/test/e2e/ganache.js +++ b/test/e2e/ganache.js @@ -1,5 +1,4 @@ -const { promisify } = require('util'); -const ganache = require('ganache-core'); +const ganache = require('ganache'); const defaultOptions = { blockTime: 2, @@ -8,6 +7,7 @@ const defaultOptions = { 'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent', port: 8545, vmErrorsOnRPCResponse: false, + hardfork: 'muirGlacier', }; class Ganache { @@ -15,22 +15,14 @@ class Ganache { const options = { ...defaultOptions, ...opts }; const { port } = options; this._server = ganache.server(options); - - const listen = promisify(this._server.listen).bind(this._server); - const blockchain = await listen(port); - - return { - ...blockchain, - port, - }; + await this._server.listen(port); } async quit() { if (!this._server) { throw new Error('Server not running yet'); } - const close = promisify(this._server.close).bind(this._server); - await close(); + await this._server.close(); } } diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index 5eb5ad8fb..6b662f1a6 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -1,5 +1,6 @@ const path = require('path'); const sinon = require('sinon'); +const BigNumber = require('bignumber.js'); const createStaticServer = require('../../development/create-static-server'); const { createSegmentServer, @@ -12,9 +13,10 @@ const { ensureXServerIsRunning } = require('./x-server'); const tinyDelayMs = 200; const regularDelayMs = tinyDelayMs * 2; const largeDelayMs = regularDelayMs * 2; - const dappPort = 8080; +const convertToHexValue = (val) => `0x${new BigNumber(val, 10).toString(16)}`; + async function withFixtures(options, testSuite) { const { dapp, @@ -42,7 +44,7 @@ async function withFixtures(options, testSuite) { secondaryGanacheServer = new Ganache(); await secondaryGanacheServer.start({ blockTime: 2, - _chainIdRpc: chainId, + chain: { chainId }, port, vmErrorsOnRPCResponse: false, }); @@ -147,9 +149,55 @@ async function withFixtures(options, testSuite) { } } +/** + * @param {*} driver - selinium driver + * @param {*} handlesCount - total count of windows that should be loaded + * @returns handles - an object with window handles, properties in object represent windows: + * 1. extension: metamask extension window + * 2. dapp: test-app window + * 3. popup: metsmask extension popup window + */ +const getWindowHandles = async (driver, handlesCount) => { + await driver.waitUntilXWindowHandles(handlesCount); + const windowHandles = await driver.getAllWindowHandles(); + + const extension = windowHandles[0]; + const dapp = await driver.switchToWindowWithTitle( + 'E2E Test Dapp', + windowHandles, + ); + const popup = windowHandles.find( + (handle) => handle !== extension && handle !== dapp, + ); + return { extension, dapp, popup }; +}; + +const connectDappWithExtensionPopup = async (driver) => { + await driver.openNewPage(`http://127.0.0.1:${dappPort}/`); + await driver.delay(regularDelayMs); + await driver.clickElement({ text: 'Connect', tag: 'button' }); + await driver.delay(regularDelayMs); + + const windowHandles = await getWindowHandles(driver, 3); + + // open extension popup and confirm connect + await driver.switchToWindow(windowHandles.popup); + await driver.delay(largeDelayMs); + await driver.clickElement({ text: 'Next', tag: 'button' }); + await driver.clickElement({ text: 'Connect', tag: 'button' }); + + // send from dapp + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindow(windowHandles.dapp); + await driver.delay(regularDelayMs); +}; + module.exports = { + getWindowHandles, + convertToHexValue, tinyDelayMs, regularDelayMs, largeDelayMs, withFixtures, + connectDappWithExtensionPopup, }; diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js index e6ec801d5..5ffd36c7f 100644 --- a/test/e2e/metamask-ui.spec.js +++ b/test/e2e/metamask-ui.spec.js @@ -217,7 +217,7 @@ describe('MetaMask', function () { it('balance renders', async function () { await driver.waitForSelector({ css: '[data-testid="wallet-balance"] .list-item__heading', - text: '100 ETH', + text: '1000', }); await driver.delay(regularDelayMs); }); diff --git a/test/e2e/metrics.spec.js b/test/e2e/metrics.spec.js index 498a6ccd0..141b56192 100644 --- a/test/e2e/metrics.spec.js +++ b/test/e2e/metrics.spec.js @@ -1,6 +1,6 @@ const { strict: assert } = require('assert'); const waitUntilCalled = require('../lib/wait-until-called'); -const { withFixtures, tinyDelayMs } = require('./helpers'); +const { convertToHexValue, withFixtures, tinyDelayMs } = require('./helpers'); /** * WARNING: These tests must be run using a build created with `yarn build:test:metrics`, so that it has @@ -15,7 +15,7 @@ describe('Segment metrics', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/send-eth-with-private-key-test/send-eth-with-private-key.js b/test/e2e/send-eth-with-private-key-test/send-eth-with-private-key.js index 5f7291950..4ea215793 100644 --- a/test/e2e/send-eth-with-private-key-test/send-eth-with-private-key.js +++ b/test/e2e/send-eth-with-private-key-test/send-eth-with-private-key.js @@ -12,9 +12,6 @@ sendButton.addEventListener('click', function () { gasPrice: '0x09184e72a000', gasLimit: '0x22710', value: '0xde0b6b3a7640000', - r: '0x25a1bc499cd8799a2ece0fcba0df6e666e54a6e2b4e18c09838e2b621c10db71', - s: '0x6cf83e6e8f6e82a0a1d7bd10bc343fc0ae4b096c1701aa54e6389d447f98ac6f', - v: '0x2d46', to: document.getElementById('address').value, } var tx = new Tx(rawTx); diff --git a/test/e2e/tests/account-details.spec.js b/test/e2e/tests/account-details.spec.js index 82f28ad15..cfdbdf238 100644 --- a/test/e2e/tests/account-details.spec.js +++ b/test/e2e/tests/account-details.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Show account details', function () { const ganacheOptions = { @@ -7,7 +7,7 @@ describe('Show account details', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/add-account.spec.js b/test/e2e/tests/add-account.spec.js index 910402e43..e358660d0 100644 --- a/test/e2e/tests/add-account.spec.js +++ b/test/e2e/tests/add-account.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Add account', function () { const ganacheOptions = { @@ -7,7 +7,7 @@ describe('Add account', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/add-hide-token.spec.js b/test/e2e/tests/add-hide-token.spec.js index 66c6067bb..56fb2840c 100644 --- a/test/e2e/tests/add-hide-token.spec.js +++ b/test/e2e/tests/add-hide-token.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Hide token', function () { const ganacheOptions = { @@ -7,7 +7,7 @@ describe('Hide token', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; @@ -61,7 +61,7 @@ describe('Add existing token using search', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/address-book.spec.js b/test/e2e/tests/address-book.spec.js index 707c72e00..e4d43d347 100644 --- a/test/e2e/tests/address-book.spec.js +++ b/test/e2e/tests/address-book.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Address Book', function () { const ganacheOptions = { @@ -7,7 +7,7 @@ describe('Address Book', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/contract-interactions.spec.js b/test/e2e/tests/contract-interactions.spec.js index 6ef4cc4fb..1e4ddecfa 100644 --- a/test/e2e/tests/contract-interactions.spec.js +++ b/test/e2e/tests/contract-interactions.spec.js @@ -1,5 +1,9 @@ const { strict: assert } = require('assert'); -const { withFixtures, regularDelayMs } = require('../helpers'); +const { + convertToHexValue, + withFixtures, + regularDelayMs, +} = require('../helpers'); describe('Deploy contract and call contract methods', function () { let windowHandles; @@ -11,7 +15,7 @@ describe('Deploy contract and call contract methods', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/custom-rpc-history.spec.js b/test/e2e/tests/custom-rpc-history.spec.js index 6868808ac..d392afbcd 100644 --- a/test/e2e/tests/custom-rpc-history.spec.js +++ b/test/e2e/tests/custom-rpc-history.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Stores custom RPC history', function () { const ganacheOptions = { @@ -7,7 +7,7 @@ describe('Stores custom RPC history', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/edit-gas-fee.spec.js b/test/e2e/tests/edit-gas-fee.spec.js new file mode 100644 index 000000000..9c43c9e79 --- /dev/null +++ b/test/e2e/tests/edit-gas-fee.spec.js @@ -0,0 +1,277 @@ +const { strict: assert } = require('assert'); +const { + convertToHexValue, + connectDappWithExtensionPopup, + getWindowHandles, + largeDelayMs, + withFixtures, + regularDelayMs, +} = require('../helpers'); + +if (process.env.EIP_1559_V2 === '1') { + describe('Editing Confirm Transaction', function () { + it('allows selecting high, medium, low gas estimates on edit gas fee popover', async function () { + const ganacheOptions = { + hardfork: 'london', + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + await withFixtures( + { + fixtures: 'eip-1559-v2', + 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); + + const transactionAmounts = await driver.findElements( + '.currency-display-component__text', + ); + const transactionAmount = transactionAmounts[0]; + assert.equal(await transactionAmount.getText(), '2.2'); + + // update estimates to high + await driver.clickElement('[data-testid="edit-gas-fee-button"]'); + await driver.delay(regularDelayMs); + await driver.clickElement('[data-testid="edit-gas-fee-item-high"]'); + await driver.delay(regularDelayMs); + await driver.waitForSelector({ text: '🦍' }); + await driver.waitForSelector({ + text: 'Aggressive', + }); + + // update estimates to medium + await driver.clickElement('[data-testid="edit-gas-fee-button"]'); + await driver.delay(regularDelayMs); + await driver.clickElement('[data-testid="edit-gas-fee-item-medium"]'); + await driver.delay(regularDelayMs); + await driver.waitForSelector({ text: '🦊' }); + await driver.waitForSelector({ + text: 'Market', + }); + + // update estimates to low + await driver.clickElement('[data-testid="edit-gas-fee-button"]'); + await driver.delay(regularDelayMs); + await driver.clickElement('[data-testid="edit-gas-fee-item-low"]'); + await driver.delay(regularDelayMs); + await driver.waitForSelector({ text: '🐢' }); + await driver.waitForSelector({ + text: 'Low', + }); + await driver.waitForSelector('[data-testid="low-gas-fee-alert"]'); + + // confirms the transaction + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + await driver.delay(regularDelayMs); + + await driver.clickElement('[data-testid="home__activity-tab"]'); + await driver.wait(async () => { + const confirmedTxes = await driver.findElements( + '.transaction-list__completed-transactions .transaction-list-item', + ); + return confirmedTxes.length === 1; + }, 10000); + + const txValues = await driver.findElements( + '.transaction-list-item__primary-currency', + ); + assert.equal(txValues.length, 1); + assert.ok(/-2.2\s*ETH/u.test(await txValues[0].getText())); + }, + ); + }); + + it('allows accessing advance gas fee popover from edit gas fee popover', async function () { + const ganacheOptions = { + hardfork: 'london', + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + await withFixtures( + { + fixtures: 'eip-1559-v2', + 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); + + const transactionAmounts = await driver.findElements( + '.currency-display-component__text', + ); + const transactionAmount = transactionAmounts[0]; + assert.equal(await transactionAmount.getText(), '2.2'); + + // update estimates to high + await driver.clickElement('[data-testid="edit-gas-fee-button"]'); + await driver.delay(regularDelayMs); + await driver.clickElement('[data-testid="edit-gas-fee-item-custom"]'); + await driver.delay(regularDelayMs); + + // enter max fee + const maxBaseFee = await driver.findElement( + '[data-testid="base-fee-input"]', + ); + await maxBaseFee.clear(); + await maxBaseFee.sendKeys('8'); + await driver.delay(regularDelayMs); + + // enter priority fee + const priorityFee = await driver.findElement( + '[data-testid="priority-fee-input"]', + ); + await priorityFee.clear(); + await priorityFee.sendKeys('8'); + await driver.delay(regularDelayMs); + + // save default values + await driver.clickElement('input[type="checkbox"]'); + await driver.delay(regularDelayMs); + + // edit gas limit + await driver.clickElement('[data-testid="advanced-gas-fee-edit"]'); + await driver.delay(regularDelayMs); + const gasLimit = await driver.findElement( + '[data-testid="gas-limit-input"]', + ); + await gasLimit.clear(); + await gasLimit.sendKeys('100000'); + await driver.delay(regularDelayMs); + + // Submit gas fee changes + await driver.clickElement({ text: 'Save', tag: 'button' }); + + // has correct updated value on the confirm screen the transaction + const editedTransactionAmounts = await driver.findElements( + '.transaction-detail-item__row .transaction-detail-item__detail-values .currency-display-component__text:last-of-type', + ); + const editedTransactionAmount = editedTransactionAmounts[0]; + assert.equal(await editedTransactionAmount.getText(), '0.0008'); + + const editedTransactionFee = editedTransactionAmounts[1]; + assert.equal(await editedTransactionFee.getText(), '2.2008'); + + // confirms the transaction + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + await driver.delay(regularDelayMs); + + await driver.clickElement('[data-testid="home__activity-tab"]'); + await driver.wait(async () => { + const confirmedTxes = await driver.findElements( + '.transaction-list__completed-transactions .transaction-list-item', + ); + return confirmedTxes.length === 1; + }, 10000); + + const txValues = await driver.findElements( + '.transaction-list-item__primary-currency', + ); + assert.equal(txValues.length, 1); + assert.ok(/-2.2\s*ETH/u.test(await txValues[0].getText())); + }, + ); + }); + + it('should use dapp suggested estimates for transaction coming from dapp', async function () { + const ganacheOptions = { + hardfork: 'london', + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + await withFixtures( + { + fixtures: 'eip-1559-v2-dapp', + ganacheOptions, + title: this.test.title, + dapp: true, + }, + async ({ driver }) => { + await driver.navigate(); + + // login to extension + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + + // open dapp and connect + await connectDappWithExtensionPopup(driver); + await driver.clickElement({ text: 'Send', tag: 'button' }); + + // check transaction in extension popup + const windowHandles = await getWindowHandles(driver, 3); + await driver.switchToWindow(windowHandles.popup); + await driver.delay(largeDelayMs); + await driver.waitForSelector({ text: '🌐' }); + await driver.waitForSelector({ + text: 'Site suggested', + }); + + await driver.clickElement('[data-testid="edit-gas-fee-button"]'); + await driver.delay(regularDelayMs); + await driver.clickElement( + '[data-testid="edit-gas-fee-item-dappSuggested"]', + ); + await driver.delay(regularDelayMs); + + const transactionAmounts = await driver.findElements( + '.currency-display-component__text', + ); + const transactionAmount = transactionAmounts[0]; + assert.equal(await transactionAmount.getText(), '3'); + + // has correct updated value on the confirm screen the transaction + const editedTransactionAmounts = await driver.findElements( + '.transaction-detail-item__row .transaction-detail-item__detail-values .currency-display-component__text:last-of-type', + ); + const editedTransactionAmount = editedTransactionAmounts[0]; + assert.equal(await editedTransactionAmount.getText(), '0.00042'); + + const editedTransactionFee = editedTransactionAmounts[1]; + assert.equal(await editedTransactionFee.getText(), '3.00042'); + + // confirms the transaction + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + await driver.delay(regularDelayMs); + + // transaction should correct values in activity tab + await driver.switchToWindow(windowHandles.extension); + await driver.clickElement('[data-testid="home__activity-tab"]'); + await driver.wait(async () => { + const confirmedTxes = await driver.findElements( + '.transaction-list__completed-transactions .transaction-list-item', + ); + return confirmedTxes.length === 1; + }, 10000); + + const txValues = await driver.findElements( + '.transaction-list-item__primary-currency', + ); + assert.equal(txValues.length, 1); + assert.ok(/-3\s*ETH/u.test(await txValues[0].getText())); + }, + ); + }); + }); +} diff --git a/test/e2e/tests/from-import-ui.spec.js b/test/e2e/tests/from-import-ui.spec.js index cf3becf05..fc34a2f78 100644 --- a/test/e2e/tests/from-import-ui.spec.js +++ b/test/e2e/tests/from-import-ui.spec.js @@ -1,5 +1,10 @@ const { strict: assert } = require('assert'); -const { withFixtures, regularDelayMs, largeDelayMs } = require('../helpers'); +const { + convertToHexValue, + withFixtures, + regularDelayMs, + largeDelayMs, +} = require('../helpers'); const enLocaleMessages = require('../../../app/_locales/en/messages.json'); describe('Metamask Import UI', function () { @@ -9,7 +14,7 @@ describe('Metamask Import UI', function () { { secretKey: '0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; @@ -72,7 +77,7 @@ describe('Metamask Import UI', function () { // Import Secret Recovery Phrase await driver.fill( - 'input[placeholder="Paste Secret Recovery Phrase from clipboard"]', + 'input[placeholder="Enter your Secret Recovery Phrase"]', testSeedPhrase, ); @@ -186,7 +191,7 @@ describe('Metamask Import UI', function () { { secretKey: '0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; @@ -279,10 +284,10 @@ describe('Metamask Import UI', function () { await driver.delay(regularDelayMs); await driver.clickElement('.account-menu__icon'); - const accountListItemsAgfterRemoval = await driver.findElements( + const accountListItemsAfterRemoval = await driver.findElements( '.account-menu__account', ); - assert.equal(accountListItemsAgfterRemoval.length, 4); + assert.equal(accountListItemsAfterRemoval.length, 4); }, ); }); @@ -292,7 +297,7 @@ describe('Metamask Import UI', function () { { secretKey: '0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/incremental-security.spec.js b/test/e2e/tests/incremental-security.spec.js index 90df69560..a94f0f3e2 100644 --- a/test/e2e/tests/incremental-security.spec.js +++ b/test/e2e/tests/incremental-security.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures, tinyDelayMs } = require('../helpers'); +const { convertToHexValue, withFixtures, tinyDelayMs } = require('../helpers'); const enLocaleMessages = require('../../../app/_locales/en/messages.json'); describe('Incremental Security', function () { @@ -8,12 +8,12 @@ describe('Incremental Security', function () { { secretKey: '0x250F458997A364988956409A164BA4E16F0F99F916ACDD73ADCD3A1DE30CF8D1', - balance: 0, + balance: convertToHexValue(0), }, { secretKey: '0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/localization.spec.js b/test/e2e/tests/localization.spec.js index e9d6e0f7e..a4859de68 100644 --- a/test/e2e/tests/localization.spec.js +++ b/test/e2e/tests/localization.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Localization', function () { it('can correctly display Philippine peso symbol and code', async function () { @@ -8,7 +8,7 @@ describe('Localization', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/lock-account.spec.js b/test/e2e/tests/lock-account.spec.js index c8c949a42..345a6ec78 100644 --- a/test/e2e/tests/lock-account.spec.js +++ b/test/e2e/tests/lock-account.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Lock and unlock', function () { const ganacheOptions = { @@ -7,7 +7,7 @@ describe('Lock and unlock', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/lockdown.spec.js b/test/e2e/tests/lockdown.spec.js index a1d0db969..cb38725aa 100644 --- a/test/e2e/tests/lockdown.spec.js +++ b/test/e2e/tests/lockdown.spec.js @@ -4,7 +4,7 @@ const { getGlobalProperties, testIntrinsic, } = require('../../helpers/protect-intrinsics-helpers'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); const { PAGES } = require('../webdriver/driver'); const isFirefox = process.env.SELENIUM_BROWSER === Browser.FIREFOX; @@ -56,7 +56,7 @@ describe('lockdown', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/metamask-responsive-ui.spec.js b/test/e2e/tests/metamask-responsive-ui.spec.js index 1be3d0c9c..cff4f6fb1 100644 --- a/test/e2e/tests/metamask-responsive-ui.spec.js +++ b/test/e2e/tests/metamask-responsive-ui.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures, tinyDelayMs } = require('../helpers'); +const { convertToHexValue, withFixtures, tinyDelayMs } = require('../helpers'); const enLocaleMessages = require('../../../app/_locales/en/messages.json'); describe('Metamask Responsive UI', function () { @@ -183,7 +183,7 @@ describe('Metamask Responsive UI', function () { // balance renders await driver.waitForSelector({ css: '[data-testid="eth-overview__primary-currency"]', - text: '100 ETH', + text: '1000 ETH', }); }, ); @@ -196,7 +196,7 @@ describe('Metamask Responsive UI', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/navigate-transactions.spec.js b/test/e2e/tests/navigate-transactions.spec.js index a5a6a4166..64de4c7e8 100644 --- a/test/e2e/tests/navigate-transactions.spec.js +++ b/test/e2e/tests/navigate-transactions.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Navigate transactions', function () { const ganacheOptions = { @@ -7,7 +7,7 @@ describe('Navigate transactions', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/permissions.spec.js b/test/e2e/tests/permissions.spec.js index 95636934d..16f786718 100644 --- a/test/e2e/tests/permissions.spec.js +++ b/test/e2e/tests/permissions.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Permissions', function () { it('sets permissions and connect to Dapp', async function () { @@ -8,7 +8,7 @@ describe('Permissions', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/personal-sign.spec.js b/test/e2e/tests/personal-sign.spec.js index a1812892d..c479beea4 100644 --- a/test/e2e/tests/personal-sign.spec.js +++ b/test/e2e/tests/personal-sign.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Personal sign', function () { it('can initiate and confirm a personal sign', async function () { @@ -8,7 +8,7 @@ describe('Personal sign', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/provider-api.spec.js b/test/e2e/tests/provider-api.spec.js index c2c5928f7..14a4ff0eb 100644 --- a/test/e2e/tests/provider-api.spec.js +++ b/test/e2e/tests/provider-api.spec.js @@ -1,6 +1,6 @@ const { strict: assert } = require('assert'); const { errorCodes } = require('eth-rpc-errors'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('MetaMask', function () { const ganacheOptions = { @@ -8,7 +8,7 @@ describe('MetaMask', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/send-edit.spec.js b/test/e2e/tests/send-edit.spec.js index 94000b4a8..f63796d53 100644 --- a/test/e2e/tests/send-edit.spec.js +++ b/test/e2e/tests/send-edit.spec.js @@ -1,5 +1,6 @@ const { strict: assert } = require('assert'); const { + convertToHexValue, withFixtures, tinyDelayMs, regularDelayMs, @@ -13,7 +14,7 @@ describe('Editing Confirm Transaction', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; @@ -90,4 +91,114 @@ describe('Editing Confirm Transaction', function () { }, ); }); + + if (process.env.EIP_1559_V2 === '1') { + it('goes back from confirm page to edit eth value, baseFee, priorityFee and gas limit - 1559 V2', async function () { + const ganacheOptions = { + hardfork: 'london', + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + await withFixtures( + { + fixtures: 'send-edit-v2', + 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); + + const transactionAmounts = await driver.findElements( + '.currency-display-component__text', + ); + const transactionAmount = transactionAmounts[0]; + assert.equal(await transactionAmount.getText(), '1'); + + const transactionFee = transactionAmounts[1]; + assert.equal(await transactionFee.getText(), '0.0000375'); + + await driver.clickElement( + '.confirm-page-container-header__back-button', + ); + await driver.fill('.unit-input__input', '2.2'); + + await driver.clickElement({ text: 'Next', tag: 'button' }); + + // open gas fee popover + await driver.clickElement({ text: 'Edit', tag: 'button' }); + + // show gas limit + await driver.clickElement('[data-testid="advanced-gas-fee-edit"]'); + await driver.delay(largeDelayMs); + + // enter max fee + const maxBaseFee = await driver.findElement( + '[data-testid="base-fee-input"]', + ); + await maxBaseFee.clear(); + await maxBaseFee.sendKeys('8'); + await driver.delay(regularDelayMs); + + // enter priority fee + const priorityFee = await driver.findElement( + '[data-testid="priority-fee-input"]', + ); + await priorityFee.clear(); + await priorityFee.sendKeys('8'); + await driver.delay(regularDelayMs); + + // edit gas limit + const gasLimit = await driver.findElement( + '[data-testid="gas-limit-input"]', + ); + await gasLimit.clear(); + await gasLimit.sendKeys('100000'); + await driver.delay(regularDelayMs); + + // save default values + await driver.clickElement('input[type="checkbox"]'); + await driver.delay(regularDelayMs); + + // Submit gas fee changes + await driver.clickElement({ text: 'Save', tag: 'button' }); + + // has correct updated value on the confirm screen the transaction + const editedTransactionAmounts = await driver.findElements( + '.transaction-detail-item__row .transaction-detail-item__detail-values .currency-display-component__text:last-of-type', + ); + const editedTransactionAmount = editedTransactionAmounts[0]; + assert.equal(await editedTransactionAmount.getText(), '0.0008'); + + const editedTransactionFee = editedTransactionAmounts[1]; + assert.equal(await editedTransactionFee.getText(), '2.2008'); + + // confirms the transaction + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + await driver.delay(regularDelayMs); + + await driver.clickElement('[data-testid="home__activity-tab"]'); + await driver.wait(async () => { + const confirmedTxes = await driver.findElements( + '.transaction-list__completed-transactions .transaction-list-item', + ); + return confirmedTxes.length === 1; + }, 10000); + + const txValues = await driver.findElements( + '.transaction-list-item__primary-currency', + ); + assert.equal(txValues.length, 1); + assert.ok(/-2.2\s*ETH/u.test(await txValues[0].getText())); + }, + ); + }); + } }); diff --git a/test/e2e/tests/send-eth.spec.js b/test/e2e/tests/send-eth.spec.js index 7d88f121a..abe8082a5 100644 --- a/test/e2e/tests/send-eth.spec.js +++ b/test/e2e/tests/send-eth.spec.js @@ -1,5 +1,9 @@ const { strict: assert } = require('assert'); -const { withFixtures, regularDelayMs } = require('../helpers'); +const { + convertToHexValue, + withFixtures, + regularDelayMs, +} = require('../helpers'); describe('Send ETH from inside MetaMask using default gas', function () { const ganacheOptions = { @@ -7,7 +11,7 @@ describe('Send ETH from inside MetaMask using default gas', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; @@ -93,7 +97,7 @@ describe('Send ETH from inside MetaMask using advanced gas modal', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; @@ -162,7 +166,7 @@ describe('Send ETH from dapp using advanced gas controls', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/signature-request.spec.js b/test/e2e/tests/signature-request.spec.js index bf2a547e2..a2726b860 100644 --- a/test/e2e/tests/signature-request.spec.js +++ b/test/e2e/tests/signature-request.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Signature Request', function () { it('can initiate and confirm a Signature Request', async function () { @@ -8,7 +8,7 @@ describe('Signature Request', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/simple-send.spec.js b/test/e2e/tests/simple-send.spec.js index 21502e836..2d26a1871 100644 --- a/test/e2e/tests/simple-send.spec.js +++ b/test/e2e/tests/simple-send.spec.js @@ -1,4 +1,4 @@ -const { withFixtures } = require('../helpers'); +const { convertToHexValue, withFixtures } = require('../helpers'); describe('Simple send', function () { it('can send a simple transaction from one account to another', async function () { @@ -7,7 +7,7 @@ describe('Simple send', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/e2e/tests/threebox.spec.js b/test/e2e/tests/threebox.spec.js index cae0ebca8..ac58e68ab 100644 --- a/test/e2e/tests/threebox.spec.js +++ b/test/e2e/tests/threebox.spec.js @@ -1,5 +1,5 @@ const { strict: assert } = require('assert'); -const { withFixtures, largeDelayMs } = require('../helpers'); +const { convertToHexValue, withFixtures, largeDelayMs } = require('../helpers'); const ThreeboxMockServer = require('../mock-3box/threebox-mock-server'); describe('Threebox', function () { @@ -8,7 +8,7 @@ describe('Threebox', function () { { secretKey: '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', - balance: 25000000000000000000, + balance: convertToHexValue(25000000000000000000), }, ], }; diff --git a/test/stub/provider.js b/test/stub/provider.js index da618f4c7..055c02a78 100644 --- a/test/stub/provider.js +++ b/test/stub/provider.js @@ -1,6 +1,6 @@ import { JsonRpcEngine, createScaffoldMiddleware } from 'json-rpc-engine'; import { providerAsMiddleware } from 'eth-json-rpc-middleware'; -import GanacheCore from 'ganache-core'; +import Ganache from 'ganache'; export function getTestSeed() { return 'people carpet cluster attract ankle motor ozone mass dove original primary mask'; @@ -48,11 +48,11 @@ export function createTestProviderTools(opts = {}) { // handle block tracker methods engine.push( providerAsMiddleware( - GanacheCore.provider({ + Ganache.provider({ mnemonic: getTestSeed(), network_id: opts.networkId, - _chainId: opts.chainId, - _chainIdRpc: opts.chainId, + chain: { chainId: opts.chainId }, + hardfork: 'muirGlacier', }), ), ); diff --git a/ui/components/app/account-menu/account-menu.component.js b/ui/components/app/account-menu/account-menu.component.js index b674a52ac..8105af35b 100644 --- a/ui/components/app/account-menu/account-menu.component.js +++ b/ui/components/app/account-menu/account-menu.component.js @@ -16,7 +16,6 @@ import { SUPPORT_REQUEST_LINK, ///: END:ONLY_INCLUDE_IN } from '../../../helpers/constants/common'; -import { KEYRING_TYPES } from '../../../../shared/constants/hardware-wallets'; import { SETTINGS_ROUTE, NEW_ACCOUNT_ROUTE, @@ -27,6 +26,7 @@ import { import TextField from '../../ui/text-field'; import SearchIcon from '../../ui/search-icon'; import Button from '../../ui/button'; +import KeyRingLabel from './keyring-label'; export function AccountMenuItem(props) { const { icon, children, text, subText, className, onClick } = props; @@ -214,7 +214,7 @@ export default class AccountMenu extends Component { type={PRIMARY} />
- {this.renderKeyringType(keyring)} + {iconAndNameForOpenSubject ? (
{label}
; - } - resetSearchQuery() { this.setSearchQuery(''); } diff --git a/ui/components/app/account-menu/account-menu.test.js b/ui/components/app/account-menu/account-menu.test.js index ea23bcacc..c8393533c 100644 --- a/ui/components/app/account-menu/account-menu.test.js +++ b/ui/components/app/account-menu/account-menu.test.js @@ -96,7 +96,7 @@ describe('Account Menu', () => { it('render imported account label', () => { const importedAccount = wrapper.find('.keyring-label.allcaps'); - expect(importedAccount.text()).toStrictEqual('imported'); + expect(importedAccount.text()).toStrictEqual('[imported]'); }); }); diff --git a/ui/components/app/account-menu/index.scss b/ui/components/app/account-menu/index.scss index 05d3fd6e5..820e25906 100644 --- a/ui/components/app/account-menu/index.scss +++ b/ui/components/app/account-menu/index.scss @@ -10,7 +10,9 @@ color: var(--white); @media screen and (max-width: $break-small) { - right: calc(((100vw - 100%) / 2) + 8px); + position: initial; + margin-top: -10px; + margin-left: calc(((100vw - 100%) / 2) + 8px); } @media screen and (min-width: $break-large) { diff --git a/ui/components/app/account-menu/keyring-label.js b/ui/components/app/account-menu/keyring-label.js new file mode 100644 index 000000000..794eed5e4 --- /dev/null +++ b/ui/components/app/account-menu/keyring-label.js @@ -0,0 +1,48 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { useI18nContext } from '../../../hooks/useI18nContext'; +import { + KEYRING_NAMES, + KEYRING_TYPES, +} from '../../../../shared/constants/hardware-wallets'; + +export default function KeyRingLabel({ keyring }) { + const t = useI18nContext(); + + let label = null; + + // Keyring value might take a while to get a value + if (!keyring) { + return null; + } + const { type } = keyring; + + switch (type) { + case KEYRING_TYPES.QR: + label = KEYRING_NAMES.QR; + break; + case 'Simple Key Pair': + label = t('imported'); + break; + case KEYRING_TYPES.TREZOR: + label = KEYRING_NAMES.TREZOR; + break; + case KEYRING_TYPES.LEDGER: + label = KEYRING_NAMES.LEDGER; + break; + case KEYRING_TYPES.LATTICE: + label = KEYRING_NAMES.LATTICE; + break; + default: + return null; + } + + return ( + <>{label ?
{label}
: null} + ); +} + +KeyRingLabel.propTypes = { + keyring: PropTypes.object, +}; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js index 05de4c655..a5b7c04c4 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import Box from '../../../ui/box'; @@ -19,7 +19,6 @@ import { useI18nContext } from '../../../../hooks/useI18nContext'; const AdvancedGasFeeDefaults = () => { const t = useI18nContext(); const dispatch = useDispatch(); - const { hasErrors, maxBaseFee, @@ -27,25 +26,34 @@ const AdvancedGasFeeDefaults = () => { } = useAdvancedGasFeePopoverContext(); const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues); - const updateDefaultSettings = (value) => { - if (value) { + const [isDefaultSettingsSelected, setDefaultSettingsSelected] = useState( + Boolean(advancedGasFeeValues) && + advancedGasFeeValues.maxBaseFee === maxBaseFee && + advancedGasFeeValues.priorityFee === maxPriorityFeePerGas, + ); + + useEffect(() => { + setDefaultSettingsSelected( + Boolean(advancedGasFeeValues) && + advancedGasFeeValues.maxBaseFee === maxBaseFee && + advancedGasFeeValues.priorityFee === maxPriorityFeePerGas, + ); + }, [advancedGasFeeValues, maxBaseFee, maxPriorityFeePerGas]); + + const handleUpdateDefaultSettings = () => { + if (isDefaultSettingsSelected) { + dispatch(setAdvancedGasFee(null)); + setDefaultSettingsSelected(false); + } else { dispatch( setAdvancedGasFee({ maxBaseFee, priorityFee: maxPriorityFeePerGas, }), ); - } else { - dispatch(setAdvancedGasFee(null)); + setDefaultSettingsSelected(true); } }; - const isDefaultSettingsSelected = - Boolean(advancedGasFeeValues) && - advancedGasFeeValues.maxBaseFee === maxBaseFee && - advancedGasFeeValues.priorityFee === maxPriorityFeePerGas; - - const handleUpdateDefaultSettings = () => - updateDefaultSettings(!isDefaultSettingsSelected); return ( { marginRight={4} className="advanced-gas-fee-defaults" > - - - {!isDefaultSettingsSelected && Boolean(advancedGasFeeValues) - ? t('advancedGasFeeDefaultOptIn', [ - {t('newValues')}, - ]) - : t('advancedGasFeeDefaultOptOut')} - + ); }; diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js index fd83dd1e6..e7e280889 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/advanced-gas-fee-defaults.test.js @@ -5,6 +5,7 @@ import { GAS_ESTIMATE_TYPES } from '../../../../../shared/constants/gas'; import { renderWithProvider } from '../../../../../test/lib/render-helpers'; import mockEstimates from '../../../../../test/data/mock-estimates.json'; import mockState from '../../../../../test/data/mock-state.json'; +import * as Actions from '../../../../store/actions'; import { AdvancedGasFeePopoverContextProvider } from '../context'; import { GasFeeContextProvider } from '../../../../contexts/gasFee'; @@ -20,6 +21,7 @@ jest.mock('../../../../store/actions', () => ({ .mockImplementation(() => Promise.resolve()), addPollingTokenToAppState: jest.fn(), removePollingTokenFromAppState: jest.fn(), + setAdvancedGasFee: jest.fn(), })); const render = (defaultGasParams) => { @@ -125,4 +127,21 @@ describe('AdvancedGasFeeDefaults', () => { screen.queryByText('Save these as my default for "Advanced"'), ).toBeInTheDocument(); }); + + it('should call action setAdvancedGasFee when checkbox or label text is clicked', () => { + render({ + advancedGasFee: { maxBaseFee: 50, priorityFee: 2 }, + }); + const mock = jest + .spyOn(Actions, 'setAdvancedGasFee') + .mockReturnValue({ type: 'test' }); + const checkboxLabel = screen.queryByText( + 'Always use these values and advanced setting as default.', + ); + fireEvent.click(checkboxLabel); + expect(mock).toHaveBeenCalledTimes(1); + const checkbox = document.querySelector('input[type=checkbox]'); + fireEvent.click(checkbox); + expect(mock).toHaveBeenCalledTimes(2); + }); }); diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/index.scss b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/index.scss index 814dd4722..771e3a1cb 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/index.scss +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-defaults/index.scss @@ -3,4 +3,8 @@ font-size: $font-size-h4; margin: 0 8px 0 8px; } + + &__label { + display: flex; + } } diff --git a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-gas-limit/advanced-gas-fee-gas-limit.js b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-gas-limit/advanced-gas-fee-gas-limit.js index db5a2a6f8..facc3b132 100644 --- a/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-gas-limit/advanced-gas-fee-gas-limit.js +++ b/ui/components/app/advanced-gas-fee-popover/advanced-gas-fee-gas-limit/advanced-gas-fee-gas-limit.js @@ -45,6 +45,7 @@ const AdvancedGasFeeGasLimit = () => { if (isEditing) { return ( { {gasLimit} + {inPopUp ? renderSendButton() : null} @@ -312,6 +336,7 @@ CollectibleDetails.propTypes = { standard: PropTypes.string, imageThumbnail: PropTypes.string, imagePreview: PropTypes.string, + imageOriginal: PropTypes.string, creator: PropTypes.shape({ address: PropTypes.string, config: PropTypes.string, diff --git a/ui/components/app/collectible-details/index.scss b/ui/components/app/collectible-details/index.scss index 6ce5b556b..e3e83ec9b 100644 --- a/ui/components/app/collectible-details/index.scss +++ b/ui/components/app/collectible-details/index.scss @@ -53,16 +53,38 @@ $spacer-break-small: 16px; overflow-wrap: break-word; } - &__contract-link, - &__image-link { - color: var(--primary-1); + &__contract-wrapper { + max-width: calc(100% - #{$link-title-width}); + } + + &__contract-copy-button { + @include H6; + + width: 80px; + display: flex; + align-items: flex-start; + justify-content: center; + background-color: transparent; + cursor: pointer; + color: var(--ui-4); + border: 0; + + &:active { + transform: scale(0.97); + } + } + + &__contract-link { overflow: hidden; text-overflow: ellipsis; - word-break: break-all; + white-space: nowrap; + } - &:hover { - color: var(--primary-3); - } + &__image-link { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 332px; } &__link-title { diff --git a/ui/components/app/collectibles-detection-notice/collectibles-detection-notice.js b/ui/components/app/collectibles-detection-notice/collectibles-detection-notice.js index 5ac150d8a..db7095409 100644 --- a/ui/components/app/collectibles-detection-notice/collectibles-detection-notice.js +++ b/ui/components/app/collectibles-detection-notice/collectibles-detection-notice.js @@ -20,7 +20,7 @@ export default function CollectiblesDetectionNotice() { const history = useHistory(); return ( - + + {isExpanded ? ( {collectibles.map((collectible, i) => { @@ -132,20 +150,22 @@ export default function CollectiblesItems({ key={`collectible-${i}`} className="collectibles-items__collection-item-wrapper" > -
- - history.push(`${ASSET_ROUTE}/${address}/${tokenId}`) - } - className="collectibles-items__collection-item-image" - src={collectibleImage} - /> -
+ +
+ + history.push(`${ASSET_ROUTE}/${address}/${tokenId}`) + } + className="collectibles-items__collection-item-image" + src={collectibleImage} + /> +
+
); })} diff --git a/ui/components/app/collectibles-items/index.scss b/ui/components/app/collectibles-items/index.scss index 7e427cf47..574efa6e1 100644 --- a/ui/components/app/collectibles-items/index.scss +++ b/ui/components/app/collectibles-items/index.scss @@ -6,6 +6,12 @@ cursor: pointer; } + &-wrapper { + background-color: transparent; + border: 0; + width: 100%; + } + &-image { width: 32px; height: 32px; diff --git a/ui/components/app/collectibles-tab/collectibles-tab.js b/ui/components/app/collectibles-tab/collectibles-tab.js index b0f0d07a8..34c18e032 100644 --- a/ui/components/app/collectibles-tab/collectibles-tab.js +++ b/ui/components/app/collectibles-tab/collectibles-tab.js @@ -117,6 +117,7 @@ export default function CollectiblesTab({ onAddNFT }) { marginBottom={12} justifyContent={JUSTIFY_CONTENT.CENTER} flexDirection={FLEX_DIRECTION.COLUMN} + className="collectibles-tab__link" > {t('learnMoreUpperCase')} @@ -154,27 +154,31 @@ export default function CollectiblesTab({ onAddNFT }) { alignItems={ALIGN_ITEMS.CENTER} justifyContent={JUSTIFY_CONTENT.CENTER} > - - {isMainnet && !useCollectibleDetection ? ( - - ) : ( - - )} - - - {t('or')} - + {!isMainnet && Object.keys(collections).length < 1 ? null : ( + <> + + {isMainnet && !useCollectibleDetection ? ( + + ) : ( + + )} + + + {t('or')} + + + )} , store); diff --git a/ui/components/app/collectibles-tab/index.scss b/ui/components/app/collectibles-tab/index.scss index 2702e32e7..58b0a1c21 100644 --- a/ui/components/app/collectibles-tab/index.scss +++ b/ui/components/app/collectibles-tab/index.scss @@ -2,7 +2,7 @@ &__link { a { padding: 4px; - font-size: 1rem; + font-size: 0.875rem; } } } diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js index 3ccdb21e9..5b86da60f 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.test.js @@ -11,6 +11,7 @@ describe('Confirm Page Container Content', () => { provider: { type: 'test', }, + eip1559V2Enabled: false, }, }; @@ -41,8 +42,6 @@ describe('Confirm Page Container Content', () => { }); it('render ConfirmPageContainer component with simulation error', async () => { - process.env.EIP_1559_V2 = false; - const { queryByText, getByText } = renderWithProvider( , store, 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 20b12acda..d4878442f 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 @@ -84,7 +84,7 @@ ConfirmPageContainerSummary.propTypes = { identiconAddress: PropTypes.string, nonce: PropTypes.string, origin: PropTypes.string.isRequired, - hideTitle: PropTypes.boolean, + hideTitle: PropTypes.bool, }; export default ConfirmPageContainerSummary; diff --git a/ui/components/app/confirm-page-container/confirm-page-container.component.js b/ui/components/app/confirm-page-container/confirm-page-container.component.js index fdf0b8a96..10f326594 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container.component.js @@ -16,6 +16,7 @@ import AdvancedGasFeePopover from '../advanced-gas-fee-popover'; import EditGasFeePopover from '../edit-gas-fee-popover/edit-gas-fee-popover'; import EditGasPopover from '../edit-gas-popover'; +import EnableEIP1559V2Notice from './enableEIP1559V2-notice'; import { ConfirmPageContainerHeader, ConfirmPageContainerContent, @@ -204,6 +205,7 @@ export default class ConfirmPageContainer extends Component { )} + {contentComponent || ( + + + +
+ +
+ ); +} + +EnableEIP1559V2Notice.propTypes = { + isFirstAlert: PropTypes.bool, +}; diff --git a/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.js b/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.js new file mode 100644 index 000000000..c616b7286 --- /dev/null +++ b/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.js @@ -0,0 +1 @@ +export { default } from './enableEIP1559V2-notice'; diff --git a/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.scss b/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.scss new file mode 100644 index 000000000..9615bd0ac --- /dev/null +++ b/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.scss @@ -0,0 +1,33 @@ +.enableEIP1559V2-notice { + &__dialog { + position: relative; + } + + &__close-button { + background-color: transparent; + position: absolute; + right: 0; + top: 0; + + &::after { + position: absolute; + content: '\00D7'; + font-size: 29px; + font-weight: 200; + color: var(--black); + background-color: transparent; + top: 0; + right: 12px; + cursor: pointer; + } + } + + a.enableEIP1559V2-notice__link { + @include H7; + + padding: 0; + justify-content: flex-start; + font-weight: bold; + font-size: 0.75rem; + } +} diff --git a/ui/components/app/currency-input/currency-input.js b/ui/components/app/currency-input/currency-input.js new file mode 100644 index 000000000..79efec149 --- /dev/null +++ b/ui/components/app/currency-input/currency-input.js @@ -0,0 +1,176 @@ +import React, { useContext, useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { useSelector } from 'react-redux'; +import UnitInput from '../../ui/unit-input'; +import CurrencyDisplay from '../../ui/currency-display'; +import { + getValueFromWeiHex, + getWeiHexFromDecimalValue, +} from '../../../helpers/utils/conversions.util'; +import { ETH } from '../../../helpers/constants/common'; +import { I18nContext } from '../../../contexts/i18n'; +import { + getConversionRate, + getNativeCurrency, +} from '../../../ducks/metamask/metamask'; +import { getCurrentCurrency, getShouldShowFiat } from '../../../selectors'; + +/** + * Component that allows user to enter currency values as a number, and props receive a converted + * hex value in WEI. props.value, used as a default or forced value, should be a hex value, which + * gets converted into a decimal value depending on the currency (ETH or Fiat). + * + * @param options0 + * @param options0.hexValue + * @param options0.featureSecondary + * @param options0.onChange + * @param options0.onPreferenceToggle + */ +export default function CurrencyInput({ + hexValue, + featureSecondary, + onChange, + onPreferenceToggle, +}) { + const t = useContext(I18nContext); + + const preferredCurrency = useSelector(getNativeCurrency); + const secondaryCurrency = useSelector(getCurrentCurrency); + const conversionRate = useSelector(getConversionRate); + const showFiat = useSelector(getShouldShowFiat); + const hideSecondary = !showFiat; + const primarySuffix = preferredCurrency || ETH; + const secondarySuffix = secondaryCurrency.toUpperCase(); + + const [isSwapped, setSwapped] = useState(false); + const [newHexValue, setNewHexValue] = useState(hexValue); + + const shouldUseFiat = () => { + if (hideSecondary) { + return false; + } + + return Boolean(featureSecondary); + }; + + const getDecimalValue = () => { + const decimalValueString = shouldUseFiat() + ? getValueFromWeiHex({ + value: hexValue, + toCurrency: secondaryCurrency, + conversionRate, + numberOfDecimals: 2, + }) + : getValueFromWeiHex({ + value: hexValue, + toCurrency: ETH, + numberOfDecimals: 8, + }); + + return Number(decimalValueString) || 0; + }; + + const initialDecimalValue = hexValue ? getDecimalValue() : 0; + const [decimalValue, setDecimalValue] = useState(initialDecimalValue); + + useEffect(() => { + setNewHexValue(hexValue); + const newDecimalValue = getDecimalValue(); + setDecimalValue(newDecimalValue); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hexValue]); + + const swap = async () => { + await onPreferenceToggle(!featureSecondary); + setSwapped(!isSwapped); + }; + + const handleChange = (newDecimalValue) => { + const hexValueNew = shouldUseFiat() + ? getWeiHexFromDecimalValue({ + value: newDecimalValue, + fromCurrency: secondaryCurrency, + conversionRate, + invertConversionRate: true, + }) + : getWeiHexFromDecimalValue({ + value: newDecimalValue, + fromCurrency: ETH, + fromDenomination: ETH, + conversionRate, + }); + + setNewHexValue(hexValueNew); + setDecimalValue(newDecimalValue); + onChange(hexValueNew); + setSwapped(!isSwapped); + }; + + useEffect(() => { + if (isSwapped) { + handleChange(decimalValue); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isSwapped]); + + const renderConversionComponent = () => { + let currency, numberOfDecimals; + + if (hideSecondary) { + return ( +
+ {t('noConversionRateAvailable')} +
+ ); + } + + if (shouldUseFiat()) { + // Display ETH + currency = preferredCurrency || ETH; + numberOfDecimals = 8; + } else { + // Display Fiat + currency = secondaryCurrency; + numberOfDecimals = 2; + } + + return ( + + ); + }; + + return ( + + } + > + {renderConversionComponent()} + + ); +} + +CurrencyInput.propTypes = { + hexValue: PropTypes.string, + featureSecondary: PropTypes.bool, + onChange: PropTypes.func, + onPreferenceToggle: PropTypes.func, +}; diff --git a/ui/components/ui/currency-input/currency-input.component.test.js b/ui/components/app/currency-input/currency-input.test.js similarity index 57% rename from ui/components/ui/currency-input/currency-input.component.test.js rename to ui/components/app/currency-input/currency-input.test.js index 840871cb3..a12e529df 100644 --- a/ui/components/ui/currency-input/currency-input.component.test.js +++ b/ui/components/app/currency-input/currency-input.test.js @@ -1,17 +1,35 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { shallow, mount } from 'enzyme'; +import { mount } from 'enzyme'; import sinon from 'sinon'; import { Provider } from 'react-redux'; import configureMockStore from 'redux-mock-store'; -import UnitInput from '../unit-input'; -import CurrencyDisplay from '../currency-display'; -import CurrencyInput from './currency-input.component'; +import UnitInput from '../../ui/unit-input'; +import CurrencyDisplay from '../../ui/currency-display'; +import CurrencyInput from './currency-input'; describe('CurrencyInput Component', () => { describe('rendering', () => { it('should render properly without a suffix', () => { - const wrapper = shallow(); + const mockStore = { + metamask: { + nativeCurrency: 'ETH', + currentCurrency: 'usd', + conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, + }, + }; + const store = configureMockStore()(mockStore); + const wrapper = mount( + + + , + ); expect(wrapper).toHaveLength(1); expect(wrapper.find(UnitInput)).toHaveLength(1); @@ -23,17 +41,19 @@ describe('CurrencyInput Component', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, }, }; const store = configureMockStore()(mockStore); const wrapper = mount( - + , ); @@ -49,32 +69,23 @@ describe('CurrencyInput Component', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, }, }; const store = configureMockStore()(mockStore); const wrapper = mount( - + , ); expect(wrapper).toHaveLength(1); - const currencyInputInstance = wrapper - .find(CurrencyInput) - .at(0) - .instance(); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(1); - expect(currencyInputInstance.state.hexValue).toStrictEqual( - 'de0b6b3a7640000', - ); expect(wrapper.find('.unit-input__suffix')).toHaveLength(1); expect(wrapper.find('.unit-input__suffix').text()).toStrictEqual('ETH'); expect(wrapper.find('.unit-input__input').props().value).toStrictEqual(1); @@ -89,33 +100,23 @@ describe('CurrencyInput Component', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, }, }; const store = configureMockStore()(mockStore); const wrapper = mount( - + , ); expect(wrapper).toHaveLength(1); - const currencyInputInstance = wrapper - .find(CurrencyInput) - .at(0) - .instance(); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(1); - expect(currencyInputInstance.state.hexValue).toStrictEqual( - 'f602f2234d0ea', - ); expect(wrapper.find('.unit-input__suffix')).toHaveLength(1); expect(wrapper.find('.unit-input__suffix').text()).toStrictEqual('USD'); expect(wrapper.find('.unit-input__input').props().value).toStrictEqual(1); @@ -124,29 +125,27 @@ describe('CurrencyInput Component', () => { ); }); - it('should render properly with a native value when hideFiat is true', () => { + it('should render properly with a native value when hideSecondary is true', () => { const mockStore = { metamask: { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: false, + }, }, + hideSecondary: true, }; const store = configureMockStore()(mockStore); const wrapper = mount( - + , { context: { t: (str) => `${str}_t` }, @@ -155,16 +154,6 @@ describe('CurrencyInput Component', () => { ); expect(wrapper).toHaveLength(1); - const currencyInputInstance = wrapper - .find(CurrencyInput) - .at(0) - .instance(); - expect(currencyInputInstance.state.decimalValue).toStrictEqual( - 0.00432788, - ); - expect(currencyInputInstance.state.hexValue).toStrictEqual( - 'f602f2234d0ea', - ); expect(wrapper.find('.unit-input__suffix')).toHaveLength(1); expect(wrapper.find('.unit-input__suffix').text()).toStrictEqual('ETH'); expect(wrapper.find('.unit-input__input').props().value).toStrictEqual( @@ -172,17 +161,19 @@ describe('CurrencyInput Component', () => { ); expect( wrapper.find('.currency-input__conversion-component').text(), - ).toStrictEqual('noConversionRateAvailable_t'); + ).toStrictEqual('[noConversionRateAvailable]'); }); }); describe('handling actions', () => { const handleChangeSpy = sinon.spy(); const handleBlurSpy = sinon.spy(); + const handleChangeToggle = sinon.spy(); afterEach(() => { handleChangeSpy.resetHistory(); handleBlurSpy.resetHistory(); + handleChangeToggle.resetHistory(); }); it('should call onChange on input changes with the hex value for ETH', () => { @@ -191,18 +182,18 @@ describe('CurrencyInput Component', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, }, }; const store = configureMockStore()(mockStore); const wrapper = mount( - + , ); @@ -210,28 +201,15 @@ describe('CurrencyInput Component', () => { expect(handleChangeSpy.callCount).toStrictEqual(0); expect(handleBlurSpy.callCount).toStrictEqual(0); - const currencyInputInstance = wrapper - .find(CurrencyInput) - .at(0) - .instance(); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(0); - expect(currencyInputInstance.state.hexValue).toBeUndefined(); - expect(wrapper.find('.currency-display-component').text()).toStrictEqual( - '$0.00USD', - ); const input = wrapper.find('input'); - expect(input.props().value).toStrictEqual(0); + expect(input.props().value).toStrictEqual(0.00432788); input.simulate('change', { target: { value: 1 } }); - expect(handleChangeSpy.callCount).toStrictEqual(1); + expect(handleChangeSpy.callCount).toStrictEqual(2); expect(handleChangeSpy.calledWith('de0b6b3a7640000')).toStrictEqual(true); expect(wrapper.find('.currency-display-component').text()).toStrictEqual( '$231.06USD', ); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(1); - expect(currencyInputInstance.state.hexValue).toStrictEqual( - 'de0b6b3a7640000', - ); }); it('should call onChange on input changes with the hex value for fiat', () => { @@ -240,19 +218,18 @@ describe('CurrencyInput Component', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, }, }; const store = configureMockStore()(mockStore); const wrapper = mount( - + , ); @@ -260,12 +237,6 @@ describe('CurrencyInput Component', () => { expect(handleChangeSpy.callCount).toStrictEqual(0); expect(handleBlurSpy.callCount).toStrictEqual(0); - const currencyInputInstance = wrapper - .find(CurrencyInput) - .at(0) - .instance(); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(0); - expect(currencyInputInstance.state.hexValue).toBeUndefined(); expect(wrapper.find('.currency-display-component').text()).toStrictEqual( '0ETH', ); @@ -273,15 +244,11 @@ describe('CurrencyInput Component', () => { expect(input.props().value).toStrictEqual(0); input.simulate('change', { target: { value: 1 } }); - expect(handleChangeSpy.callCount).toStrictEqual(1); + expect(handleChangeSpy.callCount).toStrictEqual(2); expect(handleChangeSpy.calledWith('f602f2234d0ea')).toStrictEqual(true); expect(wrapper.find('.currency-display-component').text()).toStrictEqual( '0.00432788ETH', ); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(1); - expect(currencyInputInstance.state.hexValue).toStrictEqual( - 'f602f2234d0ea', - ); }); it('should change the state and pass in a new decimalValue when props.value changes', () => { @@ -290,39 +257,28 @@ describe('CurrencyInput Component', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, }, }; const store = configureMockStore()(mockStore); - const wrapper = shallow( + const wrapper = mount( - + , ); expect(wrapper).toHaveLength(1); - const currencyInputInstance = wrapper.find(CurrencyInput).dive(); - expect(currencyInputInstance.state('decimalValue')).toStrictEqual(0); - expect(currencyInputInstance.state('hexValue')).toBeUndefined(); - expect(currencyInputInstance.find(UnitInput).props().value).toStrictEqual( - 0, - ); + const input = wrapper.find('input'); + expect(input.props().value).toStrictEqual(0); - currencyInputInstance.setProps({ value: '1ec05e43e72400' }); - currencyInputInstance.update(); - expect(currencyInputInstance.state('decimalValue')).toStrictEqual(2); - expect(currencyInputInstance.state('hexValue')).toStrictEqual( - '1ec05e43e72400', - ); - expect(currencyInputInstance.find(UnitInput).props().value).toStrictEqual( - 2, - ); + wrapper.setProps({ hexValue: '1ec05e43e72400' }); + input.update(); + expect(input.props().value).toStrictEqual(0); }); it('should swap selected currency when swap icon is clicked', () => { @@ -331,6 +287,12 @@ describe('CurrencyInput Component', () => { nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, + provider: { + chainId: '0x4', + }, + preferences: { + showFiatInTestnets: true, + }, }, }; const store = configureMockStore()(mockStore); @@ -338,11 +300,8 @@ describe('CurrencyInput Component', () => { , ); @@ -351,27 +310,19 @@ describe('CurrencyInput Component', () => { expect(handleChangeSpy.callCount).toStrictEqual(0); expect(handleBlurSpy.callCount).toStrictEqual(0); - const currencyInputInstance = wrapper - .find(CurrencyInput) - .at(0) - .instance(); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(0); - expect(currencyInputInstance.state.hexValue).toBeUndefined(); expect(wrapper.find('.currency-display-component').text()).toStrictEqual( - '$0.00USD', + '0ETH', ); const input = wrapper.find('input'); expect(input.props().value).toStrictEqual(0); input.simulate('change', { target: { value: 1 } }); - expect(handleChangeSpy.callCount).toStrictEqual(1); - expect(handleChangeSpy.calledWith('de0b6b3a7640000')).toStrictEqual(true); - expect(wrapper.find('.currency-display-component').text()).toStrictEqual( - '$231.06USD', + expect(handleChangeSpy.callCount).toStrictEqual(2); + expect(handleChangeSpy.calledWith('de0b6b3a7640000')).toStrictEqual( + false, ); - expect(currencyInputInstance.state.decimalValue).toStrictEqual(1); - expect(currencyInputInstance.state.hexValue).toStrictEqual( - 'de0b6b3a7640000', + expect(wrapper.find('.currency-display-component').text()).toStrictEqual( + '0.00432788ETH', ); const swap = wrapper.find('.currency-input__swap-component'); diff --git a/ui/components/app/currency-input/index.js b/ui/components/app/currency-input/index.js new file mode 100644 index 000000000..ff5702494 --- /dev/null +++ b/ui/components/app/currency-input/index.js @@ -0,0 +1 @@ +export { default } from './currency-input'; diff --git a/ui/components/ui/currency-input/index.scss b/ui/components/app/currency-input/index.scss similarity index 100% rename from ui/components/ui/currency-input/index.scss rename to ui/components/app/currency-input/index.scss diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index c72d606c5..4e0aa83cb 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -2,6 +2,7 @@ import React, { useContext, useLayoutEffect, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; +import BigNumber from 'bignumber.js'; import { GAS_RECOMMENDATIONS, EDIT_GAS_MODES, @@ -57,6 +58,7 @@ export default function EditGasDisplay({ setGasPrice, gasLimit, setGasLimit, + properGasLimit, estimateToUse, setEstimateToUse, estimatedMinimumFiat, @@ -107,6 +109,15 @@ export default function EditGasDisplay({ dappSuggestedAndTxParamGasFeesAreTheSame, ); + let warningMessage; + if ( + gasLimit !== undefined && + properGasLimit !== undefined && + new BigNumber(gasLimit).lessThan(new BigNumber(properGasLimit)) + ) { + warningMessage = t('gasLimitRecommended', [properGasLimit]); + } + const showTopError = (balanceError || estimatesUnavailableWarning) && (!isGasEstimatesLoading || txParamsHaveBeenCustomized); @@ -138,6 +149,16 @@ export default function EditGasDisplay({ )} + {warningMessage && ( +
+ +
+ )} {requireDappAcknowledgement && !isGasEstimatesLoading && (
- + ); @@ -104,6 +108,18 @@ TypeLink.args = { children: 'Click me', }; +export const TypeInline = (args) => ( +
+ this is a inline button + +
+); + +TypeInline.args = { + type: 'inline', + children: 'Click me', +}; + export const Icon = (args) => ; Icon.args = { diff --git a/ui/components/ui/button/buttons.scss b/ui/components/ui/button/buttons.scss index acaa94a24..c0f61531f 100644 --- a/ui/components/ui/button/buttons.scss +++ b/ui/components/ui/button/buttons.scss @@ -318,3 +318,29 @@ input[type="submit"][disabled] { } } } + +.btn--inline { + display: inline; + padding: 0 4px; + font-size: inherit; + width: auto; + color: var(--Blue-500); + cursor: pointer; + background-color: transparent; + + &:hover { + color: var(--Blue-400); + } + + &:active { + color: var(--Blue-600); + } + + &--disabled, + &[disabled] { + cursor: auto; + opacity: 1; + pointer-events: none; + color: var(--hover-secondary); + } +} diff --git a/ui/components/ui/currency-input/currency-input.component.js b/ui/components/ui/currency-input/currency-input.component.js deleted file mode 100644 index 8c6276f34..000000000 --- a/ui/components/ui/currency-input/currency-input.component.js +++ /dev/null @@ -1,172 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import UnitInput from '../unit-input'; -import CurrencyDisplay from '../currency-display'; -import { - getValueFromWeiHex, - getWeiHexFromDecimalValue, -} from '../../../helpers/utils/conversions.util'; -import { ETH } from '../../../helpers/constants/common'; - -/** - * Component that allows user to enter currency values as a number, and props receive a converted - * hex value in WEI. props.value, used as a default or forced value, should be a hex value, which - * gets converted into a decimal value depending on the currency (ETH or Fiat). - */ -export default class CurrencyInput extends PureComponent { - static contextTypes = { - t: PropTypes.func, - }; - - static propTypes = { - conversionRate: PropTypes.number, - currentCurrency: PropTypes.string, - nativeCurrency: PropTypes.string, - onChange: PropTypes.func, - useFiat: PropTypes.bool, - hideFiat: PropTypes.bool, - value: PropTypes.string, - fiatSuffix: PropTypes.string, - nativeSuffix: PropTypes.string, - }; - - constructor(props) { - super(props); - - const { value: hexValue } = props; - const decimalValue = hexValue ? this.getDecimalValue(props) : 0; - - this.state = { - decimalValue, - hexValue, - isSwapped: false, - }; - } - - componentDidUpdate(prevProps) { - const { value: prevPropsHexValue } = prevProps; - const { value: propsHexValue } = this.props; - const { hexValue: stateHexValue } = this.state; - - if ( - prevPropsHexValue !== propsHexValue && - propsHexValue !== stateHexValue - ) { - const decimalValue = this.getDecimalValue(this.props); - this.setState({ hexValue: propsHexValue, decimalValue }); - } - } - - getDecimalValue(props) { - const { value: hexValue, currentCurrency, conversionRate } = props; - const decimalValueString = this.shouldUseFiat() - ? getValueFromWeiHex({ - value: hexValue, - toCurrency: currentCurrency, - conversionRate, - numberOfDecimals: 2, - }) - : getValueFromWeiHex({ - value: hexValue, - toCurrency: ETH, - numberOfDecimals: 8, - }); - - return Number(decimalValueString) || 0; - } - - shouldUseFiat = () => { - const { useFiat, hideFiat } = this.props; - const { isSwapped } = this.state || {}; - - if (hideFiat) { - return false; - } - - return isSwapped ? !useFiat : useFiat; - }; - - swap = () => { - const { isSwapped, decimalValue } = this.state; - this.setState({ isSwapped: !isSwapped }, () => { - this.handleChange(decimalValue); - }); - }; - - handleChange = (decimalValue) => { - const { - currentCurrency: fromCurrency, - conversionRate, - onChange, - } = this.props; - - const hexValue = this.shouldUseFiat() - ? getWeiHexFromDecimalValue({ - value: decimalValue, - fromCurrency, - conversionRate, - invertConversionRate: true, - }) - : getWeiHexFromDecimalValue({ - value: decimalValue, - fromCurrency: ETH, - fromDenomination: ETH, - conversionRate, - }); - - this.setState({ hexValue, decimalValue }); - onChange(hexValue); - }; - - renderConversionComponent() { - const { currentCurrency, nativeCurrency, hideFiat } = this.props; - const { hexValue } = this.state; - let currency, numberOfDecimals; - - if (hideFiat) { - return ( -
- {this.context.t('noConversionRateAvailable')} -
- ); - } - - if (this.shouldUseFiat()) { - // Display ETH - currency = nativeCurrency || ETH; - numberOfDecimals = 8; - } else { - // Display Fiat - currency = currentCurrency; - numberOfDecimals = 2; - } - - return ( - - ); - } - - render() { - const { fiatSuffix, nativeSuffix, ...restProps } = this.props; - const { decimalValue } = this.state; - - return ( - - } - > - {this.renderConversionComponent()} - - ); - } -} diff --git a/ui/components/ui/currency-input/currency-input.container.js b/ui/components/ui/currency-input/currency-input.container.js deleted file mode 100644 index 1d3c09620..000000000 --- a/ui/components/ui/currency-input/currency-input.container.js +++ /dev/null @@ -1,32 +0,0 @@ -import { connect } from 'react-redux'; -import { ETH } from '../../../helpers/constants/common'; -import { getShouldShowFiat } from '../../../selectors'; -import CurrencyInput from './currency-input.component'; - -const mapStateToProps = (state) => { - const { - metamask: { nativeCurrency, currentCurrency, conversionRate }, - } = state; - const showFiat = getShouldShowFiat(state); - - return { - nativeCurrency, - currentCurrency, - conversionRate, - hideFiat: !showFiat, - }; -}; - -const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { nativeCurrency, currentCurrency } = stateProps; - - return { - ...stateProps, - ...dispatchProps, - ...ownProps, - nativeSuffix: nativeCurrency || ETH, - fiatSuffix: currentCurrency.toUpperCase(), - }; -}; - -export default connect(mapStateToProps, null, mergeProps)(CurrencyInput); diff --git a/ui/components/ui/currency-input/currency-input.container.test.js b/ui/components/ui/currency-input/currency-input.container.test.js deleted file mode 100644 index e4a153bb1..000000000 --- a/ui/components/ui/currency-input/currency-input.container.test.js +++ /dev/null @@ -1,182 +0,0 @@ -// eslint-disable-next-line import/unambiguous -let mapStateToProps, mergeProps; - -jest.mock('react-redux', () => ({ - connect: (ms, _, mp) => { - mapStateToProps = ms; - mergeProps = mp; - return () => ({}); - }, -})); - -require('./currency-input.container.js'); - -describe('CurrencyInput container', () => { - describe('mapStateToProps()', () => { - const tests = [ - // Test # 1 - { - comment: 'should return correct props in mainnet', - mockState: { - metamask: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - preferences: { - showFiatInTestnets: false, - }, - provider: { - type: 'mainnet', - chainId: '0x1', - }, - }, - }, - expected: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - hideFiat: false, - }, - }, - // Test # 2 - { - comment: - 'should return correct props when not in mainnet and showFiatInTestnets is false', - mockState: { - metamask: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - preferences: { - showFiatInTestnets: false, - }, - provider: { - type: 'rinkeby', - }, - }, - }, - expected: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - hideFiat: true, - }, - }, - // Test # 3 - { - comment: - 'should return correct props when not in mainnet and showFiatInTestnets is true', - mockState: { - metamask: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - preferences: { - showFiatInTestnets: true, - }, - provider: { - type: 'rinkeby', - }, - }, - }, - expected: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - hideFiat: false, - }, - }, - // Test # 4 - { - comment: - 'should return correct props when in mainnet and showFiatInTestnets is true', - mockState: { - metamask: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - preferences: { - showFiatInTestnets: true, - }, - provider: { - type: 'mainnet', - }, - }, - }, - expected: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - hideFiat: false, - }, - }, - ]; - - tests.forEach(({ mockState, expected, comment }) => { - it(`${comment}`, () => { - expect(mapStateToProps(mockState)).toStrictEqual(expected); - }); - }); - }); - - describe('mergeProps()', () => { - const tests = [ - // Test # 1 - { - comment: 'should return the correct props', - mock: { - stateProps: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - }, - dispatchProps: {}, - ownProps: {}, - }, - expected: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - // useFiat: true, - nativeSuffix: 'ETH', - fiatSuffix: 'USD', - }, - }, - // Test # 1 - { - comment: 'should return the correct props when useFiat is true', - mock: { - stateProps: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - }, - dispatchProps: {}, - ownProps: { useFiat: true }, - }, - expected: { - conversionRate: 280.45, - currentCurrency: 'usd', - nativeCurrency: 'ETH', - useFiat: true, - nativeSuffix: 'ETH', - fiatSuffix: 'USD', - }, - }, - ]; - - tests.forEach( - ({ - mock: { stateProps, dispatchProps, ownProps }, - expected, - comment, - }) => { - it(`${comment}`, () => { - expect(mergeProps(stateProps, dispatchProps, ownProps)).toStrictEqual( - expected, - ); - }); - }, - ); - }); -}); diff --git a/ui/components/ui/currency-input/index.js b/ui/components/ui/currency-input/index.js deleted file mode 100644 index 4f3b7bedf..000000000 --- a/ui/components/ui/currency-input/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './currency-input.container'; diff --git a/ui/components/ui/form-field/form-field.js b/ui/components/ui/form-field/form-field.js index b8af84fab..2a845a1c0 100644 --- a/ui/components/ui/form-field/form-field.js +++ b/ui/components/ui/form-field/form-field.js @@ -30,6 +30,7 @@ export default function FormField({ password, allowDecimals, disabled, + placeholder, }) { return (
) : ( )} {error && ( @@ -169,6 +173,10 @@ FormField.propTypes = { * Check if the form disabled */ disabled: PropTypes.bool, + /** + * Set the placeholder text for the input field + */ + placeholder: PropTypes.string, }; FormField.defaultProps = { diff --git a/ui/components/ui/form-field/form-field.stories.js b/ui/components/ui/form-field/form-field.stories.js index 745c845a0..3d5ee3b7f 100644 --- a/ui/components/ui/form-field/form-field.stories.js +++ b/ui/components/ui/form-field/form-field.stories.js @@ -30,6 +30,7 @@ export default { password: { control: 'boolean' }, allowDecimals: { control: 'boolean' }, disabled: { control: 'boolean' }, + placeholder: { control: 'text' }, }, }; diff --git a/ui/components/ui/loading-heartbeat/index.js b/ui/components/ui/loading-heartbeat/index.js index a94855b91..2aed84dd2 100644 --- a/ui/components/ui/loading-heartbeat/index.js +++ b/ui/components/ui/loading-heartbeat/index.js @@ -1,17 +1,26 @@ +import { useSelector } from 'react-redux'; import classNames from 'classnames'; +import PropTypes from 'prop-types'; import React from 'react'; -import { useSelector } from 'react-redux'; + +import { isVolatileGasEstimate } from '../../../helpers/utils/gas'; import { getGasLoadingAnimationIsShowing } from '../../../ducks/app/app'; import { useShouldAnimateGasEstimations } from '../../../hooks/useShouldAnimateGasEstimations'; const BASE_CLASS = 'loading-heartbeat'; const LOADING_CLASS = `${BASE_CLASS}--active`; -export default function LoadingHeartBeat() { +export default function LoadingHeartBeat({ + estimateUsed, + backgroundColor = '#fff', +}) { useShouldAnimateGasEstimations(); const active = useSelector(getGasLoadingAnimationIsShowing); - if (process.env.IN_TEST) { + if ( + process.env.IN_TEST || + (estimateUsed && !isVolatileGasEstimate(estimateUsed)) + ) { return null; } @@ -24,6 +33,12 @@ export default function LoadingHeartBeat() { e.preventDefault(); e.stopPropagation(); }} + style={{ backgroundColor }} >
); } + +LoadingHeartBeat.propTypes = { + backgroundColor: PropTypes.string, + estimateUsed: PropTypes.string, +}; diff --git a/ui/components/ui/numeric-input/numeric-input.component.js b/ui/components/ui/numeric-input/numeric-input.component.js index dcff711d3..33b82da32 100644 --- a/ui/components/ui/numeric-input/numeric-input.component.js +++ b/ui/components/ui/numeric-input/numeric-input.component.js @@ -14,6 +14,8 @@ export default function NumericInput({ autoFocus = false, allowDecimals = true, disabled = false, + dataTestId, + placeholder, }) { return (
{detailText && ( @@ -56,4 +60,6 @@ NumericInput.propTypes = { autoFocus: PropTypes.bool, allowDecimals: PropTypes.bool, disabled: PropTypes.bool, + dataTestId: PropTypes.string, + placeholder: PropTypes.string, }; diff --git a/ui/components/ui/page-container/index.scss b/ui/components/ui/page-container/index.scss index a249bb912..45f5f8854 100644 --- a/ui/components/ui/page-container/index.scss +++ b/ui/components/ui/page-container/index.scss @@ -28,6 +28,7 @@ right: 16px; cursor: pointer; overflow: hidden; + background-color: transparent; &::after { content: '\00D7'; diff --git a/ui/components/ui/page-container/page-container-header/page-container-header.component.js b/ui/components/ui/page-container/page-container-header/page-container-header.component.js index 78f71b65a..9b4787e0e 100644 --- a/ui/components/ui/page-container/page-container-header/page-container-header.component.js +++ b/ui/components/ui/page-container/page-container-header/page-container-header.component.js @@ -47,9 +47,10 @@ export default class PageContainerHeader extends Component { return ( onClose && ( -
onClose()} + aria-label="close" /> ) ); diff --git a/ui/components/ui/text-field/text-field.component.js b/ui/components/ui/text-field/text-field.component.js index 8720ff19b..44a96ee3f 100644 --- a/ui/components/ui/text-field/text-field.component.js +++ b/ui/components/ui/text-field/text-field.component.js @@ -246,7 +246,7 @@ TextField.propTypes = { /** * Show error message */ - error: PropTypes.string, + error: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), /** * Add custom CSS class */ diff --git a/ui/components/ui/typography/README.mdx b/ui/components/ui/typography/README.mdx new file mode 100644 index 000000000..1286a8b1c --- /dev/null +++ b/ui/components/ui/typography/README.mdx @@ -0,0 +1,16 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; +import Typography from '.'; + +# Typography + + + + + + + + + +## Component API + + diff --git a/ui/components/ui/typography/typography.stories.js b/ui/components/ui/typography/typography.stories.js index 2f761bfcf..119860c25 100644 --- a/ui/components/ui/typography/typography.stories.js +++ b/ui/components/ui/typography/typography.stories.js @@ -1,52 +1,65 @@ import React from 'react'; -import { number, select, text } from '@storybook/addon-knobs'; import { COLORS, FONT_WEIGHT, TEXT_ALIGN, TYPOGRAPHY, } from '../../../helpers/constants/design-system'; +import README from './README.mdx'; import Typography from '.'; export default { title: 'Components/UI/Typography', id: __filename, + parameters: { + docs: { + page: README, + }, + }, + argTypes: { + color: { + control: { type: 'select' }, + options: COLORS, + defaultValue: COLORS.BLACK, + }, + align: { + control: { type: 'select' }, + options: TEXT_ALIGN, + defaultValue: TEXT_ALIGN.LEFT, + }, + fontWeight: { + control: { type: 'select' }, + options: FONT_WEIGHT, + defaultValue: FONT_WEIGHT.NORMAL, + }, + variant: { + control: { type: 'select' }, + options: TYPOGRAPHY, + defaultValue: TYPOGRAPHY.Paragraph, + }, + content: { + control: { type: 'text' }, + defaultValue: 'The quick orange fox jumped over the lazy dog.', + }, + }, }; -export const List = () => ( +export const DefaultStory = (args) => (
{Object.values(TYPOGRAPHY).map((variant) => (
- - {variant} - + {variant}
))}
); -export const TheQuickOrangeFox = () => ( +DefaultStory.storyName = 'List'; + +export const TheQuickOrangeFox = (args) => (
- - {text('content', 'The quick orange fox jumped over the lazy dog.')} - + {args.content}
); diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss index 4c0e871a1..cf85f6cf0 100644 --- a/ui/components/ui/ui-components.scss +++ b/ui/components/ui/ui-components.scss @@ -15,7 +15,6 @@ @import 'color-indicator/color-indicator'; @import 'confusable/index'; @import 'currency-display/index'; -@import 'currency-input/index'; @import 'definition-list/definition-list'; @import 'dialog/dialog'; @import 'dropdown/dropdown'; diff --git a/ui/components/ui/update-nickname-popover/README.mdx b/ui/components/ui/update-nickname-popover/README.mdx new file mode 100644 index 000000000..28074738c --- /dev/null +++ b/ui/components/ui/update-nickname-popover/README.mdx @@ -0,0 +1,14 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; +import UpdateNicknamePopover from '.'; + +# UpdateNicknamePopover + +Popover to update nickname of an address + + + + + +## Component API + + diff --git a/ui/components/ui/update-nickname-popover/update-nickname-popover.stories.js b/ui/components/ui/update-nickname-popover/update-nickname-popover.stories.js index 4e870b01f..fa8973298 100644 --- a/ui/components/ui/update-nickname-popover/update-nickname-popover.stories.js +++ b/ui/components/ui/update-nickname-popover/update-nickname-popover.stories.js @@ -1,44 +1,71 @@ -import React, { useState } from 'react'; -import { action } from '@storybook/addon-actions'; -import { text } from '@storybook/addon-knobs'; +import { useArgs } from '@storybook/client-api'; +import React from 'react'; import Button from '../button'; +import README from './README.mdx'; import UpdateNicknamePopover from '.'; export default { - title: 'Components/UI/UpdateNickname', + title: 'Components/UI/UpdateNicknamePopover', id: __filename, + parameters: { + docs: { + page: README, + }, + }, + argTypes: { + address: { + control: { type: 'text' }, + defaultValue: '0xdeDbcA0156308960E3bBa2f5a273E72179940788', + }, + showPopover: { + control: { type: 'boolean' }, + defaultValue: false, + }, + onAdd: { action: 'onAdd' }, + onClose: { action: 'onClose' }, + }, }; -export const AddNickname = () => { - const [showPopover, setShowPopover] = useState(false); +export const DefaultStory = (args) => { + const [{ showPopover }, updateArgs] = useArgs(); + + const handlePopoverState = () => { + updateArgs({ + showPopover: !showPopover, + }); + }; + return (
- + {showPopover && ( action(`Close Update Nickname Popover`)()} + {...args} + nickname="user_nickname" + memo="This is a memo" + onClose={handlePopoverState} /> )}
); }; -export const UpdateNickname = () => { - const [showPopover, setShowPopover] = useState(false); +DefaultStory.storyName = 'UpdateNickname'; + +export const AddNickname = (args) => { + const [{ showPopover }, updateArgs] = useArgs(); + + const handlePopoverState = () => { + updateArgs({ + showPopover: !showPopover, + }); + }; + return (
- + {showPopover && ( - action(`Close Update Nickname Popover`)()} - /> + )}
); diff --git a/ui/contexts/metametrics.js b/ui/contexts/metametrics.js index 3e2b9786a..bb23afef8 100644 --- a/ui/contexts/metametrics.js +++ b/ui/contexts/metametrics.js @@ -3,7 +3,6 @@ import React, { createContext, useEffect, useCallback, - useContext, useState, } from 'react'; import { useSelector } from 'react-redux'; @@ -125,10 +124,6 @@ export function MetaMetricsProvider({ children }) { MetaMetricsProvider.propTypes = { children: PropTypes.node }; -export function useMetaMetricsContext() { - return useContext(MetaMetricsContext); -} - export class LegacyMetaMetricsProvider extends Component { static propTypes = { children: PropTypes.node, diff --git a/ui/contexts/transaction-modal.js b/ui/contexts/transaction-modal.js index 626e623d5..d7967ba7f 100644 --- a/ui/contexts/transaction-modal.js +++ b/ui/contexts/transaction-modal.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { TRANSACTION_TYPES } from '../../shared/constants/transaction'; import { getMethodName } from '../helpers/utils/metrics'; import { useGasFeeContext } from './gasFee'; -import { useMetaMetricsContext } from './metametrics'; +import { MetaMetricsContext } from './metametrics'; export const TransactionModalContext = createContext({}); @@ -15,7 +15,7 @@ export const TransactionModalContextProvider = ({ captureEventEnabled = true, }) => { const [openModals, setOpenModals] = useState([]); - const metricsEvent = useMetaMetricsContext(); + const metricsEvent = useContext(MetaMetricsContext); const { transaction: { origin } = {} } = useGasFeeContext(); const captureEvent = () => { diff --git a/ui/css/base-styles.scss b/ui/css/base-styles.scss index 6ee85339f..b3fe24ac0 100644 --- a/ui/css/base-styles.scss +++ b/ui/css/base-styles.scss @@ -45,7 +45,6 @@ html { /* This warning class is used in the following files still: - /ui/pages/create-account/import-account/json.js /ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js */ .warning { @@ -72,7 +71,7 @@ a { } a:hover { - color: #df6b0e; + color: var(--Blue-400); } input.large-input, diff --git a/ui/css/itcss/components/send.scss b/ui/css/itcss/components/send.scss index 512f21fc1..90b4743a0 100644 --- a/ui/css/itcss/components/send.scss +++ b/ui/css/itcss/components/send.scss @@ -203,12 +203,12 @@ box-shadow: 2px 2px 2px var(--alto); } -.customize-gas-tooltip-container input[type="number"]::-webkit-inner-spin-button { +.customize-gas-tooltip-container input[type='number']::-webkit-inner-spin-button { -webkit-appearance: none; display: none; } -.customize-gas-tooltip-container input[type="number"]:hover::-webkit-inner-spin-button { +.customize-gas-tooltip-container input[type='number']:hover::-webkit-inner-spin-button { -webkit-appearance: none; display: none; } @@ -684,10 +684,19 @@ flex-grow: 1; } + @media screen and (max-width: $break-small) { + &__asset-data { + width: 60%; + } + } + &__symbol { @include Paragraph; margin-bottom: 2px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } &__name { @@ -698,6 +707,7 @@ &__label { margin-right: 0.25rem; + min-width: 52px; } } @@ -1061,12 +1071,12 @@ justify-content: center; } - input[type="number"]::-webkit-inner-spin-button { + input[type='number']::-webkit-inner-spin-button { -webkit-appearance: none; display: none; } - input[type="number"]:hover::-webkit-inner-spin-button { + input[type='number']:hover::-webkit-inner-spin-button { -webkit-appearance: none; display: none; } diff --git a/ui/ducks/app/app.js b/ui/ducks/app/app.js index 47c440e00..cc43bf999 100644 --- a/ui/ducks/app/app.js +++ b/ui/ducks/app/app.js @@ -56,6 +56,7 @@ export default function reduceApp(state = {}, action) { ledgerTransportStatus: TRANSPORT_STATES.NONE, newNetworkAdded: '', newCollectibleAddedMessage: '', + sendInputCurrencySwitched: false, ...state, }; @@ -365,7 +366,11 @@ export default function reduceApp(state = {}, action) { ...appState, ledgerTransportStatus: action.value, }; - + case actionConstants.SET_CURRENCY_INPUT_SWITCH: + return { + ...appState, + sendInputCurrencySwitched: action.value, + }; default: return appState; } @@ -413,3 +418,7 @@ export function getLedgerWebHidConnectedStatus(state) { export function getLedgerTransportStatus(state) { return state.appState.ledgerTransportStatus; } + +export function toggleCurrencySwitch(value) { + return { type: actionConstants.SET_CURRENCY_INPUT_SWITCH, value }; +} diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 09635ae04..bb2634288 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -264,6 +264,14 @@ export function getCollectiblesDetectionNoticeDismissed(state) { return state.metamask.collectiblesDetectionNoticeDismissed; } +export function getCollectiblesDropdownState(state) { + return state.metamask.collectiblesDropdownState; +} + +export function getEnableEIP1559V2NoticeDismissed(state) { + return state.metamask.enableEIP1559V2NoticeDismissed; +} + export const getCollectibles = (state) => { const { metamask: { diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index 9dde7b2a5..8df8cbc9d 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -53,11 +53,12 @@ import { hideLoadingIndication, showConfTxPage, showLoadingIndication, - updateTokenType, updateTransaction, addPollingTokenToAppState, removePollingTokenFromAppState, isCollectibleOwner, + getTokenStandardAndDetails, + showModal, } from '../../store/actions'; import { setCustomGasLimit } from '../gas/gas.duck'; import { @@ -91,9 +92,16 @@ import { isValidHexAddress, } from '../../../shared/modules/hexstring-utils'; import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network'; -import { ERC20, ETH, GWEI } from '../../helpers/constants/common'; +import { + ERC20, + ERC721, + ERC1155, + ETH, + GWEI, +} from '../../helpers/constants/common'; import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; +import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys'; // typedefs /** * @typedef {import('@reduxjs/toolkit').PayloadAction} PayloadAction @@ -361,9 +369,15 @@ export const computeEstimatedGasLimit = createAsyncThunk( async (_, thunkApi) => { const state = thunkApi.getState(); const { send, metamask } = state; + const unapprovedTxs = getUnapprovedTxs(state); + const transaction = unapprovedTxs[send.draftTransaction.id]; const isNonStandardEthChain = getIsNonStandardEthChain(state); const chainId = getCurrentChainId(state); - if (send.stage !== SEND_STAGES.EDIT) { + if ( + send.stage !== SEND_STAGES.EDIT || + !transaction.dappSuggestedGasFees?.gas || + !transaction.userEditedGasLimit + ) { const gasLimit = await estimateGasLimitForSend({ gasPrice: send.gas.gasPrice, blockGasLimit: metamask.currentBlockGasLimit, @@ -607,6 +621,8 @@ export const initialState = { // In the case of tokens, the address, decimals and symbol of the token // will be included in details details: null, + // error to display when there is an issue with the asset + error: null, }, draftTransaction: { // The metamask internal id of the transaction. Only populated in the EDIT @@ -736,6 +752,7 @@ const slice = createSlice({ 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.draftTransaction.id = action.payload.id; @@ -887,6 +904,7 @@ const slice = createSlice({ 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 @@ -1146,7 +1164,7 @@ const slice = createSlice({ }, validateSendState: (state) => { switch (true) { - // 1 + 2. State is invalid when either gas or amount fields have errors + // 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 @@ -1156,6 +1174,7 @@ const slice = createSlice({ // 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 state.stage === SEND_STAGES.ADD_RECIPIENT: @@ -1166,10 +1185,6 @@ const slice = createSlice({ ): state.status = SEND_STATUSES.INVALID; break; - case state.asset.type === ASSET_TYPES.TOKEN && - state.asset.details.isERC721 === true: - state.status = SEND_STATUSES.INVALID; - break; default: state.status = SEND_STATUSES.VALID; // Recompute the draftTransaction object @@ -1419,27 +1434,46 @@ export function updateSendAmount(amount) { export function updateSendAsset({ type, details }) { return async (dispatch, getState) => { const state = getState(); - let { balance } = state.send.asset; + let { balance, error } = state.send.asset; + const userAddress = state.send.account.address ?? getSelectedAddress(state); if (type === ASSET_TYPES.TOKEN) { - // 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. - await dispatch(showLoadingIndication()); - balance = await getERC20Balance( - details, - state.send.account.address ?? getSelectedAddress(state), - ); - // TODO remove along with migration of isERC721 tokens and stripping away this designation - if (details && details.isERC721 === undefined) { - const updatedAssetDetails = await updateTokenType(details.address); - details.isERC721 = updatedAssetDetails.isERC721; + if (details) { + if (details.standard === undefined) { + await dispatch(showLoadingIndication()); + const { standard } = await getTokenStandardAndDetails( + details.address, + userAddress, + ); + if ( + process.env.COLLECTIBLES_V1 && + (standard === ERC721 || standard === 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 === ERC20) { + error = null; + balance = await getERC20Balance(details, userAddress); + } + await dispatch(hideLoadingIndication()); } - details.standard = ERC20; - await dispatch(hideLoadingIndication()); } else if (type === ASSET_TYPES.COLLECTIBLE) { let isCurrentOwner = true; try { @@ -1448,16 +1482,30 @@ export function updateSendAsset({ type, details }) { details.address, details.tokenId, ); - } catch (error) { - if (error.message.includes('Unable to verify ownership.')) { + } 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(error.message)); + dispatch(displayWarning(err.message)); } } + + if (details.standard === undefined) { + const { standard } = await getTokenStandardAndDetails( + details.address, + userAddress, + ); + details.standard = standard; + } + + if (details.standard === ERC1155) { + throw new Error('Sends of ERC1155 tokens are not currently supported'); + } + if (isCurrentOwner) { + error = null; balance = '0x1'; } else { throw new Error( @@ -1465,12 +1513,13 @@ export function updateSendAsset({ type, details }) { ); } } 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 })); + await dispatch(actions.updateAsset({ type, details, balance, error })); await dispatch(computeEstimatedGasLimit()); }; } @@ -1632,15 +1681,18 @@ 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]; // We only update the tx params that can be changed via the edit flow UX const eip1559OnlyTxParamsToUpdate = { data: txParams.data, from: txParams.from, to: txParams.to, value: txParams.value, - gas: txParams.gas, + gas: unapprovedTx.userEditedGasLimit + ? unapprovedTx.txParams.gas + : txParams.gas, }; - const unapprovedTx = unapprovedTxs[id]; + unapprovedTx.originalGasEstimate = eip1559OnlyTxParamsToUpdate.gas; const editingTx = { ...unapprovedTx, txParams: Object.assign( @@ -1866,7 +1918,6 @@ export function getGasInputMode(state) { } // Asset Selectors - export function getSendAsset(state) { return state[name].asset; } @@ -1882,6 +1933,10 @@ export function getIsAssetSendable(state) { return state[name].asset.details.isERC721 === false; } +export function getAssetError(state) { + return state[name].asset.error; +} + // Amount Selectors export function getSendAmount(state) { return state[name].amount.value; diff --git a/ui/ducks/send/send.test.js b/ui/ducks/send/send.test.js index 1284ec11f..741b1370c 100644 --- a/ui/ducks/send/send.test.js +++ b/ui/ducks/send/send.test.js @@ -19,6 +19,7 @@ import { TRANSACTION_ENVELOPE_TYPES, TRANSACTION_TYPES, } from '../../../shared/constants/transaction'; +import * as Actions from '../../store/actions'; import sendReducer, { initialState, initializeSendState, @@ -67,17 +68,6 @@ import sendReducer, { const mockStore = createMockStore([thunk]); -jest.mock('../../store/actions', () => { - const actual = jest.requireActual('../../store/actions'); - return { - ...actual, - estimateGas: jest.fn(() => Promise.resolve('0x0')), - getGasFeeEstimatesAndStartPolling: jest.fn(() => Promise.resolve()), - updateTokenType: jest.fn(() => Promise.resolve({ isERC721: false })), - isCollectibleOwner: jest.fn(() => Promise.resolve(true)), - }; -}); - jest.mock('./send', () => { const actual = jest.requireActual('./send'); return { @@ -88,6 +78,25 @@ jest.mock('./send', () => { }); describe('Send Slice', () => { + let getTokenStandardAndDetailsStub; + beforeEach(() => { + getTokenStandardAndDetailsStub = jest + .spyOn(Actions, 'getTokenStandardAndDetails') + .mockImplementation(() => Promise.resolve({ standard: 'ERC20' })); + jest + .spyOn(Actions, 'estimateGas') + .mockImplementation(() => Promise.resolve('0x0')); + jest + .spyOn(Actions, 'getGasFeeEstimatesAndStartPolling') + .mockImplementation(() => Promise.resolve()); + jest + .spyOn(Actions, 'updateTokenType') + .mockImplementation(() => Promise.resolve({ isERC721: false })); + jest + .spyOn(Actions, 'isCollectibleOwner') + .mockImplementation(() => Promise.resolve(true)); + }); + describe('Reducers', () => { describe('updateSendAmount', () => { it('should', async () => { @@ -1294,10 +1303,7 @@ describe('Send Slice', () => { 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[2].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[3].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); @@ -1346,10 +1352,7 @@ describe('Send Slice', () => { 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[2].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[3].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); @@ -1389,16 +1392,13 @@ describe('Send Slice', () => { const actionResult = store.getActions(); - expect(actionResult).toHaveLength(4); + expect(actionResult).toHaveLength(3); expect(actionResult[0].type).toStrictEqual('send/updateSendAmount'); expect(actionResult[1].type).toStrictEqual( 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[2].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[3].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); }); @@ -1451,22 +1451,20 @@ describe('Send Slice', () => { const actionResult = store.getActions(); - expect(actionResult).toHaveLength(4); + expect(actionResult).toHaveLength(3); expect(actionResult[0].type).toStrictEqual('send/updateAsset'); expect(actionResult[0].payload).toStrictEqual({ ...newSendAsset, balance: '', + error: null, }); expect(actionResult[1].type).toStrictEqual( 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[2].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[3].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); @@ -1493,23 +1491,54 @@ describe('Send Slice', () => { const actionResult = store.getActions(); - expect(actionResult).toHaveLength(6); + expect(actionResult).toHaveLength(5); expect(actionResult[0].type).toStrictEqual('SHOW_LOADING_INDICATION'); expect(actionResult[1].type).toStrictEqual('HIDE_LOADING_INDICATION'); expect(actionResult[2].payload).toStrictEqual({ ...newSendAsset, balance: '0x0', + error: null, }); expect(actionResult[3].type).toStrictEqual( 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[4].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', + 'send/computeEstimatedGasLimit/rejected', ); - expect(actionResult[5].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + }); + + 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' }), ); + const store = mockStore(defaultSendAssetState); + + const newSendAsset = { + type: ASSET_TYPES.TOKEN, + details: { + address: 'tokenAddress', + symbol: 'tokenSymbol', + decimals: 'tokenDecimals', + }, + }; + + await expect(() => + store.dispatch(updateSendAsset(newSendAsset)), + ).rejects.toThrow('invalidAssetType'); + const actionResult = store.getActions(); + 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', + }, + type: 'UI_MODAL_OPEN', + }); + process.env.COLLECTIBLES_V1 = false; }); }); @@ -1660,16 +1689,13 @@ describe('Send Slice', () => { const actionResult = store.getActions(); - expect(actionResult).toHaveLength(4); + expect(actionResult).toHaveLength(3); expect(actionResult[0].type).toStrictEqual('send/updateRecipient'); expect(actionResult[1].type).toStrictEqual( 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[2].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[3].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); @@ -1728,7 +1754,7 @@ describe('Send Slice', () => { ); const actionResult = store.getActions(); - expect(actionResult).toHaveLength(4); + expect(actionResult).toHaveLength(3); expect(actionResult[0].type).toStrictEqual('send/updateRecipient'); expect(actionResult[0].payload.address).toStrictEqual( TEST_RECIPIENT_ADDRESS, @@ -1778,16 +1804,13 @@ describe('Send Slice', () => { const actionResult = store.getActions(); - expect(actionResult).toHaveLength(4); + expect(actionResult).toHaveLength(3); expect(actionResult[0].type).toStrictEqual('send/updateRecipient'); expect(actionResult[1].type).toStrictEqual( 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[2].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[3].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); }); @@ -1852,7 +1875,7 @@ describe('Send Slice', () => { await store.dispatch(resetRecipientInput()); const actionResult = store.getActions(); - expect(actionResult).toHaveLength(7); + expect(actionResult).toHaveLength(6); expect(actionResult[0].type).toStrictEqual( 'send/updateRecipientUserInput', ); @@ -1862,13 +1885,10 @@ describe('Send Slice', () => { 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[3].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[4].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); - expect(actionResult[5].type).toStrictEqual('ENS/resetEnsResolution'); - expect(actionResult[6].type).toStrictEqual( + expect(actionResult[4].type).toStrictEqual('ENS/resetEnsResolution'); + expect(actionResult[5].type).toStrictEqual( 'send/validateRecipientUserInput', ); }); @@ -1935,17 +1955,14 @@ describe('Send Slice', () => { const actionResult = store.getActions(); - expect(actionResult).toHaveLength(5); + expect(actionResult).toHaveLength(4); expect(actionResult[0].type).toStrictEqual('send/updateAmountMode'); expect(actionResult[1].type).toStrictEqual('send/updateAmountToMax'); expect(actionResult[2].type).toStrictEqual( 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[3].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[4].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); @@ -1982,17 +1999,14 @@ describe('Send Slice', () => { const actionResult = store.getActions(); - expect(actionResult).toHaveLength(5); + expect(actionResult).toHaveLength(4); expect(actionResult[0].type).toStrictEqual('send/updateAmountMode'); expect(actionResult[1].type).toStrictEqual('send/updateSendAmount'); expect(actionResult[2].type).toStrictEqual( 'send/computeEstimatedGasLimit/pending', ); expect(actionResult[3].type).toStrictEqual( - 'metamask/gas/SET_CUSTOM_GAS_LIMIT', - ); - expect(actionResult[4].type).toStrictEqual( - 'send/computeEstimatedGasLimit/fulfilled', + 'send/computeEstimatedGasLimit/rejected', ); }); }); @@ -2231,6 +2245,7 @@ describe('Send Slice', () => { expect(actionResult[0].payload).toStrictEqual({ balance: '0x1', type: ASSET_TYPES.COLLECTIBLE, + error: null, details: { address: '0xTokenAddress', description: 'A test NFT dispensed from faucet.paradigm.xyz.', @@ -2361,11 +2376,11 @@ describe('Send Slice', () => { expect(actionResult[2].payload).toStrictEqual({ balance: '0x0', type: ASSET_TYPES.TOKEN, + error: null, details: { address: '0xTokenAddress', decimals: 18, symbol: 'SYMB', - isERC721: false, standard: 'ERC20', }, }); diff --git a/ui/helpers/constants/common.js b/ui/helpers/constants/common.js index 96620f45b..b62e053f1 100644 --- a/ui/helpers/constants/common.js +++ b/ui/helpers/constants/common.js @@ -7,6 +7,7 @@ export const SECONDARY = 'SECONDARY'; export const ERC20 = 'ERC20'; export const ERC721 = 'ERC721'; +export const ERC1155 = 'ERC1155'; export const GAS_ESTIMATE_TYPES = { SLOW: 'SLOW', diff --git a/ui/helpers/constants/error-keys.js b/ui/helpers/constants/error-keys.js index 8372a3f1e..de43f9ffc 100644 --- a/ui/helpers/constants/error-keys.js +++ b/ui/helpers/constants/error-keys.js @@ -7,3 +7,4 @@ export const GAS_PRICE_FETCH_FAILURE_ERROR_KEY = 'gasPriceFetchFailed'; export const GAS_PRICE_EXCESSIVE_ERROR_KEY = 'gasPriceExcessive'; export const UNSENDABLE_ASSET_ERROR_KEY = 'unsendableAsset'; export const INSUFFICIENT_FUNDS_FOR_GAS_ERROR_KEY = 'insufficientFundsForGas'; +export const INVALID_ASSET_TYPE = 'invalidAssetType'; diff --git a/ui/helpers/utils/gas.js b/ui/helpers/utils/gas.js index 70856343a..fa4d34a4c 100644 --- a/ui/helpers/utils/gas.js +++ b/ui/helpers/utils/gas.js @@ -1,16 +1,17 @@ import BigNumber from 'bignumber.js'; import { addHexPrefix } from 'ethereumjs-util'; +import { GAS_RECOMMENDATIONS } from '../../../shared/constants/gas'; import { multiplyCurrencies } from '../../../shared/modules/conversion.utils'; import { bnGreaterThan } from './util'; import { hexWEIToDecGWEI } from './conversions.util'; export const gasEstimateGreaterThanGasUsedPlusTenPercent = ( - transaction, + gasUsed, gasFeeEstimates, estimate, ) => { - let { maxFeePerGas: maxFeePerGasInTransaction } = transaction.txParams; + let { maxFeePerGas: maxFeePerGasInTransaction } = gasUsed; maxFeePerGasInTransaction = new BigNumber( hexWEIToDecGWEI(addTenPercentAndRound(maxFeePerGasInTransaction)), ); @@ -53,3 +54,11 @@ export function addTenPercent(hexStringValue, conversionOptions = {}) { export function addTenPercentAndRound(hexStringValue) { return addTenPercent(hexStringValue, { numberOfDecimals: 0 }); } + +export function isVolatileGasEstimate(estimate) { + return [ + GAS_RECOMMENDATIONS.HIGH, + GAS_RECOMMENDATIONS.MEDIUM, + GAS_RECOMMENDATIONS.LOW, + ].includes(estimate); +} diff --git a/ui/helpers/utils/gas.test.js b/ui/helpers/utils/gas.test.js index 2d13f4030..58aaaf38e 100644 --- a/ui/helpers/utils/gas.test.js +++ b/ui/helpers/utils/gas.test.js @@ -10,10 +10,8 @@ describe('Gas utils', () => { const compareGas = (estimateValues) => { return gasEstimateGreaterThanGasUsedPlusTenPercent( { - txParams: { - maxFeePerGas: '0x59682f10', - maxPriorityFeePerGas: '0x59682f00', - }, + maxFeePerGas: '0x59682f10', + maxPriorityFeePerGas: '0x59682f00', }, { medium: estimateValues, diff --git a/ui/helpers/utils/util.js b/ui/helpers/utils/util.js index e35826881..ae2568fbb 100644 --- a/ui/helpers/utils/util.js +++ b/ui/helpers/utils/util.js @@ -573,7 +573,10 @@ export function roundToDecimalPlacesRemovingExtraZeroes( numberish, numberOfDecimalPlaces, ) { - return toBigNumber.dec( - toBigNumber.dec(numberish).toFixed(numberOfDecimalPlaces), - ); + if (numberish === undefined || numberish === null) { + return ''; + } + return toBigNumber + .dec(toBigNumber.dec(numberish).toFixed(numberOfDecimalPlaces)) + .toNumber(); } diff --git a/ui/hooks/gasFeeInput/test-utils.js b/ui/hooks/gasFeeInput/test-utils.js index 117d4f7cc..73693c2a4 100644 --- a/ui/hooks/gasFeeInput/test-utils.js +++ b/ui/hooks/gasFeeInput/test-utils.js @@ -12,10 +12,13 @@ import { import { checkNetworkAndAccountSupports1559, getCurrentCurrency, + getEIP1559V2Enabled, getSelectedAccount, getShouldShowFiat, getPreferences, txDataSelector, + getCurrentKeyring, + getTokenExchangeRates, } from '../../selectors'; import { ETH } from '../../helpers/constants/common'; @@ -96,6 +99,7 @@ export const HIGH_FEE_MARKET_ESTIMATE_RETURN_VALUE = { export const generateUseSelectorRouter = ({ checkNetworkAndAccountSupports1559Response, shouldShowFiat = true, + eip1559V2Enabled = false, } = {}) => (selector) => { if (selector === getConversionRate) { return MOCK_ETH_USD_CONVERSION_RATE; @@ -135,6 +139,15 @@ export const generateUseSelectorRouter = ({ if (selector === checkNetworkAndAccountSupports1559) { return checkNetworkAndAccountSupports1559Response; } + if (selector === getEIP1559V2Enabled) { + return eip1559V2Enabled; + } + if (selector === getCurrentKeyring) { + return { type: '' }; + } + if (selector === getTokenExchangeRates) { + return { '0x1': '1' }; + } return undefined; }; diff --git a/ui/hooks/gasFeeInput/useGasFeeInputs.js b/ui/hooks/gasFeeInput/useGasFeeInputs.js index 63b5b3fe6..63f54753a 100644 --- a/ui/hooks/gasFeeInput/useGasFeeInputs.js +++ b/ui/hooks/gasFeeInput/useGasFeeInputs.js @@ -11,6 +11,7 @@ import { GAS_FORM_ERRORS } from '../../helpers/constants/gas'; import { checkNetworkAndAccountSupports1559, getAdvancedInlineGasShown, + getEIP1559V2Enabled, } from '../../selectors'; import { hexToDecimal } from '../../helpers/utils/conversions.util'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; @@ -93,15 +94,13 @@ export function useGasFeeInputs( minimumGasLimit = '0x5208', editGasMode = EDIT_GAS_MODES.MODIFY_IN_PLACE, ) { - const EIP_1559_V2_ENABLED = - // This is a string in unit tests but is a boolean in the browser - process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true'; + const eip1559V2Enabled = useSelector(getEIP1559V2Enabled); const supportsEIP1559 = useSelector(checkNetworkAndAccountSupports1559) && !isLegacyTransaction(transaction?.txParams); - const supportsEIP1559V2 = supportsEIP1559 && EIP_1559_V2_ENABLED; + const supportsEIP1559V2 = supportsEIP1559 && eip1559V2Enabled; // We need the gas estimates from the GasFeeController in the background. // Calling this hooks initiates polling for new gas estimates and returns the @@ -141,6 +140,12 @@ export function useGasFeeInputs( Number(hexToDecimal(transaction?.txParams?.gas ?? '0x0')), ); + const properGasLimit = Number(hexToDecimal(transaction?.originalGasEstimate)); + + const [userEditedGasLimit, setUserEditedGasLimit] = useState(() => + Boolean(transaction?.userEditedGasLimit), + ); + /** * In EIP-1559 V2 designs change to gas estimate is always updated to transaction * Thus callback setEstimateToUse can be deprecate in favour of this useEffect @@ -299,6 +304,7 @@ export function useGasFeeInputs( // Restore existing values setGasPrice(gasPrice); setGasLimit(gasLimit); + setUserEditedGasLimit(true); setMaxFeePerGas(maxFeePerGas); setMaxPriorityFeePerGas(maxPriorityFeePerGas); setGasPriceHasBeenManuallySet(true); @@ -310,6 +316,7 @@ export function useGasFeeInputs( gasPrice, setGasLimit, gasLimit, + setUserEditedGasLimit, setMaxFeePerGas, maxFeePerGas, setMaxPriorityFeePerGas, @@ -329,6 +336,8 @@ export function useGasFeeInputs( setGasPrice, gasLimit, setGasLimit, + properGasLimit, + userEditedGasLimit, editGasMode, estimateToUse, setEstimateToUse, diff --git a/ui/hooks/gasFeeInput/useGasFeeInputs.test.js b/ui/hooks/gasFeeInput/useGasFeeInputs.test.js index 8b3f1e9e3..c3c722c18 100644 --- a/ui/hooks/gasFeeInput/useGasFeeInputs.test.js +++ b/ui/hooks/gasFeeInput/useGasFeeInputs.test.js @@ -324,13 +324,9 @@ describe('useGasFeeInputs', () => { useSelector.mockImplementation( generateUseSelectorRouter({ checkNetworkAndAccountSupports1559Response: true, + eip1559V2Enabled: true, }), ); - process.env.EIP_1559_V2 = true; - }); - - afterEach(() => { - process.env.EIP_1559_V2 = false; }); it('return true for fee_market transaction type', () => { diff --git a/ui/hooks/useEventFragment.js b/ui/hooks/useEventFragment.js new file mode 100644 index 000000000..abdbb4244 --- /dev/null +++ b/ui/hooks/useEventFragment.js @@ -0,0 +1,100 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { getEnvironmentType } from '../../app/scripts/lib/util'; +import { selectMatchingFragment } from '../selectors'; +import { + finalizeEventFragment, + createEventFragment, + updateEventFragment, +} from '../store/actions'; +import { useMetaMetricsContext } from './useMetricEvent'; + +/** + * Retrieves a fragment from memory or initializes new fragment if one does not + * exist. Returns three methods that are tied to the fragment, as well as the + * fragment id. + * + * @param {string} existingId + * @param {Object} fragmentOptions + * @returns + */ +export function useEventFragment(existingId, fragmentOptions) { + // To prevent overcalling the createEventFragment background method a ref + // is used to store a boolean value of whether we have already called the + // method. + const createEventFragmentCalled = useRef(false); + + // In order to immediately return a created fragment, instead of waiting for + // background state to update and find the newly created fragment, we have a + // state element that is updated with the fragmentId returned from the + // call into the background process. + const [createdFragmentId, setCreatedFragmentId] = useState(undefined); + + // Select a matching fragment from state if one exists that matches the + // criteria. If an existingId is passed in it is preferred, if not and the + // fragmentOptions has the persist key set to true, a fragment with matching + // successEvent will be pulled from memory if it exists. + const fragment = useSelector((state) => + selectMatchingFragment(state, { + fragmentOptions, + existingId: existingId ?? createdFragmentId, + }), + ); + + // If no valid existing fragment can be found, a new one must be created that + // will then be found by the selector above. To do this, invoke the + // createEventFragment method with the fragmentOptions and current sessionId. + // As soon as we call the background method we also update the + // createEventFragmentCalled ref's current value to true so that future calls + // are suppressed. + useEffect(() => { + if (fragment === undefined && createEventFragmentCalled.current === false) { + createEventFragmentCalled.current = true; + createEventFragment({ + ...fragmentOptions, + environmentType: getEnvironmentType(), + }).then((createdFragment) => { + setCreatedFragmentId(createdFragment.id); + }); + } + }, [fragment, fragmentOptions]); + + const context = useMetaMetricsContext(); + + /** + * trackSuccess is used to close a fragment with the affirmative action. This + * method is just a thin wrapper around the background method that sets the + * necessary values. + */ + const trackSuccess = useCallback(() => { + finalizeEventFragment(fragment.id, { context }); + }, [fragment, context]); + + /** + * trackFailure is used to close a fragment as abandoned. This method is just a + * thin wrapper around the background method that sets the necessary values. + */ + const trackFailure = useCallback(() => { + finalizeEventFragment(fragment.id, { abandoned: true, context }); + }, [fragment, context]); + + /** + * updateEventFragmentProperties is a thin wrapper around updateEventFragment + * that supplies the fragment id as the first parameter. This function will + * be passed back from the hook as 'updateEventFragment', but is named + * updateEventFragmentProperties to avoid naming conflicts. + */ + const updateEventFragmentProperties = useCallback( + (payload) => { + updateEventFragment(fragment.id, payload); + }, + [fragment], + ); + + return { + trackSuccess, + trackFailure, + updateEventFragment: updateEventFragmentProperties, + fragment, + }; +} diff --git a/ui/hooks/useEventFragment.test.js b/ui/hooks/useEventFragment.test.js new file mode 100644 index 000000000..f40febd82 --- /dev/null +++ b/ui/hooks/useEventFragment.test.js @@ -0,0 +1,211 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useSelector } from 'react-redux'; +import { + finalizeEventFragment, + createEventFragment, + updateEventFragment, +} from '../store/actions'; +import { useEventFragment } from './useEventFragment'; + +jest.mock('../store/actions', () => ({ + finalizeEventFragment: jest.fn(), + updateEventFragment: jest.fn(), + createEventFragment: jest.fn(), +})); + +jest.mock('./useMetricEvent', () => ({ + useMetaMetricsContext: jest.fn(() => ({ page: '/' })), +})); + +jest.mock('react-redux', () => ({ + useSelector: jest.fn(), +})); + +describe('useEventFragment', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('return shape', () => { + let value; + beforeAll(async () => { + useSelector.mockImplementation((selector) => + selector({ metamask: { fragments: { testid: { id: 'testid' } } } }), + ); + createEventFragment.mockImplementation(() => + Promise.resolve({ + id: 'testid', + }), + ); + const { result, waitForNextUpdate } = renderHook(() => + useEventFragment(undefined, { + successEvent: 'success', + failureEvent: 'failure', + persist: true, + }), + ); + await waitForNextUpdate(); + value = result.current; + }); + + it('should have trackSuccess method', () => { + expect(value).toHaveProperty('trackSuccess'); + expect(typeof value.trackSuccess).toBe('function'); + }); + + it('should have trackFailure method', () => { + expect(value).toHaveProperty('trackFailure'); + expect(typeof value.trackFailure).toBe('function'); + }); + + it('should have updateEventFragment method', () => { + expect(value).toHaveProperty('updateEventFragment'); + expect(typeof value.updateEventFragment).toBe('function'); + }); + + it('should have fragment property', () => { + expect(value).toHaveProperty('fragment'); + expect(value.fragment).toMatchObject({ + id: 'testid', + }); + }); + }); + + describe('identifying appropriate fragment', () => { + it('should create a new fragment when a matching fragment does not exist', async () => { + useSelector.mockImplementation((selector) => + selector({ + metamask: { + fragments: { + testid: { + id: 'testid', + successEvent: 'success', + failureEvent: 'failure', + }, + }, + }, + }), + ); + createEventFragment.mockImplementation(() => + Promise.resolve({ + id: 'testid', + }), + ); + const { result, waitForNextUpdate } = renderHook(() => + useEventFragment(undefined, { + successEvent: 'success', + failureEvent: 'failure', + }), + ); + await waitForNextUpdate(); + expect(createEventFragment).toHaveBeenCalledTimes(1); + const returnValue = result.current; + expect(returnValue.fragment).toMatchObject({ + id: 'testid', + successEvent: 'success', + failureEvent: 'failure', + }); + }); + + it('should return the matching fragment by id when existingId is provided', async () => { + useSelector.mockImplementation((selector) => + selector({ + metamask: { + fragments: { + testid: { + id: 'testid', + successEvent: 'success', + failureEvent: 'failure', + }, + }, + }, + }), + ); + const { result } = renderHook(() => + useEventFragment('testid', { + successEvent: 'success', + failureEvent: 'failure', + }), + ); + const returnValue = result.current; + expect(returnValue.fragment).toMatchObject({ + id: 'testid', + successEvent: 'success', + failureEvent: 'failure', + }); + }); + + it('should return matching fragment by successEvent when no id is provided, but persist is true', async () => { + useSelector.mockImplementation((selector) => + selector({ + metamask: { + fragments: { + testid: { + persist: true, + id: 'testid', + successEvent: 'track new event', + }, + }, + }, + }), + ); + const { result } = renderHook(() => + useEventFragment(undefined, { + successEvent: 'track new event', + persist: true, + }), + ); + const returnValue = result.current; + expect(returnValue.fragment).toMatchObject({ + id: 'testid', + persist: true, + successEvent: 'track new event', + }); + }); + }); + + describe('methods', () => { + let value; + beforeAll(async () => { + useSelector.mockImplementation((selector) => + selector({ metamask: { fragments: { testid: { id: 'testid' } } } }), + ); + createEventFragment.mockImplementation(() => + Promise.resolve({ + id: 'testid', + }), + ); + const { result, waitForNextUpdate } = renderHook(() => + useEventFragment(undefined, { + successEvent: 'success', + failureEvent: 'failure', + persist: true, + }), + ); + await waitForNextUpdate(); + value = result.current; + }); + + it('trackSuccess method should invoke the background finalizeEventFragment method', () => { + value.trackSuccess(); + expect(finalizeEventFragment).toHaveBeenCalledWith('testid', { + context: { page: '/' }, + }); + }); + + it('trackFailure method should invoke the background finalizeEventFragment method', () => { + value.trackFailure(); + expect(finalizeEventFragment).toHaveBeenCalledWith('testid', { + abandoned: true, + context: { page: '/' }, + }); + }); + + it('updateEventFragment method should invoke the background updateEventFragment method', () => { + value.updateEventFragment({ properties: { count: 1 } }); + expect(updateEventFragment).toHaveBeenCalledWith('testid', { + properties: { count: 1 }, + }); + }); + }); +}); diff --git a/ui/hooks/useMetricEvent.js b/ui/hooks/useMetricEvent.js index 4e9fb8323..f6e44574a 100644 --- a/ui/hooks/useMetricEvent.js +++ b/ui/hooks/useMetricEvent.js @@ -1,6 +1,10 @@ import { useContext, useCallback } from 'react'; +import { useSelector } from 'react-redux'; +import { useRouteMatch } from 'react-router-dom'; import { MetaMetricsContext } from '../contexts/metametrics'; import { MetaMetricsContext as NewMetaMetricsContext } from '../contexts/metametrics.new'; +import { PATH_NAME_MAP } from '../helpers/constants/routes'; +import { txDataSelector } from '../selectors'; import { useEqualityCheck } from './useEqualityCheck'; // Type imports @@ -38,3 +42,45 @@ export function useNewMetricEvent(payload, options) { memoizedOptions, ]); } + +const PATHS_TO_CHECK = Object.keys(PATH_NAME_MAP); + +/** + * Returns the current page if it matches our route map, as well as the origin + * if there is a confirmation that was triggered by a dapp. These values are + * not required but add valuable context to events, and should be included in + * the context object on the event payload. + * + * @returns {{ + * page?: MetaMetricsPageObject + * referrer?: MetaMetricsReferrerObject + * }} + */ +export function useMetaMetricsContext() { + const match = useRouteMatch({ + path: PATHS_TO_CHECK, + exact: true, + strict: true, + }); + const txData = useSelector(txDataSelector) || {}; + const confirmTransactionOrigin = txData.origin; + + const referrer = confirmTransactionOrigin + ? { + url: confirmTransactionOrigin, + } + : undefined; + + const page = match + ? { + path: match.path, + title: PATH_NAME_MAP[match.path], + url: match.path, + } + : undefined; + + return { + page, + referrer, + }; +} diff --git a/ui/hooks/useTokenTracker.js b/ui/hooks/useTokenTracker.js index 01b56f615..29772a1de 100644 --- a/ui/hooks/useTokenTracker.js +++ b/ui/hooks/useTokenTracker.js @@ -67,6 +67,7 @@ export function useTokenTracker( tokens: tokenList, includeFailedTokens, pollingInterval: SECOND * 8, + balanceDecimals: 5, }); tokenTracker.current.on('update', updateBalances); diff --git a/ui/hooks/useTokensToSearch.js b/ui/hooks/useTokensToSearch.js index 7140737a4..ac06d84ef 100644 --- a/ui/hooks/useTokensToSearch.js +++ b/ui/hooks/useTokensToSearch.js @@ -175,8 +175,8 @@ export function useTokensToSearch({ ); tokensToSearchBuckets.top = tokensToSearchBuckets.top.filter(Boolean); return [ - ...tokensToSearchBuckets.owned, ...tokensToSearchBuckets.top, + ...tokensToSearchBuckets.owned, ...tokensToSearchBuckets.others, ]; }, [ diff --git a/ui/pages/add-collectible/add-collectible.js b/ui/pages/add-collectible/add-collectible.js index 71e4002d7..188756aa1 100644 --- a/ui/pages/add-collectible/add-collectible.js +++ b/ui/pages/add-collectible/add-collectible.js @@ -1,36 +1,60 @@ import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { util } from '@metamask/controllers'; import { useI18nContext } from '../../hooks/useI18nContext'; import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; import Box from '../../components/ui/box'; -import TextField from '../../components/ui/text-field'; import PageContainer from '../../components/ui/page-container'; import { addCollectibleVerifyOwnership, + removeToken, setNewCollectibleAddedMessage, } from '../../store/actions'; +import FormField from '../../components/ui/form-field'; +import { getIsMainnet, getUseCollectibleDetection } from '../../selectors'; +import { getCollectiblesDetectionNoticeDismissed } from '../../ducks/metamask/metamask'; +import CollectiblesDetectionNotice from '../../components/app/collectibles-detection-notice'; export default function AddCollectible() { const t = useI18nContext(); const history = useHistory(); const dispatch = useDispatch(); + const useCollectibleDetection = useSelector(getUseCollectibleDetection); + const isMainnet = useSelector(getIsMainnet); + const collectibleDetectionNoticeDismissed = useSelector( + getCollectiblesDetectionNoticeDismissed, + ); + const addressEnteredOnImportTokensPage = + history?.location?.state?.addressEnteredOnImportTokensPage; + const contractAddressToConvertFromTokenToCollectible = + history?.location?.state?.tokenAddress; - const [address, setAddress] = useState(''); + const [address, setAddress] = useState( + addressEnteredOnImportTokensPage ?? + contractAddressToConvertFromTokenToCollectible ?? + '', + ); const [tokenId, setTokenId] = useState(''); const [disabled, setDisabled] = useState(true); const handleAddCollectible = async () => { try { - await dispatch(addCollectibleVerifyOwnership(address, tokenId)); + await dispatch( + addCollectibleVerifyOwnership(address, tokenId.toString()), + ); } catch (error) { const { message } = error; dispatch(setNewCollectibleAddedMessage(message)); history.push(DEFAULT_ROUTE); return; } + if (contractAddressToConvertFromTokenToCollectible) { + await dispatch( + removeToken(contractAddressToConvertFromTokenToCollectible), + ); + } dispatch(setNewCollectibleAddedMessage('success')); history.push(DEFAULT_ROUTE); }; @@ -47,7 +71,7 @@ export default function AddCollectible() { return ( { handleAddCollectible(); }} @@ -61,29 +85,33 @@ export default function AddCollectible() { disabled={disabled} contentComponent={ + {isMainnet && + !useCollectibleDetection && + !collectibleDetectionNoticeDismissed ? ( + + ) : null} - validateAndSetAddress(e.target.value)} - fullWidth + onChange={(val) => validateAndSetAddress(val)} + tooltipText={t('importNFTAddressToolTip')} autoFocus - margin="normal" /> - validateAndSetTokenId(e.target.value)} - fullWidth - margin="normal" + onChange={(val) => { + validateAndSetTokenId(val); + }} + tooltipText={t('importNFTTokenIdToolTip')} + numeric /> diff --git a/ui/pages/asset/asset.js b/ui/pages/asset/asset.js index f48fd9bc4..42c710fd7 100644 --- a/ui/pages/asset/asset.js +++ b/ui/pages/asset/asset.js @@ -21,7 +21,7 @@ const Asset = () => { const collectible = collectibles.find( ({ address, tokenId }) => - isEqualCaseInsensitive(address, asset) && id === tokenId, + isEqualCaseInsensitive(address, asset) && id === tokenId.toString(), ); useEffect(() => { diff --git a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js index fdb3a84ab..60656b753 100644 --- a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js +++ b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.component.js @@ -1,192 +1,49 @@ -import React, { Component } from 'react'; +import React, { useContext, useEffect } from 'react'; import PropTypes from 'prop-types'; import Button from '../../components/ui/button'; import Identicon from '../../components/ui/identicon'; import TokenBalance from '../../components/ui/token-balance'; +import { I18nContext } from '../../contexts/i18n'; +import { MetaMetricsContext } from '../../contexts/metametrics'; import { isEqualCaseInsensitive } from '../../helpers/utils/util'; -export default class ConfirmAddSuggestedToken extends Component { - static contextTypes = { - t: PropTypes.func, - trackEvent: PropTypes.func, - }; - - static propTypes = { - history: PropTypes.object, - acceptWatchAsset: PropTypes.func, - rejectWatchAsset: PropTypes.func, - mostRecentOverviewPage: PropTypes.string.isRequired, - suggestedAssets: PropTypes.array, - tokens: PropTypes.array, - }; - - componentDidMount() { - this._checksuggestedAssets(); - } - - componentDidUpdate() { - this._checksuggestedAssets(); - } - - _checksuggestedAssets() { - const { - mostRecentOverviewPage, - suggestedAssets = [], - history, - } = this.props; - - if (suggestedAssets.length > 0) { - return; - } - - history.push(mostRecentOverviewPage); - } - - getTokenName(name, symbol) { - return typeof name === 'undefined' ? symbol : `${name} (${symbol})`; - } - - render() { - const { - suggestedAssets, - tokens, - rejectWatchAsset, - history, - mostRecentOverviewPage, - acceptWatchAsset, - } = this.props; - - const hasTokenDuplicates = this.checkTokenDuplicates( - suggestedAssets, - tokens, - ); - const reusesName = this.checkNameReuse(suggestedAssets, tokens); +function getTokenName(name, symbol) { + return typeof name === 'undefined' ? symbol : `${name} (${symbol})`; +} +const ConfirmAddSuggestedToken = (props) => { + const { + acceptWatchAsset, + history, + mostRecentOverviewPage, + rejectWatchAsset, + suggestedAssets, + tokens, + } = props; - return ( -
-
-
- {this.context.t('addSuggestedTokens')} -
-
- {this.context.t('likeToImportTokens')} -
- {hasTokenDuplicates ? ( -
{this.context.t('knownTokenWarning')}
- ) : null} - {reusesName ? ( -
- {this.context.t('reusedTokenNameWarning')} -
- ) : null} -
-
-
-
-
- {this.context.t('token')} -
-
- {this.context.t('balance')} -
-
-
- {suggestedAssets.map(({ asset }) => { - return ( -
-
- -
- {this.getTokenName(asset.name, asset.symbol)} -
-
-
- -
-
- ); - })} -
-
-
-
-
- - -
-
-
- ); - } + const metricsEvent = useContext(MetaMetricsContext); + const t = useContext(I18nContext); - checkTokenDuplicates(suggestedAssets, tokens) { - const pending = suggestedAssets.map(({ asset }) => - asset.address.toUpperCase(), - ); - const existing = tokens.map((token) => token.address.toUpperCase()); - const dupes = pending.filter((proposed) => { - return existing.includes(proposed); + const tokenAddedEvent = (asset) => { + metricsEvent({ + event: 'Token Added', + category: 'Wallet', + sensitiveProperties: { + token_symbol: asset.symbol, + token_contract_address: asset.address, + token_decimal_precision: asset.decimals, + unlisted: asset.unlisted, + source: 'dapp', + }, }); - - return dupes.length > 0; - } + }; /** * Returns true if any suggestedAssets both: * - Share a symbol with an existing `tokens` member. * - Does not share an address with that same `tokens` member. * This should be flagged as possibly deceptive or confusing. - * - * @param suggestedAssets - * @param tokens */ - checkNameReuse(suggestedAssets, tokens) { + const checkNameReuse = () => { const duplicates = suggestedAssets.filter(({ asset }) => { const dupes = tokens.filter( (old) => @@ -196,5 +53,121 @@ export default class ConfirmAddSuggestedToken extends Component { return dupes.length > 0; }); return duplicates.length > 0; - } -} + }; + + const checkTokenDuplicates = () => { + const pending = suggestedAssets.map(({ asset }) => + asset.address.toUpperCase(), + ); + const existing = tokens.map((token) => token.address.toUpperCase()); + const dupes = pending.filter((proposed) => { + return existing.includes(proposed); + }); + + return dupes.length > 0; + }; + + const hasTokenDuplicates = checkTokenDuplicates(); + const reusesName = checkNameReuse(); + + useEffect(() => { + if (!suggestedAssets.length) { + history.push(mostRecentOverviewPage); + } + }, [history, suggestedAssets, mostRecentOverviewPage]); + + return ( +
+
+
{t('addSuggestedTokens')}
+
+ {t('likeToImportTokens')} +
+ {hasTokenDuplicates ? ( +
{t('knownTokenWarning')}
+ ) : null} + {reusesName ? ( +
{t('reusedTokenNameWarning')}
+ ) : null} +
+
+
+
+
{t('token')}
+
{t('balance')}
+
+
+ {suggestedAssets.map(({ asset }) => { + return ( +
+
+ +
+ {getTokenName(asset.name, asset.symbol)} +
+
+
+ +
+
+ ); + })} +
+
+
+
+
+ + +
+
+
+ ); +}; + +ConfirmAddSuggestedToken.propTypes = { + acceptWatchAsset: PropTypes.func, + history: PropTypes.object, + mostRecentOverviewPage: PropTypes.string.isRequired, + rejectWatchAsset: PropTypes.func, + suggestedAssets: PropTypes.array, + tokens: PropTypes.array, +}; + +export default ConfirmAddSuggestedToken; diff --git a/ui/pages/confirm-approve/confirm-approve.js b/ui/pages/confirm-approve/confirm-approve.js index 20ad652d2..b8b5bb287 100644 --- a/ui/pages/confirm-approve/confirm-approve.js +++ b/ui/pages/confirm-approve/confirm-approve.js @@ -35,6 +35,7 @@ import { getRpcPrefsForCurrentProvider, getIsMultiLayerFeeNetwork, checkNetworkAndAccountSupports1559, + getEIP1559V2Enabled, } from '../../selectors'; import { useApproveTransaction } from '../../hooks/useApproveTransaction'; import { currentNetworkTxListSelector } from '../../selectors/transactions'; @@ -50,10 +51,6 @@ const isAddressLedgerByFromAddress = (address) => (state) => { return isAddressLedger(state, address); }; -// eslint-disable-next-line prefer-destructuring -const EIP_1559_V2_ENABLED = - process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true'; - export default function ConfirmApprove() { const dispatch = useDispatch(); const { id: paramsTransactionId } = useParams(); @@ -89,8 +86,8 @@ export default function ConfirmApprove() { hexTransactionTotal, } = useSelector((state) => transactionFeeSelector(state, transaction)); - const supportsEIP1559V2 = - EIP_1559_V2_ENABLED && networkAndAccountSupports1559; + const eip1559V2Enabled = useSelector(getEIP1559V2Enabled); + const supportsEIP1559V2 = eip1559V2Enabled && networkAndAccountSupports1559; const currentToken = (tokens && tokens.find(({ address }) => 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 5b3b2ba4a..d9e4769d3 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -63,10 +63,6 @@ import { MIN_GAS_LIMIT_DEC } from '../send/send.constants'; import TransactionAlerts from './transaction-alerts'; -// eslint-disable-next-line prefer-destructuring -const EIP_1559_V2_ENABLED = - process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true'; - const renderHeartBeatIfNotInTest = () => process.env.IN_TEST ? null : ; @@ -147,6 +143,7 @@ export default class ConfirmTransactionBase extends Component { supportsEIP1559: PropTypes.bool, hardwareWalletRequiresConnection: PropTypes.bool, isMultiLayerFeeNetwork: PropTypes.bool, + eip1559V2Enabled: PropTypes.bool, }; state = { @@ -368,7 +365,7 @@ export default class ConfirmTransactionBase extends Component { ) { return (
- + - + - + {t('editGasSubTextAmountLabel')} {' '} @@ -974,7 +973,7 @@ export default class ConfirmTransactionBase extends Component { } supportsEIP1559V2 = - EIP_1559_V2_ENABLED && + this.props.eip1559V2Enabled && this.props.supportsEIP1559 && !isLegacyTransaction(this.props.txData); 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 03ddd85f1..8b13a1034 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -36,6 +36,7 @@ import { getUseTokenDetection, getTokenList, getIsMultiLayerFeeNetwork, + getEIP1559V2Enabled, } from '../../selectors'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { @@ -197,6 +198,7 @@ const mapStateToProps = (state, ownProps) => { ); const isMultiLayerFeeNetwork = getIsMultiLayerFeeNetwork(state); + const eip1559V2Enabled = getEIP1559V2Enabled(state); return { balance, @@ -248,6 +250,7 @@ const mapStateToProps = (state, ownProps) => { hardwareWalletRequiresConnection, isMultiLayerFeeNetwork, chainId, + eip1559V2Enabled, }; }; diff --git a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js index c998daa86..ae6b195ad 100644 --- a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js +++ b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js @@ -93,6 +93,7 @@ const TransactionAlerts = ({ {balanceError && } {estimateUsed === PRIORITY_LEVELS.LOW && ( } + message={ + + + + } iconFillColor="#f8c000" type="warning" + useIcon /> ) : null}
diff --git a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.test.js b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.test.js index 038e9c3d3..5bb113556 100644 --- a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.test.js +++ b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.test.js @@ -174,9 +174,7 @@ describe('TransactionAlerts', () => { }, }); expect( - getByText( - 'Future transactions will queue after this one. This price was last seen was some time ago.', - ), + getByText('Future transactions will queue after this one.'), ).toBeInTheDocument(); }); }); @@ -190,9 +188,7 @@ describe('TransactionAlerts', () => { }, }); expect( - queryByText( - 'Future transactions will queue after this one. This price was last seen was some time ago.', - ), + queryByText('Future transactions will queue after this one.'), ).not.toBeInTheDocument(); }); }); diff --git a/ui/pages/create-account/import-account/index.scss b/ui/pages/create-account/import-account/index.scss index 54fe8a277..7633fab7a 100644 --- a/ui/pages/create-account/import-account/index.scss +++ b/ui/pages/create-account/import-account/index.scss @@ -56,6 +56,10 @@ margin-top: 34px; } + &__help-link { + color: var(--primary-blue); + } + &__input-password { @include Paragraph; diff --git a/ui/pages/create-account/import-account/json.js b/ui/pages/create-account/import-account/json.js index bd14d6a2e..fbb237c4a 100644 --- a/ui/pages/create-account/import-account/json.js +++ b/ui/pages/create-account/import-account/json.js @@ -28,7 +28,7 @@ class JsonImportSubview extends Component {

{this.context.t('usedByClients')}

+ this.props.history.push({ + pathname: ADD_COLLECTIBLE_ROUTE, + state: { + addressEnteredOnImportTokensPage: this.state.customAddress, + }, + }) + } + key="collectibleAddressError" + > + {this.context.t('importNFTPage')} + , + ]), + }); + break; case isMainnetToken && !isMainnetNetwork: this.setState({ @@ -242,7 +286,7 @@ class ImportToken extends Component { break; default: - if (customAddress !== emptyAddr) { + if (!addressIsEmpty) { this.attemptToAutoFillTokenParams(customAddress); } } @@ -290,6 +334,7 @@ class ImportToken extends Component { symbolAutoFilled, decimalAutoFilled, mainnetTokenWarning, + collectibleAddressError, } = this.state; const { chainId, rpcPrefs } = this.props; @@ -330,7 +375,9 @@ class ImportToken extends Component { type="text" value={customAddress} onChange={(e) => this.handleCustomAddressChange(e.target.value)} - error={customAddressError || mainnetTokenWarning} + error={ + customAddressError || mainnetTokenWarning || collectibleAddressError + } fullWidth autoFocus margin="normal" diff --git a/ui/pages/import-token/import-token.container.js b/ui/pages/import-token/import-token.container.js index f6fa265f1..ae3d97228 100644 --- a/ui/pages/import-token/import-token.container.js +++ b/ui/pages/import-token/import-token.container.js @@ -1,6 +1,10 @@ import { connect } from 'react-redux'; -import { setPendingTokens, clearPendingTokens } from '../../store/actions'; +import { + setPendingTokens, + clearPendingTokens, + getTokenStandardAndDetails, +} from '../../store/actions'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { getRpcPrefsForCurrentProvider, @@ -17,6 +21,7 @@ const mapStateToProps = (state) => { provider: { chainId }, useTokenDetection, tokenList, + selectedAddress, }, } = state; const showSearchTabCustomNetwork = @@ -33,13 +38,15 @@ const mapStateToProps = (state) => { rpcPrefs: getRpcPrefsForCurrentProvider(state), tokenList, useTokenDetection, + selectedAddress, }; }; - const mapDispatchToProps = (dispatch) => { return { setPendingTokens: (tokens) => dispatch(setPendingTokens(tokens)), clearPendingTokens: () => dispatch(clearPendingTokens()), + getTokenStandardAndDetails: (address, selectedAddress) => + getTokenStandardAndDetails(address, selectedAddress, null), }; }; diff --git a/ui/pages/import-token/import-token.test.js b/ui/pages/import-token/import-token.test.js index c6be1f1bb..c03a9c486 100644 --- a/ui/pages/import-token/import-token.test.js +++ b/ui/pages/import-token/import-token.test.js @@ -1,110 +1,177 @@ import React from 'react'; -import { Provider } from 'react-redux'; -import sinon from 'sinon'; -import configureMockStore from 'redux-mock-store'; -import { mountWithRouter } from '../../../test/lib/render-helpers'; +import { fireEvent } from '@testing-library/react'; +import { renderWithProvider } from '../../../test/lib/render-helpers'; +import configureStore from '../../store/store'; +import { + setPendingTokens, + clearPendingTokens, + getTokenStandardAndDetails, +} from '../../store/actions'; import ImportToken from './import-token.container'; -describe('Import Token', () => { - let wrapper; - - const state = { - metamask: { - tokens: [], - }, - }; - - const store = configureMockStore()(state); +jest.mock('../../store/actions', () => ({ + getTokenStandardAndDetails: jest + .fn() + .mockImplementation(() => Promise.resolve({ standard: 'ERC20' })), + setPendingTokens: jest + .fn() + .mockImplementation(() => ({ type: 'SET_PENDING_TOKENS' })), + clearPendingTokens: jest + .fn() + .mockImplementation(() => ({ type: 'CLEAR_PENDING_TOKENS' })), +})); +describe('Import Token', () => { + const historyStub = jest.fn(); const props = { history: { - push: sinon.stub().callsFake(() => undefined), + push: historyStub, }, - setPendingTokens: sinon.spy(), - clearPendingTokens: sinon.spy(), - tokens: [], - identities: {}, - mostRecentOverviewPage: '/', showSearchTab: true, tokenList: {}, }; - describe('Import Token', () => { - beforeAll(() => { - wrapper = mountWithRouter( - - - , - store, - ); - - wrapper.find({ name: 'customToken' }).simulate('click'); - }); - - afterEach(() => { - props.history.push.reset(); - }); + const render = () => { + const baseStore = { + metamask: { + tokens: [], + provider: { chainId: '0x1' }, + frequentRpcListDetail: [], + identities: {}, + selectedAddress: '0x1231231', + }, + history: { + mostRecentOverviewPage: '/', + }, + }; + + const store = configureStore(baseStore); + + return renderWithProvider(, store); + }; - it('next button is disabled when no fields are populated', () => { - const nextButton = wrapper.find( - '.button.btn-primary.page-container__footer-button', - ); + describe('Import Token', () => { + it('add Custom Token button is disabled when no fields are populated', () => { + const { getByText } = render(); + const customTokenButton = getByText('Custom Token'); + fireEvent.click(customTokenButton); + const submit = getByText('Add Custom Token'); - expect(nextButton.props().disabled).toStrictEqual(true); + expect(submit).toBeDisabled(); }); it('edits token address', () => { + const { getByText } = render(); + const customTokenButton = getByText('Custom Token'); + fireEvent.click(customTokenButton); + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; const event = { target: { value: tokenAddress } }; - const customAddress = wrapper.find('input#custom-address'); + fireEvent.change(document.getElementById('custom-address'), event); - customAddress.simulate('change', event); - expect( - wrapper.find('ImportToken').instance().state.customAddress, - ).toStrictEqual(tokenAddress); + expect(document.getElementById('custom-address').value).toStrictEqual( + tokenAddress, + ); }); it('edits token symbol', () => { + const { getByText } = render(); + const customTokenButton = getByText('Custom Token'); + fireEvent.click(customTokenButton); + const tokenSymbol = 'META'; const event = { target: { value: tokenSymbol } }; - const customAddress = wrapper.find('#custom-symbol'); - customAddress.last().simulate('change', event); + fireEvent.change(document.getElementById('custom-symbol'), event); - expect( - wrapper.find('ImportToken').instance().state.customSymbol, - ).toStrictEqual(tokenSymbol); + expect(document.getElementById('custom-symbol').value).toStrictEqual( + tokenSymbol, + ); }); it('edits token decimal precision', () => { + const { getByText } = render(); + const customTokenButton = getByText('Custom Token'); + fireEvent.click(customTokenButton); + const tokenPrecision = '2'; const event = { target: { value: tokenPrecision } }; - const customAddress = wrapper.find('#custom-decimals'); - customAddress.last().simulate('change', event); + fireEvent.change(document.getElementById('custom-decimals'), event); - expect( - wrapper.find('ImportToken').instance().state.customDecimals, - ).toStrictEqual(Number(tokenPrecision)); + expect(document.getElementById('custom-decimals').value).toStrictEqual( + tokenPrecision, + ); }); - it('next', () => { - const nextButton = wrapper.find( - '.button.btn-primary.page-container__footer-button', - ); - nextButton.simulate('click'); + it('adds custom tokens successfully', async () => { + const { getByText } = render(); + const customTokenButton = getByText('Custom Token'); + fireEvent.click(customTokenButton); - expect(props.setPendingTokens.calledOnce).toStrictEqual(true); - expect(props.history.push.calledOnce).toStrictEqual(true); - expect(props.history.push.getCall(0).args[0]).toStrictEqual( - '/confirm-import-token', - ); + const submit = getByText('Add Custom Token'); + expect(submit).toBeDisabled(); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + fireEvent.change(document.getElementById('custom-address'), { + target: { value: tokenAddress }, + }); + expect(submit).not.toBeDisabled(); + + const tokenSymbol = 'META'; + fireEvent.change(document.getElementById('custom-symbol'), { + target: { value: tokenSymbol }, + }); + + const tokenPrecision = '2'; + await fireEvent.change(document.getElementById('custom-decimals'), { + target: { value: tokenPrecision }, + }); + + expect(submit).not.toBeDisabled(); + fireEvent.click(submit); + expect(setPendingTokens).toHaveBeenCalledWith({ + customToken: { + address: tokenAddress, + decimals: Number(tokenPrecision), + symbol: tokenSymbol, + }, + selectedTokens: {}, + tokenAddressList: [], + }); + expect(historyStub).toHaveBeenCalledWith('/confirm-import-token'); }); - it('cancels', () => { - const cancelButton = wrapper.find('.page-container__header-close'); - cancelButton.simulate('click'); + it('cancels out of import token flow', () => { + const { getByRole } = render(); + const closeButton = getByRole('button', { name: 'close' }); + fireEvent.click(closeButton); + + expect(clearPendingTokens).toHaveBeenCalled(); + expect(historyStub).toHaveBeenCalledWith('/'); + }); + + it('sets and error when a token is an NFT', async () => { + process.env.COLLECTIBLES_V1 = true; + getTokenStandardAndDetails.mockImplementation(() => + Promise.resolve({ standard: 'ERC721' }), + ); + + const { getByText } = render(); + const customTokenButton = getByText('Custom Token'); + fireEvent.click(customTokenButton); + + const submit = getByText('Add Custom Token'); + expect(submit).toBeDisabled(); + + const tokenAddress = '0x617b3f8050a0BD94b6b1da02B4384eE5B4DF13F4'; + await fireEvent.change(document.getElementById('custom-address'), { + target: { value: tokenAddress }, + }); + + expect(submit).toBeDisabled(); - expect(props.clearPendingTokens.calledOnce).toStrictEqual(true); - expect(props.history.push.getCall(0).args[0]).toStrictEqual('/'); + // The last part of this error message won't be found by getByText because it is wrapped as a link. + const errorMessage = getByText('This token is an NFT. Add on the'); + expect(errorMessage).toBeInTheDocument(); }); }); }); diff --git a/ui/pages/import-token/index.scss b/ui/pages/import-token/index.scss index b687b9170..178334fda 100644 --- a/ui/pages/import-token/index.scss +++ b/ui/pages/import-token/index.scss @@ -6,12 +6,12 @@ &__custom-token-form { padding: 8px 16px 16px; - input[type="number"]::-webkit-inner-spin-button { + input[type='number']::-webkit-inner-spin-button { -webkit-appearance: none; display: none; } - input[type="number"]:hover::-webkit-inner-spin-button { + input[type='number']:hover::-webkit-inner-spin-button { -webkit-appearance: none; display: none; } @@ -59,4 +59,13 @@ margin-bottom: 16px; margin-top: 0; } + + &__collectible-address-error-link { + color: var(--primary-blue); + cursor: pointer; + + &:hover { + color: var(--Blue-400); + } + } } diff --git a/ui/pages/send/send-content/send-amount-row/send-amount-row.component.js b/ui/pages/send/send-content/send-amount-row/send-amount-row.component.js index 2e46bd94e..711437e46 100644 --- a/ui/pages/send/send-content/send-amount-row/send-amount-row.component.js +++ b/ui/pages/send/send-content/send-amount-row/send-amount-row.component.js @@ -36,7 +36,7 @@ export default class SendAmountRow extends Component { ); } diff --git a/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js b/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js index 795b9a2c1..d1e308c70 100644 --- a/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js +++ b/ui/pages/send/send-content/send-asset-row/send-asset-row.component.js @@ -4,7 +4,7 @@ import SendRowWrapper from '../send-row-wrapper'; import Identicon from '../../../../components/ui/identicon'; import TokenBalance from '../../../../components/ui/token-balance'; import UserPreferencedCurrencyDisplay from '../../../../components/app/user-preferenced-currency-display'; -import { ERC20, PRIMARY } from '../../../../helpers/constants/common'; +import { ERC20, ERC721, PRIMARY } from '../../../../helpers/constants/common'; import { ASSET_TYPES } from '../../../../ducks/send'; import { isEqualCaseInsensitive } from '../../../../helpers/utils/util'; @@ -57,7 +57,8 @@ export default class SendAssetRow extends Component { async componentDidMount() { const sendableTokens = this.props.tokens.filter((token) => !token.isERC721); const sendableCollectibles = this.props.collectibles.filter( - (collectible) => collectible.isCurrentlyOwned, + (collectible) => + collectible.isCurrentlyOwned && collectible.standard === ERC721, ); this.setState({ sendableTokens, sendableCollectibles }); } diff --git a/ui/pages/send/send-content/send-content.component.js b/ui/pages/send/send-content/send-content.component.js index 7e8a7dfe4..dca6d4a91 100644 --- a/ui/pages/send/send-content/send-content.component.js +++ b/ui/pages/send/send-content/send-content.component.js @@ -37,6 +37,7 @@ export default class SendContent extends Component { getIsBalanceInsufficient: PropTypes.bool, asset: PropTypes.object, to: PropTypes.string, + assetError: PropTypes.string, }; render() { @@ -49,6 +50,7 @@ export default class SendContent extends Component { networkOrAccountNotSupports1559, getIsBalanceInsufficient, asset, + assetError, } = this.props; let gasError; @@ -67,6 +69,7 @@ export default class SendContent extends Component { return (
+ {assetError ? this.renderError(assetError) : null} {gasError ? this.renderError(gasError) : null} {isEthGasPrice ? this.renderWarning(ETH_GAS_PRICE_FETCH_WARNING_KEY) diff --git a/ui/pages/send/send-content/send-content.container.js b/ui/pages/send/send-content/send-content.container.js index ba57a29e9..d3e508e9f 100644 --- a/ui/pages/send/send-content/send-content.container.js +++ b/ui/pages/send/send-content/send-content.container.js @@ -10,6 +10,7 @@ import { getIsBalanceInsufficient, getSendTo, getSendAsset, + getAssetError, } from '../../../ducks/send'; import SendContent from './send-content.component'; @@ -32,6 +33,7 @@ function mapStateToProps(state) { ), getIsBalanceInsufficient: getIsBalanceInsufficient(state), asset: getSendAsset(state), + assetError: getAssetError(state), }; } diff --git a/ui/pages/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.stories.js b/ui/pages/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.stories.js index 3e55f7f06..b02ce345d 100644 --- a/ui/pages/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.stories.js +++ b/ui/pages/send/send-content/send-gas-row/gas-fee-display/gas-fee-display.stories.js @@ -1,22 +1,22 @@ import React from 'react'; -import { action } from '@storybook/addon-actions'; -import { number, boolean } from '@storybook/addon-knobs'; import GasFeeDisplay from './gas-fee-display.component'; export default { title: 'Pages/Send/SendContent/SendGasRow/GasFeeDisplay', id: __filename, + argTypes: { + gasTotal: { control: 'number' }, + gasLoadingError: { control: 'boolean' }, + onReset: { action: 'OnReset' }, + }, }; -export const DefaultStory = () => { - const gasTotal = number('Gas Total', 10000000000); - return ( - - ); +export const DefaultStory = (args) => { + return ; }; DefaultStory.storyName = 'Default'; +DefaultStory.args = { + gasTotal: 10000000000, + gasLoadingError: false, +}; diff --git a/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.stories.js b/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.stories.js index c4dd46e79..b3c774d6a 100644 --- a/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.stories.js +++ b/ui/pages/send/send-content/send-hex-data-row/send-hex-data-row.stories.js @@ -1,21 +1,24 @@ import React from 'react'; -import { boolean } from '@storybook/addon-knobs'; import SendHexDataRow from './send-hex-data-row.component'; export default { title: 'Pages/Send/SendContent/SendHexDataRow', id: __filename, + argTypes: { + inError: { control: 'boolean' }, + updateSendHexData: { action: 'updateSendHexData' }, + }, }; -export const DefaultStory = () => { +export const DefaultStory = (args) => { return (
- null} - /> + null} />
); }; DefaultStory.storyName = 'Default'; +DefaultStory.args = { + inError: false, +}; diff --git a/ui/pages/send/send-footer/send-footer.stories.js b/ui/pages/send/send-footer/send-footer.stories.js index 84118cace..9a9179a35 100644 --- a/ui/pages/send/send-footer/send-footer.stories.js +++ b/ui/pages/send/send-footer/send-footer.stories.js @@ -1,30 +1,33 @@ import React from 'react'; -import { action } from '@storybook/addon-actions'; -import { boolean } from '@storybook/addon-knobs'; import SendFooter from './send-footer.component'; export default { title: 'Pages/Send/SendFooter', id: __filename, + argTypes: { + clearSend: { action: 'clearSend' }, + sign: { action: 'sign' }, + from: { control: 'object' }, + disabled: { control: 'boolean' }, + mostRecentOverviewPage: { control: 'text' }, + sendErrors: { control: 'object' }, + history: { action: 'history' }, + addToAddressBookIfNew: { action: 'addToAddressBookIfNew' }, + resetSendState: { action: 'resetSendState' }, + }, }; -export const DefaultStory = () => { - const disabled = boolean('Disabled', false); - return ( - action('Cancel Button Pressed')()} - sign={() => action('Next Button Pressed')()} - // The other props below are only to make the component show no error - from={{ address: '' }} - history={{ push: () => undefined }} - addToAddressBookIfNew={() => undefined} - disabled={disabled} - mostRecentOverviewPage="" - resetSendState={() => undefined} - sendErrors={{}} - /> - ); +export const DefaultStory = (args) => { + return ; }; DefaultStory.storyName = 'Default'; +DefaultStory.args = { + from: { + address: '', + }, + disabled: false, + mostRecentOverviewPage: '', + sendErrors: {}, +}; diff --git a/ui/pages/send/send-header/send-header.stories.js b/ui/pages/send/send-header/send-header.stories.js index 8b0f317e8..9423bb6cd 100644 --- a/ui/pages/send/send-header/send-header.stories.js +++ b/ui/pages/send/send-header/send-header.stories.js @@ -2,7 +2,6 @@ import React, { useEffect } from 'react'; import { combineReducers, createStore } from 'redux'; import { Provider } from 'react-redux'; -import { select } from '@storybook/addon-knobs'; import { updateSendStage, updateSendAsset, @@ -10,47 +9,53 @@ import { import sendSBReducer from '../../../../.storybook/reducers/sb-send-reducer'; import historySBReducer from '../../../../.storybook/reducers/sb-history-reducer'; - -import { ASSET_TYPES, SEND_STAGES } from '../../../ducks/send'; import SendHeader from './send-header.component'; export default { title: 'Pages/Send/SendHeader', id: __filename, + argTypes: { + asset: { + control: { + type: 'select', + }, + options: ['NATIVE', 'TOKEN'], + }, + stage: { + control: { + type: 'select', + }, + options: ['ADD_RECIPIENT', 'DRAFT', 'EDIT', 'INACTIVE'], + }, + }, }; -export const DefaultStory = () => { - const store = createStore( - combineReducers({ send: sendSBReducer, history: historySBReducer }), - ); - const state = store.getState(); - const { send } = state; - const asset = - select('Asset', [ASSET_TYPES.NATIVE, ASSET_TYPES.TOKEN]) || send.asset; - - const stage = - select('Stage', [ - SEND_STAGES.ADD_RECIPIENT, - SEND_STAGES.DRAFT, - SEND_STAGES.EDIT, - SEND_STAGES.INACTIVE, - ]) || send.stage; +const store = createStore( + combineReducers({ send: sendSBReducer, history: historySBReducer }), +); +const state = store.getState(); +const { send } = state; +export const DefaultStory = (args) => { useEffect(() => { - store.dispatch(updateSendAsset(asset)); - }, [store, asset]); + store.dispatch(updateSendAsset(args.asset)); + }, [args.asset]); useEffect(() => { - store.dispatch(updateSendStage(stage)); - }, [store, stage]); + store.dispatch(updateSendStage(args.stage)); + }, [args.stage]); return (
- +
); }; DefaultStory.storyName = 'Default'; +DefaultStory.args = { + asset: 'NATIVE' || send.asset, + stage: 'ADD_RECIPIENT' || send.stage, +}; diff --git a/ui/pages/send/send.test.js b/ui/pages/send/send.test.js index 2f7be9c56..e2d16be9f 100644 --- a/ui/pages/send/send.test.js +++ b/ui/pages/send/send.test.js @@ -80,6 +80,9 @@ const baseStore = { }, identities: { '0x0': { address: '0x0' } }, }, + appState: { + sendInputCurrencySwitched: false, + }, }; describe('Send Page', () => { diff --git a/ui/pages/settings/advanced-tab/advanced-tab.stories.js b/ui/pages/settings/advanced-tab/advanced-tab.stories.js index 3be10b39c..259069686 100644 --- a/ui/pages/settings/advanced-tab/advanced-tab.stories.js +++ b/ui/pages/settings/advanced-tab/advanced-tab.stories.js @@ -1,48 +1,121 @@ import React from 'react'; -import { text, boolean } from '@storybook/addon-knobs'; +import { useArgs } from '@storybook/client-api'; import AdvancedTab from './advanced-tab.component'; export default { title: 'Pages/Settings/AdvancedTab', id: __filename, + argTypes: { + warning: { control: 'text' }, + useNonceField: { control: 'boolean' }, + sendHexData: { control: 'boolean' }, + advancedInlineGas: { control: 'boolean' }, + showFiatInTestnets: { control: 'boolean' }, + threeBoxSyncingAllowed: { control: 'boolean' }, + threeBoxDisabled: { control: 'boolean' }, + useLedgerLive: { control: 'boolean' }, + dismissSeedBackUpReminder: { control: 'boolean' }, + setAutoLockTimeLimit: { action: 'setAutoLockTimeLimit' }, + setShowFiatConversionOnTestnetsPreference: { + action: 'setShowFiatConversionOnTestnetsPreference', + }, + setShowTestNetworks: { action: 'setShowTestNetworks' }, + setThreeBoxSyncingPermission: { action: 'setThreeBoxSyncingPermission' }, + setIpfsGateway: { action: 'setIpfsGateway' }, + setLedgerTransportPreference: { action: 'setLedgerTransportPreference' }, + setDismissSeedBackUpReminder: { action: 'setDismissSeedBackUpReminder' }, + setUseNonceField: { action: 'setUseNonceField' }, + setHexDataFeatureFlag: { action: 'setHexDataFeatureFlag' }, + displayWarning: { action: 'displayWarning' }, + history: { action: 'history' }, + showResetAccountConfirmationModal: { + action: 'showResetAccountConfirmationModal', + }, + setAdvancedInlineGasFeatureFlag: { + action: 'setAdvancedInlineGasFeatureFlag', + }, + }, }; -export const DefaultStory = () => { +export const DefaultStory = (args) => { + const [ + { + useNonceField, + sendHexData, + advancedInlineGas, + showFiatInTestnets, + threeBoxSyncingAllowed, + dismissSeedBackUpReminder, + }, + updateArgs, + ] = useArgs(); + + const handleUseNonceField = () => { + updateArgs({ + useNonceField: !useNonceField, + }); + }; + + const handleSendHexData = () => { + updateArgs({ + sendHexData: !sendHexData, + }); + }; + + const handleAdvancedInlineGas = () => { + updateArgs({ + advancedInlineGas: !advancedInlineGas, + }); + }; + + const handleShowFiatInTestnets = () => { + updateArgs({ + showFiatInTestnets: !showFiatInTestnets, + }); + }; + + const handleThreeBoxSyncingAllowed = () => { + updateArgs({ + threeBoxSyncingAllowed: !threeBoxSyncingAllowed, + }); + }; + + const handleDismissSeedBackUpReminder = () => { + updateArgs({ + dismissSeedBackUpReminder: !dismissSeedBackUpReminder, + }); + }; return (
undefined} - setShowFiatConversionOnTestnetsPreference={() => undefined} - setShowTestNetworks={() => undefined} - setThreeBoxSyncingPermission={() => undefined} - setIpfsGateway={() => undefined} - setLedgerTransportPreference={() => undefined} - setDismissSeedBackUpReminder={() => undefined} - setUseNonceField={() => undefined} - setHexDataFeatureFlag={() => undefined} - displayWarning={() => undefined} - history={{ push: () => undefined }} - showResetAccountConfirmationModal={() => undefined} - setAdvancedInlineGasFeatureFlag={() => undefined} - warning={text('Warning', 'Warning Sample')} + {...args} + useNonceField={useNonceField} + setUseNonceField={handleUseNonceField} + sendHexData={sendHexData} + setHexDataFeatureFlag={handleSendHexData} + advancedInlineGas={advancedInlineGas} + setAdvancedInlineGasFeatureFlag={handleAdvancedInlineGas} + showFiatInTestnets={showFiatInTestnets} + setShowFiatConversionOnTestnetsPreference={handleShowFiatInTestnets} + threeBoxSyncingAllowed={threeBoxSyncingAllowed} + setThreeBoxSyncingPermission={handleThreeBoxSyncingAllowed} + dismissSeedBackUpReminder={dismissSeedBackUpReminder} + setDismissSeedBackUpReminder={handleDismissSeedBackUpReminder} ipfsGateway="ipfs-gateway" - useNonceField={boolean('Customize Transaction Nonce', false)} - sendHexData={boolean('Show Hex Data', false)} - advancedInlineGas={boolean('Advanced Inline Gas', false)} - showFiatInTestnets={boolean('Show Conversion on Testnets', false)} - threeBoxSyncingAllowed={boolean( - 'Sync data with 3Box (experimental)', - false, - )} - threeBoxDisabled={boolean('3Box Disabled', false)} - useLedgerLive={boolean('Use Ledger Live', false)} - dismissSeedBackUpReminder={boolean( - 'Dismiss recovery phrase backup reminder', - false, - )} />
); }; DefaultStory.storyName = 'Default'; +DefaultStory.args = { + warning: 'Warning Sample', + useNonceField: false, + sendHexData: false, + advancedInlineGas: false, + showFiatInTestnets: false, + threeBoxSyncingAllowed: false, + threeBoxDisabled: false, + useLedgerLive: false, + dismissSeedBackUpReminder: false, +}; diff --git a/ui/pages/settings/contact-list-tab/contact-list-tab.stories.js b/ui/pages/settings/contact-list-tab/contact-list-tab.stories.js index fc3409ca7..20c9640e9 100644 --- a/ui/pages/settings/contact-list-tab/contact-list-tab.stories.js +++ b/ui/pages/settings/contact-list-tab/contact-list-tab.stories.js @@ -1,6 +1,5 @@ import React from 'react'; import { Provider } from 'react-redux'; -import { object, boolean, select } from '@storybook/addon-knobs'; import configureStore from '../../../store/store'; import testData from '../../../../.storybook/test-data'; @@ -13,28 +12,28 @@ export default { title: 'Pages/Settings/ContactListTab', id: __filename, decorators: [(story) => {story()}], + argsTypes: { + addressBook: { control: 'object' }, + hideAddressBook: { control: 'boolean' }, + selectedAddress: { control: 'select' }, + history: { action: 'history' }, + }, }; -export const DefaultStory = () => { - const { metamask } = store.getState(); - const { addresses } = metamask; - const addressBook = object('Address Book', addresses); - const hideAddressBook = boolean('Hide Address Book', false); - const selectedAddress = select( - 'Selected Address', - addresses.map(({ address }) => address), - ); +const { metamask } = store.getState(); +const { addresses } = metamask; +export const DefaultStory = (args) => { return (
- undefined }} - hideAddressBook={hideAddressBook} - selectedAddress={selectedAddress} - /> +
); }; DefaultStory.storyName = 'Default'; +DefaultStory.args = { + addressBook: addresses, + hideAddressBook: false, + selectedAddress: addresses.map(({ address }) => address), +}; diff --git a/ui/pages/settings/experimental-tab/experimental-tab.component.js b/ui/pages/settings/experimental-tab/experimental-tab.component.js index 1dc9cb549..6b37d955a 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.component.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.component.js @@ -14,7 +14,9 @@ export default class ExperimentalTab extends PureComponent { useCollectibleDetection: PropTypes.bool, setUseCollectibleDetection: PropTypes.func, setOpenSeaEnabled: PropTypes.func, - openSeaEnabled: PropTypes.func, + openSeaEnabled: PropTypes.bool, + eip1559V2Enabled: PropTypes.bool, + setEIP1559V2Enabled: PropTypes.func, }; renderTokenDetectionToggle() { @@ -62,10 +64,11 @@ export default class ExperimentalTab extends PureComponent { useCollectibleDetection, setUseCollectibleDetection, openSeaEnabled, + setOpenSeaEnabled, } = this.props; return ( -
+
{t('useCollectibleDetection')}
@@ -75,7 +78,6 @@ export default class ExperimentalTab extends PureComponent {
{ this.context.metricsEvent({ @@ -85,6 +87,9 @@ export default class ExperimentalTab extends PureComponent { name: 'Collectible Detection', }, }); + if (!value && !openSeaEnabled) { + setOpenSeaEnabled(!value); + } setUseCollectibleDetection(!value); }} offLabel={t('off')} @@ -101,10 +106,15 @@ export default class ExperimentalTab extends PureComponent { return null; } const { t } = this.context; - const { openSeaEnabled, setOpenSeaEnabled } = this.props; + const { + openSeaEnabled, + setOpenSeaEnabled, + useCollectibleDetection, + setUseCollectibleDetection, + } = this.props; return ( -
+
{t('enableOpenSeaAPI')}
@@ -124,6 +134,59 @@ export default class ExperimentalTab extends PureComponent { }, }); setOpenSeaEnabled(!value); + if (value && !useCollectibleDetection) { + setUseCollectibleDetection(true); + } + }} + offLabel={t('off')} + onLabel={t('on')} + /> +
+
+
+ ); + } + + renderEIP1559V2EnabledToggle() { + const EIP_1559_V2_ENABLED = + // This is a string in unit tests but is a boolean in the browser + process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true'; + if (!EIP_1559_V2_ENABLED) { + return null; + } + const { t } = this.context; + const { eip1559V2Enabled, setEIP1559V2Enabled } = this.props; + + return ( +
+
+ {t('enableEIP1559V2')} +
+ {t('enableEIP1559V2Description', [ + + {t('learnMoreUpperCase')} + , + ])} +
+
+
+
+ { + this.context.metricsEvent({ + eventOpts: { + category: 'Settings', + action: 'Enabled/Disable OpenSea', + name: 'Enabled/Disable OpenSea', + }, + }); + setEIP1559V2Enabled(!value); }} offLabel={t('off')} onLabel={t('on')} @@ -140,6 +203,7 @@ export default class ExperimentalTab extends PureComponent { {this.renderTokenDetectionToggle()} {this.renderOpenSeaEnabledToggle()} {this.renderCollectibleDetectionToggle()} + {this.renderEIP1559V2EnabledToggle()}
); } diff --git a/ui/pages/settings/experimental-tab/experimental-tab.container.js b/ui/pages/settings/experimental-tab/experimental-tab.container.js index ac26f10ab..195d01e88 100644 --- a/ui/pages/settings/experimental-tab/experimental-tab.container.js +++ b/ui/pages/settings/experimental-tab/experimental-tab.container.js @@ -5,11 +5,13 @@ import { setUseTokenDetection, setUseCollectibleDetection, setOpenSeaEnabled, + setEIP1559V2Enabled, } from '../../../store/actions'; import { getUseTokenDetection, getUseCollectibleDetection, getOpenSeaEnabled, + getEIP1559V2Enabled, } from '../../../selectors'; import ExperimentalTab from './experimental-tab.component'; @@ -18,6 +20,7 @@ const mapStateToProps = (state) => { useTokenDetection: getUseTokenDetection(state), useCollectibleDetection: getUseCollectibleDetection(state), openSeaEnabled: getOpenSeaEnabled(state), + eip1559V2Enabled: getEIP1559V2Enabled(state), }; }; @@ -27,6 +30,7 @@ const mapDispatchToProps = (dispatch) => { setUseCollectibleDetection: (val) => dispatch(setUseCollectibleDetection(val)), setOpenSeaEnabled: (val) => dispatch(setOpenSeaEnabled(val)), + setEIP1559V2Enabled: (val) => dispatch(setEIP1559V2Enabled(val)), }; }; diff --git a/ui/pages/settings/index.scss b/ui/pages/settings/index.scss index b10a15b37..d854a5951 100644 --- a/ui/pages/settings/index.scss +++ b/ui/pages/settings/index.scss @@ -168,6 +168,15 @@ display: flex; flex-direction: column; padding: 10px 0 20px; + + &--parent { + padding: 10px 0 10px; + } + + &--dependent { + margin-left: 48px; + padding: 0 0 20px; + } } &__content-item { @@ -205,6 +214,10 @@ color: var(--dusty-gray); padding-top: 5px; + + a { + color: var(--Blue-500); + } } &__content-item-col { diff --git a/ui/pages/swaps/awaiting-swap/__snapshots__/awaiting-swap.test.js.snap b/ui/pages/swaps/awaiting-swap/__snapshots__/awaiting-swap.test.js.snap index c0c43246c..3f523d5d1 100644 --- a/ui/pages/swaps/awaiting-swap/__snapshots__/awaiting-swap.test.js.snap +++ b/ui/pages/swaps/awaiting-swap/__snapshots__/awaiting-swap.test.js.snap @@ -2,7 +2,7 @@ exports[`AwaitingSwap renders the component with initial props 1`] = `
diff --git a/ui/pages/swaps/awaiting-swap/awaiting-swap.js b/ui/pages/swaps/awaiting-swap/awaiting-swap.js index 64905efd4..658ed7175 100644 --- a/ui/pages/swaps/awaiting-swap/awaiting-swap.js +++ b/ui/pages/swaps/awaiting-swap/awaiting-swap.js @@ -273,7 +273,7 @@ export default function AwaitingSwap({ )}
{statusImage}
{headerText}
-
{descriptionText}
+
{descriptionText}
{content}
{!errorKey && swapComplete ? : null} diff --git a/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js b/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js index 079f16a01..30a587df1 100644 --- a/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js +++ b/ui/pages/swaps/awaiting-swap/awaiting-swap.test.js @@ -30,7 +30,7 @@ describe('AwaitingSwap', () => { expect(getByText('ETH')).toBeInTheDocument(); expect(getByText('View in activity')).toBeInTheDocument(); expect( - document.querySelector('.awaiting-swap__main-descrption'), + document.querySelector('.awaiting-swap__main-description'), ).toMatchSnapshot(); expect(getByText('View in activity')).toBeInTheDocument(); }); diff --git a/ui/pages/swaps/awaiting-swap/index.scss b/ui/pages/swaps/awaiting-swap/index.scss index 297970dc1..9ed729b9c 100644 --- a/ui/pages/swaps/awaiting-swap/index.scss +++ b/ui/pages/swaps/awaiting-swap/index.scss @@ -35,7 +35,7 @@ color: var(--Black-100); } - &__main-descrption { + &__main-description { @include H6; color: var(--Grey-500); diff --git a/ui/pages/swaps/fee-card/fee-card.js b/ui/pages/swaps/fee-card/fee-card.js index d044f77c7..fd1acd538 100644 --- a/ui/pages/swaps/fee-card/fee-card.js +++ b/ui/pages/swaps/fee-card/fee-card.js @@ -35,7 +35,7 @@ export default function FeeCard({ onQuotesClick, chainId, isBestQuote, - supportsEIP1559V2, + supportsEIP1559V2 = false, }) { const t = useContext(I18nContext); @@ -211,5 +211,5 @@ FeeCard.propTypes = { numberOfQuotes: PropTypes.number.isRequired, chainId: PropTypes.string.isRequired, isBestQuote: PropTypes.bool.isRequired, - supportsEIP1559V2: PropTypes.bool.isRequired, + supportsEIP1559V2: PropTypes.bool, }; diff --git a/ui/pages/swaps/fee-card/fee-card.test.js b/ui/pages/swaps/fee-card/fee-card.test.js index 27dba700d..16f43da8d 100644 --- a/ui/pages/swaps/fee-card/fee-card.test.js +++ b/ui/pages/swaps/fee-card/fee-card.test.js @@ -14,6 +14,7 @@ import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network'; import { checkNetworkAndAccountSupports1559, + getEIP1559V2Enabled, getPreferences, getSelectedAccount, } from '../../../selectors'; @@ -133,7 +134,6 @@ describe('FeeCard', () => { }); it('renders the component with EIP-1559 V2 enabled', () => { - process.env.EIP_1559_V2 = true; useGasFeeEstimates.mockImplementation(() => ({ gasFeeEstimates: {} })); useSelector.mockImplementation((selector) => { if (selector === getPreferences) { @@ -141,6 +141,9 @@ describe('FeeCard', () => { useNativeCurrencyAsPrimaryCurrency: true, }; } + if (selector === getEIP1559V2Enabled) { + return true; + } if (selector === getSelectedAccount) { return { balance: '0x440aa47cc2556', @@ -180,6 +183,5 @@ describe('FeeCard', () => { expect( document.querySelector('.fee-card__top-bordered-row'), ).toMatchSnapshot(); - process.env.EIP_1559_V2 = false; }); }); diff --git a/ui/pages/swaps/view-quote/view-quote.js b/ui/pages/swaps/view-quote/view-quote.js index 47dbaaf6e..d2fb50d93 100644 --- a/ui/pages/swaps/view-quote/view-quote.js +++ b/ui/pages/swaps/view-quote/view-quote.js @@ -46,6 +46,7 @@ import { isHardwareWallet, getHardwareWalletType, checkNetworkAndAccountSupports1559, + getEIP1559V2Enabled, } from '../../../selectors'; import { getNativeCurrency, getTokens } from '../../../ducks/metamask/metamask'; @@ -103,15 +104,12 @@ import CountdownTimer from '../countdown-timer'; import SwapsFooter from '../swaps-footer'; import ViewQuotePriceDifference from './view-quote-price-difference'; -// eslint-disable-next-line prefer-destructuring -const EIP_1559_V2_ENABLED = - process.env.EIP_1559_V2 === true || process.env.EIP_1559_V2 === 'true'; - export default function ViewQuote() { const history = useHistory(); const dispatch = useDispatch(); const t = useContext(I18nContext); const metaMetricsEvent = useContext(MetaMetricsContext); + const eip1559V2Enabled = useSelector(getEIP1559V2Enabled); const [dispatchedSafeRefetch, setDispatchedSafeRefetch] = useState(false); const [submitClicked, setSubmitClicked] = useState(false); @@ -689,8 +687,7 @@ export default function ViewQuote() { }, }; - const supportsEIP1559V2 = - EIP_1559_V2_ENABLED && networkAndAccountSupports1559; + const supportsEIP1559V2 = eip1559V2Enabled && networkAndAccountSupports1559; return ( state.metamask.fragments; + +export const selectFragmentBySuccessEvent = createSelector( + selectFragments, + (_, fragmentOptions) => fragmentOptions, + (fragments, fragmentOptions) => { + if (fragmentOptions.persist) { + return Object.values(fragments).find( + (fragment) => fragment.successEvent === fragmentOptions.successEvent, + ); + } + return undefined; + }, +); + +export const selectFragmentById = createSelector( + selectFragments, + (_, fragmentId) => fragmentId, + (fragments, fragmentId) => { + // A valid existing fragment must exist in state. + // If these conditions are not meant we will create a new fragment. + if (fragmentId && fragments?.[fragmentId]) { + return fragments[fragmentId]; + } + return undefined; + }, +); + +export const selectMatchingFragment = createSelector( + (state, params) => + selectFragmentBySuccessEvent(state, params.fragmentOptions), + (state, params) => selectFragmentById(state, params.existingId), + (matchedBySuccessEvent, matchedById) => matchedById ?? matchedBySuccessEvent, +); diff --git a/ui/selectors/metametrics.test.js b/ui/selectors/metametrics.test.js new file mode 100644 index 000000000..13185a477 --- /dev/null +++ b/ui/selectors/metametrics.test.js @@ -0,0 +1,71 @@ +const { + selectFragmentBySuccessEvent, + selectFragmentById, + selectMatchingFragment, +} = require('.'); + +describe('selectFragmentBySuccessEvent', () => { + it('should find matching fragment in state by successEvent', () => { + const state = { + metamask: { + fragments: { + randomid: { + successEvent: 'example event', + persist: true, + id: 'randomid', + }, + }, + }, + }; + const selected = selectFragmentBySuccessEvent(state, { + successEvent: 'example event', + persist: true, + }); + expect(selected).toHaveProperty('id', 'randomid'); + }); +}); + +describe('selectFragmentById', () => { + it('should find matching fragment in state by id', () => { + const state = { + metamask: { + fragments: { + randomid: { + successEvent: 'example event', + persist: true, + id: 'randomid', + }, + }, + }, + }; + const selected = selectFragmentById(state, 'randomid'); + expect(selected).toHaveProperty('id', 'randomid'); + }); +}); + +describe('selectMatchingFragment', () => { + it('should find matching fragment in state by id', () => { + const state = { + metamask: { + fragments: { + notthecorrectid: { + successEvent: 'event name', + id: 'notthecorrectid', + }, + randomid: { + successEvent: 'example event', + persist: true, + id: 'randomid', + }, + }, + }, + }; + const selected = selectMatchingFragment(state, { + fragmentOptions: { + successEvent: 'event name', + }, + existingId: 'randomid', + }); + expect(selected).toHaveProperty('id', 'randomid'); + }); +}); diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 75ff3ab0b..0c0ce397d 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -816,6 +816,10 @@ export function getAdvancedGasFeeValues(state) { return state.metamask.advancedGasFee; } +export function getEIP1559V2Enabled(state) { + return state.metamask.eip1559V2Enabled; +} + /** * To check if the user has set advanced gas fee settings as default with a non empty maxBaseFee and priotityFee. * diff --git a/ui/store/actionConstants.js b/ui/store/actionConstants.js index f3a86abd8..6ad3bef76 100644 --- a/ui/store/actionConstants.js +++ b/ui/store/actionConstants.js @@ -107,3 +107,5 @@ export const SET_OPEN_METAMASK_TAB_IDS = 'SET_OPEN_METAMASK_TAB_IDS'; export const HIDE_WHATS_NEW_POPUP = 'HIDE_WHATS_NEW_POPUP'; export const TOGGLE_GAS_LOADING_ANIMATION = 'TOGGLE_GAS_LOADING_ANIMATION'; + +export const SET_CURRENCY_INPUT_SWITCH = 'SET_CURRENCY_INPUT_SWITCH'; diff --git a/ui/store/actions.js b/ui/store/actions.js index bfd437bc0..6800a9ced 100644 --- a/ui/store/actions.js +++ b/ui/store/actions.js @@ -1418,6 +1418,18 @@ export async function checkAndUpdateSingleCollectibleOwnershipStatus( ); } +export async function getTokenStandardAndDetails( + address, + userAddress, + tokenId, +) { + return await promisifiedBackground.getTokenStandardAndDetails( + address, + userAddress, + tokenId, + ); +} + export function removeToken(address) { return async (dispatch) => { dispatch(showLoadingIndication()); @@ -1817,6 +1829,13 @@ export function hideAlert() { }; } +export function updateCollectibleDropDownState(value) { + return async (dispatch) => { + await promisifiedBackground.updateCollectibleDropDownState(value); + await forceUpdateMetamaskState(dispatch); + }; +} + /** * This action will receive two types of values via qrCodeData * an object with the following structure {type, values} @@ -2256,6 +2275,18 @@ export function setAdvancedGasFee(val) { }; } +export function setEIP1559V2Enabled(val) { + return async (dispatch) => { + dispatch(showLoadingIndication()); + log.debug(`background.setEIP1559V2Enabled`); + try { + await promisifiedBackground.setEIP1559V2Enabled(val); + } finally { + dispatch(hideLoadingIndication()); + } + }; +} + export function setIpfsGateway(val) { return (dispatch) => { dispatch(showLoadingIndication()); @@ -2743,13 +2774,26 @@ export function loadingTokenParamsFinished() { }; } -export function getTokenParams(tokenAddress) { +export function getTokenParams(address) { return (dispatch, getState) => { const tokenList = getTokenList(getState()); const existingTokens = getState().metamask.tokens; - const existingToken = existingTokens.find(({ address }) => - isEqualCaseInsensitive(tokenAddress, address), + const { selectedAddress } = getState().metamask; + const { chainId } = getState().metamask.provider; + const existingCollectibles = getState().metamask?.allCollectibles?.[ + selectedAddress + ]?.[chainId]; + const existingToken = existingTokens.find(({ address: tokenAddress }) => + isEqualCaseInsensitive(address, tokenAddress), ); + const existingCollectible = existingCollectibles?.find( + ({ address: collectibleAddress }) => + isEqualCaseInsensitive(address, collectibleAddress), + ); + + if (existingCollectible) { + return null; + } if (existingToken) { return Promise.resolve({ @@ -2761,9 +2805,9 @@ export function getTokenParams(tokenAddress) { dispatch(loadingTokenParamsStarted()); log.debug(`loadingTokenParams`); - return getSymbolAndDecimals(tokenAddress, tokenList).then( + return getSymbolAndDecimals(address, tokenList).then( ({ symbol, decimals }) => { - dispatch(addToken(tokenAddress, symbol, Number(decimals))); + dispatch(addToken(address, symbol, Number(decimals))); dispatch(loadingTokenParamsFinished()); }, ); @@ -3060,6 +3104,18 @@ export function trackMetaMetricsEvent(payload, options) { return promisifiedBackground.trackMetaMetricsEvent(payload, options); } +export function createEventFragment(options) { + return promisifiedBackground.createEventFragment(options); +} + +export function updateEventFragment(id, payload) { + return promisifiedBackground.updateEventFragment(id, payload); +} + +export function finalizeEventFragment(id, options) { + return promisifiedBackground.finalizeEventFragment(id, options); +} + /** * @param {MetaMetricsPagePayload} payload - details of the page viewed * @param {MetaMetricsPageOptions} options - options for handling the page view @@ -3100,6 +3156,10 @@ export function setCollectiblesDetectionNoticeDismissed() { return promisifiedBackground.setCollectiblesDetectionNoticeDismissed(true); } +export function setEnableEIP1559V2NoticeDismissed() { + return promisifiedBackground.setEnableEIP1559V2NoticeDismissed(true); +} + // QR Hardware Wallets export async function submitQRHardwareCryptoHDKey(cbor) { await promisifiedBackground.submitQRHardwareCryptoHDKey(cbor); diff --git a/yarn.lock b/yarn.lock index d6cce7d46..e45a72e68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1440,21 +1440,6 @@ "@ethereumjs/common" "^2.4.0" ethereumjs-util "^7.1.0" -"@ethersproject/abi@5.0.0-beta.153": - version "5.0.0-beta.153" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" - integrity sha512-aXweZ1Z7vMNzJdLpR1CZUAIgnwjrZeUSvN9syCwlBaEBUFJmFY+HHnfuTI5vIhVs/mRkfJVrbEyl51JZQqyjAg== - dependencies: - "@ethersproject/address" ">=5.0.0-beta.128" - "@ethersproject/bignumber" ">=5.0.0-beta.130" - "@ethersproject/bytes" ">=5.0.0-beta.129" - "@ethersproject/constants" ">=5.0.0-beta.128" - "@ethersproject/hash" ">=5.0.0-beta.128" - "@ethersproject/keccak256" ">=5.0.0-beta.127" - "@ethersproject/logger" ">=5.0.0-beta.129" - "@ethersproject/properties" ">=5.0.0-beta.131" - "@ethersproject/strings" ">=5.0.0-beta.130" - "@ethersproject/abi@5.5.0", "@ethersproject/abi@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" @@ -1494,7 +1479,7 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/properties" "^5.5.0" -"@ethersproject/address@5.5.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.5.0": +"@ethersproject/address@5.5.0", "@ethersproject/address@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== @@ -1520,7 +1505,7 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/properties" "^5.5.0" -"@ethersproject/bignumber@5.5.0", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.5.0": +"@ethersproject/bignumber@5.5.0", "@ethersproject/bignumber@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== @@ -1529,14 +1514,14 @@ "@ethersproject/logger" "^5.5.0" bn.js "^4.11.9" -"@ethersproject/bytes@5.5.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.5.0": +"@ethersproject/bytes@5.5.0", "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== dependencies: "@ethersproject/logger" "^5.5.0" -"@ethersproject/constants@5.5.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.5.0": +"@ethersproject/constants@5.5.0", "@ethersproject/constants@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== @@ -1571,7 +1556,7 @@ optionalDependencies: "@ledgerhq/hw-transport-node-hid" "5.26.0" -"@ethersproject/hash@5.5.0", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.5.0": +"@ethersproject/hash@5.5.0", "@ethersproject/hash@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== @@ -1622,7 +1607,7 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.5.0", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.5.0": +"@ethersproject/keccak256@5.5.0", "@ethersproject/keccak256@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== @@ -1630,7 +1615,7 @@ "@ethersproject/bytes" "^5.5.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.5.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.5.0": +"@ethersproject/logger@5.5.0", "@ethersproject/logger@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== @@ -1650,7 +1635,7 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/sha2" "^5.5.0" -"@ethersproject/properties@5.5.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.5.0": +"@ethersproject/properties@5.5.0", "@ethersproject/properties@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== @@ -1731,7 +1716,7 @@ "@ethersproject/sha2" "^5.5.0" "@ethersproject/strings" "^5.5.0" -"@ethersproject/strings@5.5.0", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.5.0": +"@ethersproject/strings@5.5.0", "@ethersproject/strings@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== @@ -1740,7 +1725,7 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" -"@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.5.0": +"@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== @@ -2652,10 +2637,10 @@ web3 "^0.20.7" web3-provider-engine "^16.0.3" -"@metamask/controllers@^24.0.0": - version "24.0.0" - resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-24.0.0.tgz#380d4e10bd9b903afc38b041ea7e64b30ae34dab" - integrity sha512-gOTpvxQTNTrXOUpGOiRKcqHUgnjSiTgJcwNLxenZWqPIixz7eI2qHnjO7ZaGbV/baK7SOa4rRGokvsCNV0mafA== +"@metamask/controllers@^25.0.0": + version "25.1.0" + resolved "https://registry.yarnpkg.com/@metamask/controllers/-/controllers-25.1.0.tgz#2efee24a9a2b03ab2a2b0422c8f250931c269560" + integrity sha512-syndn2lIhtlACzaqjDrw23dJzw8pZ6en4Cr35C7B9RRS87EhahUqkPP73moAzLtvbyqtBlAUO1HHrqV3lw4E5g== dependencies: "@ethereumjs/common" "^2.3.1" "@ethereumjs/tx" "^3.2.1" @@ -2681,7 +2666,7 @@ isomorphic-fetch "^3.0.0" jsonschema "^1.2.4" multiformats "^9.5.2" - nanoid "^3.1.12" + nanoid "^3.1.31" punycode "^2.1.1" single-call-balance-checker-abi "^1.0.0" uuid "^8.3.2" @@ -2718,10 +2703,10 @@ ethereumjs-util "^7.0.9" hdkey "0.8.0" -"@metamask/eth-token-tracker@^3.0.1": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@metamask/eth-token-tracker/-/eth-token-tracker-3.1.0.tgz#1396c5d88401f3cd598b0c86d3a42951c8e78d73" - integrity sha512-SopDFnIu7lI8aloD9HGPEpqlcjZNzZ5BeXZqrNRSdSO+CGpuwmFoeyrCrgEmrlkKQvP8dQi6R6aYK/itFPhPrg== +"@metamask/eth-token-tracker@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-token-tracker/-/eth-token-tracker-4.0.0.tgz#f3129855873a857ef675d4c28f532be684241b61" + integrity sha512-T+InGiclG5JJB1uOhMpzywthuDm2xw+Wiq3Y7UbCDdLHtAWxgAwPlNVAyfjwlbWmf9G/TODzMuYyyxEtynbYJw== dependencies: deep-equal "^1.1.0" eth-block-tracker "^4.4.2" @@ -4167,6 +4152,13 @@ node-interval-tree "^1.3.3" web3-utils "1.5.3" +"@trufflesuite/bigint-buffer@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.9.tgz#e2604d76e1e4747b74376d68f1312f9944d0d75d" + integrity sha512-bdM5cEGCOhDSwminryHJbRmXc1x7dPKg6Pqns3qyTwFlxsqUgxE29lsERS3PlIW1HTjoIGMUqsk1zQQwST1Yxw== + dependencies: + node-gyp-build "4.3.0" + "@types/aria-query@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" @@ -4205,7 +4197,7 @@ dependencies: "@babel/types" "^7.3.0" -"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": +"@types/bn.js@^4.11.3": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== @@ -4422,7 +4414,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== -"@types/node@^12.0.12", "@types/node@^12.12.6": +"@types/node@^12.0.12": version "12.19.15" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.15.tgz#0de7e978fb43db62da369db18ea088a63673c182" integrity sha512-lowukE3GUI+VSYSu6VcBXl14d61Rp5hA1D+61r16qnwC0lYNSqdxcvRh0pswejorHfS+HgwBasM8jLXz0/aOsw== @@ -4983,20 +4975,6 @@ abstract-leveldown@0.12.3: dependencies: xtend "~3.0.0" -abstract-leveldown@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz#5cb89f958a44f526779d740d1440e743e0c30a57" - integrity sha512-KUWx9UWGQD12zsmLNj64/pndaz4iJh/Pj7nopgkfDG6RlCcbMZvT6+9l7dchK4idog2Is8VdC/PvNbFuFmalIQ== - dependencies: - xtend "~4.0.0" - -abstract-leveldown@^2.4.1, abstract-leveldown@~2.7.1: - version "2.7.2" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" - integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== - dependencies: - xtend "~4.0.0" - abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz#f7128e1f86ccabf7d2893077ce5d06d798e386c6" @@ -5004,14 +4982,29 @@ abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: dependencies: xtend "~4.0.0" -abstract-leveldown@^6.0.0, abstract-leveldown@~6.0.0, abstract-leveldown@~6.0.1, abstract-leveldown@~6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz#b4b6159343c74b0c5197b2817854782d8f748c4a" - integrity sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q== +abstract-leveldown@^6.0.0, abstract-leveldown@~6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== dependencies: + buffer "^5.5.0" + immediate "^3.2.3" level-concat-iterator "~2.0.0" + level-supports "~1.0.0" xtend "~4.0.0" +abstract-leveldown@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz#08d19d4e26fb5be426f7a57004851b39e1795a2e" + integrity sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ== + dependencies: + buffer "^6.0.3" + catering "^2.0.0" + is-buffer "^2.0.5" + level-concat-iterator "^3.0.0" + level-supports "^2.0.1" + queue-microtask "^1.2.3" + abstract-leveldown@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.4.1.tgz#b3bfedb884eb693a12775f0c55e9f0a420ccee64" @@ -5026,6 +5019,21 @@ abstract-leveldown@~2.6.0: dependencies: xtend "~4.0.0" +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~6.0.0, abstract-leveldown@~6.0.1, abstract-leveldown@~6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz#b4b6159343c74b0c5197b2817854782d8f748c4a" + integrity sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q== + dependencies: + level-concat-iterator "~2.0.0" + xtend "~4.0.0" + abstract-logging@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-1.0.0.tgz#8b7deafd310559bc28f77724dd1bb30177278c1b" @@ -5959,13 +5967,6 @@ async@0.9.x: resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= -async@2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" - integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== - dependencies: - lodash "^4.17.11" - async@^1.4.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -6062,7 +6063,7 @@ axios@^0.19.2, axios@^0.21.2: dependencies: follow-redirects "^1.14.0" -babel-code-frame@^6.16.0, babel-code-frame@^6.26.0: +babel-code-frame@^6.16.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= @@ -6071,158 +6072,6 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.26.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-core@^6.0.14, babel-core@^6.26.0: - version "6.26.3" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" - integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== - dependencies: - babel-code-frame "^6.26.0" - babel-generator "^6.26.0" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.26.0" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - convert-source-map "^1.5.1" - debug "^2.6.9" - json5 "^0.5.1" - lodash "^4.17.4" - minimatch "^3.0.4" - path-is-absolute "^1.0.1" - private "^0.1.8" - slash "^1.0.0" - source-map "^0.5.7" - -babel-generator@^6.26.0: - version "6.26.1" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" - integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.17.4" - source-map "^0.5.7" - trim-right "^1.0.1" - -babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" - integrity sha1-zORReto1b0IgvK6KAsKzRvmlZmQ= - dependencies: - babel-helper-explode-assignable-expression "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-call-delegate@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" - integrity sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340= - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-define-map@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" - integrity sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8= - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-explode-assignable-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" - integrity sha1-8luCz33BBDPFX3BZLVdGQArCLKo= - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-function-name@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" - integrity sha1-00dbjAPtmCQqJbSDUasYOZ01gKk= - dependencies: - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-get-function-arity@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" - integrity sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-hoist-variables@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" - integrity sha1-HssnaJydJVE+rbyZFKc/VAi+enY= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-optimise-call-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" - integrity sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-regex@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" - integrity sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI= - dependencies: - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-remap-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" - integrity sha1-XsWBgnrXI/7N04HxySg5BnbkVRs= - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-replace-supers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" - integrity sha1-v22/5Dk40XNpohPKiov3S2qQqxo= - dependencies: - babel-helper-optimise-call-expression "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" - integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" @@ -6247,13 +6096,6 @@ babel-loader@^8.2.2: make-dir "^3.1.0" schema-utils "^2.6.5" -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" - integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= - dependencies: - babel-runtime "^6.22.0" - babel-plugin-add-react-displayname@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" @@ -6267,13 +6109,6 @@ babel-plugin-apply-mdx-type-prop@1.6.22: "@babel/helper-plugin-utils" "7.10.4" "@mdx-js/util" "1.6.22" -babel-plugin-check-es2015-constants@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" - integrity sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o= - dependencies: - babel-runtime "^6.22.0" - babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" @@ -6389,249 +6224,11 @@ babel-plugin-react-docgen@^4.2.1: lodash "^4.17.15" react-docgen "^5.0.0" -babel-plugin-syntax-async-functions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" - integrity sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU= - -babel-plugin-syntax-exponentiation-operator@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" - integrity sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4= - babel-plugin-syntax-jsx@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= -babel-plugin-syntax-trailing-function-commas@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" - integrity sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM= - -babel-plugin-transform-async-to-generator@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" - integrity sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E= - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-functions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-arrow-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" - integrity sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" - integrity sha1-u8UbSflk1wy42OC5ToICRs46YUE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoping@^6.23.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" - integrity sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8= - dependencies: - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-plugin-transform-es2015-classes@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" - integrity sha1-WkxYpQyclGHlZLSyo7+ryXolhNs= - dependencies: - babel-helper-define-map "^6.24.1" - babel-helper-function-name "^6.24.1" - babel-helper-optimise-call-expression "^6.24.1" - babel-helper-replace-supers "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-computed-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" - integrity sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM= - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-destructuring@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" - integrity sha1-mXux8auWf2gtKwh2/jWNYOdlxW0= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-duplicate-keys@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" - integrity sha1-c+s9MQypaePvnskcU3QabxV2Qj4= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-for-of@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" - integrity sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-function-name@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" - integrity sha1-g0yJhTvDaxrw86TF26qU/Y6sqos= - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" - integrity sha1-T1SgLWzWbPkVKAAZox0xklN3yi4= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" - integrity sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ= - dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: - version "6.26.2" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" - integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q== - dependencies: - babel-plugin-transform-strict-mode "^6.24.1" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-types "^6.26.0" - -babel-plugin-transform-es2015-modules-systemjs@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" - integrity sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM= - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-umd@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" - integrity sha1-rJl+YoXNGO1hdq22B9YCNErThGg= - dependencies: - babel-plugin-transform-es2015-modules-amd "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-object-super@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" - integrity sha1-JM72muIcuDp/hgPa0CH1cusnj40= - dependencies: - babel-helper-replace-supers "^6.24.1" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-parameters@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" - integrity sha1-V6w1GrScrxSpfNE7CfZv3wpiXys= - dependencies: - babel-helper-call-delegate "^6.24.1" - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-shorthand-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" - integrity sha1-JPh11nIch2YbvZmkYi5R8U3jiqA= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-spread@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" - integrity sha1-1taKmfia7cRTbIGlQujdnxdG+NE= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-sticky-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" - integrity sha1-AMHNsaynERLN8M9hJsLta0V8zbw= - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-template-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" - integrity sha1-qEs0UPfp+PH2g51taH2oS7EjbY0= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-typeof-symbol@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" - integrity sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-unicode-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" - integrity sha1-04sS9C6nMj9yk4fxinxa4frrNek= - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - regexpu-core "^2.0.0" - -babel-plugin-transform-exponentiation-operator@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" - integrity sha1-KrDJx/MJj6SJB3cruBP+QejeOg4= - dependencies: - babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" - babel-plugin-syntax-exponentiation-operator "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-regenerator@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" - integrity sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8= - dependencies: - regenerator-transform "^0.10.0" - -babel-plugin-transform-strict-mode@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" - integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g= - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -6650,42 +6247,6 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-env@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" - integrity sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg== - dependencies: - babel-plugin-check-es2015-constants "^6.22.0" - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-to-generator "^6.22.0" - babel-plugin-transform-es2015-arrow-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.23.0" - babel-plugin-transform-es2015-classes "^6.23.0" - babel-plugin-transform-es2015-computed-properties "^6.22.0" - babel-plugin-transform-es2015-destructuring "^6.23.0" - babel-plugin-transform-es2015-duplicate-keys "^6.22.0" - babel-plugin-transform-es2015-for-of "^6.23.0" - babel-plugin-transform-es2015-function-name "^6.22.0" - babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.22.0" - babel-plugin-transform-es2015-modules-commonjs "^6.23.0" - babel-plugin-transform-es2015-modules-systemjs "^6.23.0" - babel-plugin-transform-es2015-modules-umd "^6.23.0" - babel-plugin-transform-es2015-object-super "^6.22.0" - babel-plugin-transform-es2015-parameters "^6.23.0" - babel-plugin-transform-es2015-shorthand-properties "^6.22.0" - babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.22.0" - babel-plugin-transform-es2015-template-literals "^6.22.0" - babel-plugin-transform-es2015-typeof-symbol "^6.23.0" - babel-plugin-transform-es2015-unicode-regex "^6.22.0" - babel-plugin-transform-exponentiation-operator "^6.22.0" - babel-plugin-transform-regenerator "^6.22.0" - browserslist "^3.2.6" - invariant "^2.2.2" - semver "^5.3.0" - babel-preset-jest@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" @@ -6694,20 +6255,7 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" - integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= - dependencies: - babel-core "^6.26.0" - babel-runtime "^6.26.0" - core-js "^2.5.0" - home-or-tmp "^2.0.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - source-map-support "^0.4.15" - -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= @@ -6715,60 +6263,11 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: core-js "^2.4.0" regenerator-runtime "^0.11.0" -babel-template@^6.24.1, babel-template@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" - integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= - dependencies: - babel-runtime "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - lodash "^4.17.4" - -babel-traverse@^6.24.1, babel-traverse@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" - integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= - dependencies: - babel-code-frame "^6.26.0" - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - debug "^2.6.8" - globals "^9.18.0" - invariant "^2.2.2" - lodash "^4.17.4" - -babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" - integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" - babelify@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/babelify/-/babelify-10.0.0.tgz#fe73b1a22583f06680d8d072e25a1e0d1d1d7fb5" integrity sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg== -babelify@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" - integrity sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU= - dependencies: - babel-core "^6.0.14" - object-assign "^4.0.0" - -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" - integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== - bach@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" @@ -7013,7 +6512,7 @@ bip32@^2.0.4: typeforce "^1.11.5" wif "^2.0.6" -bip39@2.5.0, bip39@^2.2.0, bip39@^2.4.0: +bip39@^2.2.0, bip39@^2.4.0: version "2.5.0" resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235" integrity sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA== @@ -7151,7 +6650,7 @@ bo-selector@0.0.10: resolved "https://registry.yarnpkg.com/bo-selector/-/bo-selector-0.0.10.tgz#9816dcb00adf374ea87941a863b2acfc026afa3e" integrity sha1-mBbcsArfN06oeUGoY7Ks/AJq+j4= -body-parser@1.19.0, body-parser@^1.16.0: +body-parser@1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== @@ -7399,14 +6898,6 @@ browserify-rsa@^4.0.0: bn.js "^4.1.0" randombytes "^2.0.1" -browserify-sha3@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/browserify-sha3/-/browserify-sha3-0.0.4.tgz#086c47b8c82316c9d47022c26185954576dd8e26" - integrity sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY= - dependencies: - js-sha3 "^0.6.1" - safe-buffer "^5.1.1" - browserify-sign@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" @@ -7550,7 +7041,7 @@ browserslist@4.14.2: escalade "^3.0.2" node-releases "^1.1.61" -browserslist@^3.1.1, browserslist@^3.2.6: +browserslist@^3.1.1: version "3.2.8" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" integrity sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ== @@ -7676,13 +7167,6 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer-xor@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289" - integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ== - dependencies: - safe-buffer "^5.1.1" - buffer@^4.3.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" @@ -7692,7 +7176,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -buffer@^5.0.5, buffer@^5.1.0, buffer@^5.2.1, buffer@^5.4.2, buffer@^5.5.0, buffer@^5.6.0: +buffer@^5.1.0, buffer@^5.2.1, buffer@^5.4.2, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -7716,12 +7200,12 @@ buffer@~5.2.1: base64-js "^1.0.2" ieee754 "^1.1.4" -bufferutil@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.1.tgz#3a177e8e5819a1243fe16b63a199951a7ad8d4a7" - integrity sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA== +bufferutil@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" + integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== dependencies: - node-gyp-build "~3.7.0" + node-gyp-build "^4.3.0" builtin-status-codes@^3.0.0: version "3.0.0" @@ -7755,21 +7239,6 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -bytewise-core@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/bytewise-core/-/bytewise-core-1.2.3.tgz#3fb410c7e91558eb1ab22a82834577aa6bd61d42" - integrity sha1-P7QQx+kVWOsasiqCg0V3qmvWHUI= - dependencies: - typewise-core "^1.2" - -bytewise@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/bytewise/-/bytewise-1.1.0.tgz#1d13cbff717ae7158094aa881b35d081b387253e" - integrity sha1-HRPL/3F65xWAlKqIGzXQgbOHJT4= - dependencies: - bytewise-core "^1.2.2" - typewise "^1.0.3" - cacache@^12.0.2: version "12.0.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.3.tgz#be99abba4e1bf5df461cd5a2c1071fc432573390" @@ -7855,14 +7324,6 @@ cached-path-relative@^1.0.0, cached-path-relative@^1.0.2: resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.2.tgz#a13df4196d26776220cc3356eb147a52dba2c6db" integrity sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg== -cachedown@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cachedown/-/cachedown-1.0.0.tgz#d43f036e4510696b31246d7db31ebf0f7ac32d15" - integrity sha1-1D8DbkUQaWsxJG19sx6/D3rDLRU= - dependencies: - abstract-leveldown "^2.4.1" - lru-cache "^3.2.0" - caching-transform@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" @@ -7997,6 +7458,13 @@ cashaddrjs@0.4.4: dependencies: big-integer "1.6.36" +catering@^2.0.0, catering@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.0.tgz#1354f5e8e231a5b80309302bb23b40624d3212c5" + integrity sha512-M5imwzQn6y+ODBfgi+cfgZv2hIUI6oYU/0f35Mdb1ujGeqeoI5tOnl9Q13DTH7LW+7er+NYq8stNOKZD/Z3U/A== + dependencies: + queue-tick "^1.0.0" + cbor-sync@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/cbor-sync/-/cbor-sync-1.0.4.tgz#5a11a1ab75c2a14d1af1b237fd84aa8c1593662f" @@ -8503,16 +7971,16 @@ clone@2.1.1: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs= -clone@2.1.2, clone@^2.0.0, clone@^2.1.1, clone@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - clone@^1.0.0, clone@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" integrity sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8= +clone@^2.0.0, clone@^2.1.1, clone@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + cloneable-readable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.0.0.tgz#a6290d413f217a61232f95e458ff38418cfb0117" @@ -8754,7 +8222,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0, concat-stream@^1.5.1, concat-stream@^1.5.2, concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@^1.6.2, concat-stream@~1.6.0: +concat-stream@^1.5.0, concat-stream@^1.5.2, concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@^1.6.2, concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -8867,15 +8335,6 @@ content-disposition@0.5.3, content-disposition@~0.5.2: dependencies: safe-buffer "5.1.2" -content-hash@^2.5.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" - integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== - dependencies: - cids "^0.7.1" - multicodec "^0.5.5" - multihashes "^0.4.15" - content-type-parser@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" @@ -9000,12 +8459,12 @@ core-js-compat@^3.16.0, core-js-compat@^3.16.2, core-js-compat@^3.8.1: browserslist "^4.17.3" semver "7.0.0" -core-js-pure@^3.0.0, core-js-pure@^3.0.1, core-js-pure@^3.8.2: +core-js-pure@^3.0.0, core-js-pure@^3.8.2: version "3.18.3" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.18.3.tgz#7eed77dcce1445ab68fd68715856633e2fb3b90c" integrity sha512-qfskyO/KjtbYn09bn1IPkuhHl5PlJ6IzJ9s9sraJ1EqcuGyLGKzhSM1cY0zgyL9hx42eulQLZ6WaeK5ycJCkqw== -core-js@^2.4.0, core-js@^2.5.0: +core-js@^2.4.0: version "2.6.11" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== @@ -9020,14 +8479,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cors@^2.8.1: - version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -9149,7 +8600,7 @@ cross-fetch@2.2.2: node-fetch "2.1.2" whatwg-fetch "2.0.4" -cross-fetch@^2.1.0, cross-fetch@^2.1.1: +cross-fetch@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== @@ -9198,7 +8649,7 @@ crypt@0.0.2: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -crypto-browserify@3.12.0, crypto-browserify@^3.0.0, crypto-browserify@^3.11.0: +crypto-browserify@^3.0.0, crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== @@ -9498,7 +8949,7 @@ debug-fabulous@^1.0.0: memoizee "0.4.X" object-assign "4.X" -debug@2, debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -9575,7 +9026,7 @@ decomment@^0.9.3: dependencies: esprima "4.0.1" -decompress-response@^3.2.0, decompress-response@^3.3.0: +decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= @@ -9738,7 +9189,7 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -defined@^1.0.0, defined@~1.0.0: +defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= @@ -9907,13 +9358,6 @@ detect-file@^1.0.0: resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" - integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= - dependencies: - repeating "^2.0.0" - detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -10471,6 +9915,11 @@ elliptic@6.5.3, elliptic@6.5.4, elliptic@=3.0.3, elliptic@^6.0.0, elliptic@^6.4. minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emittery@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.0.tgz#bb373c660a9d421bb44706ec4967ed50c02a8026" + integrity sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ== + emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" @@ -10510,17 +9959,6 @@ encodeurl@^1.0.2, encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding-down@5.0.4, encoding-down@~5.0.0: - version "5.0.4" - resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-5.0.4.tgz#1e477da8e9e9d0f7c8293d320044f8b2cd8e9614" - integrity sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw== - dependencies: - abstract-leveldown "^5.0.0" - inherits "^2.0.3" - level-codec "^9.0.0" - level-errors "^2.0.0" - xtend "^4.0.1" - encoding-down@^6.0.2: version "6.1.0" resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.1.0.tgz#7c0dceb10cc12e7db30abf84db62ce48079672fc" @@ -10531,6 +9969,17 @@ encoding-down@^6.0.2: level-codec "^9.0.0" level-errors "^2.0.0" +encoding-down@~5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-5.0.4.tgz#1e477da8e9e9d0f7c8293d320044f8b2cd8e9614" + integrity sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw== + dependencies: + abstract-leveldown "^5.0.0" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + xtend "^4.0.1" + encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" @@ -10736,7 +10185,7 @@ error@^7.0.0: string-template "~0.2.1" xtend "~4.0.0" -es-abstract@^1.13.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.9.0: +es-abstract@^1.13.0, es-abstract@^1.17.0-next.1, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.4.3, es-abstract@^1.9.0: version "1.18.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== @@ -10767,7 +10216,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.14, es5-ext@^0.10.30, es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2: +es5-ext@^0.10.14, es5-ext@^0.10.30, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2: version "0.10.51" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.51.tgz#ed2d7d9d48a12df86e0299287e93a09ff478842f" integrity sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ== @@ -11307,19 +10756,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eth-block-tracker@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz#95cd5e763c7293e0b1b2790a2a39ac2ac188a5e1" - integrity sha512-WUVxWLuhMmsfenfZvFO5sbl1qFY2IqUlw/FPVmjjdElpqLsZtSG+wPe9Dz7W/sB6e80HgFKknOmKk2eNlznHug== - dependencies: - eth-query "^2.1.0" - ethereumjs-tx "^1.3.3" - ethereumjs-util "^5.1.3" - ethjs-util "^0.1.3" - json-rpc-engine "^3.6.0" - pify "^2.3.0" - tape "^4.6.3" - eth-block-tracker@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-4.4.2.tgz#cd25f5536e3f003f677364be630b9879d3aa69c1" @@ -11341,14 +10777,6 @@ eth-block-tracker@^5.0.1: json-rpc-random-id "^1.0.1" pify "^3.0.0" -eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" - integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= - dependencies: - idna-uts46-hx "^2.3.1" - js-sha3 "^0.5.7" - eth-ens-namehash@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-1.0.2.tgz#05ecdd6bac2d7fd7bc5ca84a993c6bad9da4edb9" @@ -11357,6 +10785,14 @@ eth-ens-namehash@^1.0.2: idna-uts46 "^1.0.1" js-sha3 "^0.5.7" +eth-ens-namehash@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + eth-hd-keyring@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/eth-hd-keyring/-/eth-hd-keyring-3.6.0.tgz#6835d30aa411b8d3ef098e82f6427b5325082abb" @@ -11380,17 +10816,6 @@ eth-json-rpc-filters@^4.2.1: lodash.flatmap "^4.5.0" safe-event-emitter "^1.0.1" -eth-json-rpc-infura@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.0.tgz#62c3f516b51351038c32a548704467cec113ca8f" - integrity sha512-FLcpdxPRVBCUc7yoE+wHGvyYg2lATedP+/q7PsKvaSzQpJbgTG4ZjLnyrLanxDr6M1k/dSNa6V5QnILwjUKJcw== - dependencies: - cross-fetch "^2.1.1" - eth-json-rpc-middleware "^1.5.0" - json-rpc-engine "^3.4.0" - json-rpc-error "^2.0.0" - tape "^4.8.0" - eth-json-rpc-infura@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-5.1.0.tgz#e6da7dc47402ce64c54e7018170d89433c4e8fb6" @@ -11401,25 +10826,6 @@ eth-json-rpc-infura@^5.1.0: json-rpc-engine "^5.3.0" node-fetch "^2.6.0" -eth-json-rpc-middleware@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz#5c9d4c28f745ccb01630f0300ba945f4bef9593f" - integrity sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q== - dependencies: - async "^2.5.0" - eth-query "^2.1.2" - eth-tx-summary "^3.1.2" - ethereumjs-block "^1.6.0" - ethereumjs-tx "^1.3.3" - ethereumjs-util "^5.1.2" - ethereumjs-vm "^2.1.0" - fetch-ponyfill "^4.0.0" - json-rpc-engine "^3.6.0" - json-rpc-error "^2.0.0" - json-stable-stringify "^1.0.1" - promise-to-callback "^1.0.0" - tape "^4.6.3" - eth-json-rpc-middleware@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-6.0.0.tgz#4fe16928b34231a2537856f08a5ebbc3d0c31175" @@ -11487,19 +10893,6 @@ eth-lib@0.2.8: elliptic "^6.4.0" xhr-request-promise "^0.1.2" -eth-lib@^0.1.26: - version "0.1.27" - resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" - integrity sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA== - dependencies: - bn.js "^4.11.6" - elliptic "^6.4.0" - keccakjs "^0.2.1" - nano-json-stream-parser "^0.1.2" - servify "^0.1.12" - ws "^3.0.0" - xhr-request-promise "^0.1.2" - eth-method-registry@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eth-method-registry/-/eth-method-registry-1.1.0.tgz#3cc01bd23dcf513428d14a0bb19910652cc5cac0" @@ -11521,7 +10914,7 @@ eth-phishing-detect@^1.1.14: dependencies: fast-levenshtein "^2.0.6" -eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: +eth-query@^2.1.0, eth-query@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= @@ -11591,32 +10984,6 @@ eth-trezor-keyring@^0.9.1: hdkey "0.8.0" trezor-connect "8.2.3-extended" -eth-tx-summary@^3.1.2: - version "3.2.4" - resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz#e10eb95eb57cdfe549bf29f97f1e4f1db679035c" - integrity sha512-NtlDnaVZah146Rm8HMRUNMgIwG/ED4jiqk0TME9zFheMl1jOp6jL1m0NKGjJwehXQ6ZKCPr16MTr+qspKpEXNg== - dependencies: - async "^2.1.2" - clone "^2.0.0" - concat-stream "^1.5.1" - end-of-stream "^1.1.0" - eth-query "^2.0.2" - ethereumjs-block "^1.4.1" - ethereumjs-tx "^1.1.1" - ethereumjs-util "^5.0.1" - ethereumjs-vm "^2.6.0" - through2 "^2.0.3" - -ethashjs@~0.0.7: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ethashjs/-/ethashjs-0.0.8.tgz#227442f1bdee409a548fb04136e24c874f3aa6f9" - integrity sha512-/MSbf/r2/Ld8o0l15AymjOTlPqpN8Cr4ByUEA9GtR4x0yAh3TdtDzEg29zMjXCNPI7u6E5fOQdj/Cf9Tc7oVNw== - dependencies: - async "^2.1.2" - buffer-xor "^2.0.1" - ethereumjs-util "^7.0.2" - miller-rabin "^4.0.0" - ethereum-bloom-filters@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.7.tgz#b7b80735e385dbb7f944ce6b4533e24511306060" @@ -11675,15 +11042,6 @@ ethereumjs-abi@0.6.8, ethereumjs-abi@^0.6.4, ethereumjs-abi@^0.6.8: bn.js "^4.11.8" ethereumjs-util "^6.0.0" -ethereumjs-account@3.0.0, ethereumjs-account@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-3.0.0.tgz#728f060c8e0c6e87f1e987f751d3da25422570a9" - integrity sha512-WP6BdscjiiPkQfF9PVfMcwx/rDvfZTjFKY0Uwc09zSQr9JfIVH87dYIJu0gNhBhpmovV4yq295fdllS925fnBA== - dependencies: - ethereumjs-util "^6.0.0" - rlp "^2.2.1" - safe-buffer "^5.1.1" - ethereumjs-account@^2.0.3, ethereumjs-account@^2.0.4: version "2.0.5" resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" @@ -11693,18 +11051,7 @@ ethereumjs-account@^2.0.3, ethereumjs-account@^2.0.4: rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-block@2.2.2, ethereumjs-block@^2.1.0, ethereumjs-block@^2.2.2, ethereumjs-block@~2.2.0, ethereumjs-block@~2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" - integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== - dependencies: - async "^2.0.1" - ethereumjs-common "^1.5.0" - ethereumjs-tx "^2.1.1" - ethereumjs-util "^5.0.0" - merkle-patricia-tree "^2.1.2" - -ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0: +ethereumjs-block@^1.2.2: version "1.7.1" resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== @@ -11715,41 +11062,23 @@ ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0: ethereumjs-util "^5.0.0" merkle-patricia-tree "^2.1.2" -ethereumjs-blockchain@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/ethereumjs-blockchain/-/ethereumjs-blockchain-4.0.3.tgz#e013034633a30ad2006728e8e2b21956b267b773" - integrity sha512-0nJWbyA+Gu0ZKZr/cywMtB/77aS/4lOVsIKbgUN2sFQYscXO5rPbUfrEe7G2Zhjp86/a0VqLllemDSTHvx3vZA== +ethereumjs-block@^2.1.0, ethereumjs-block@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== dependencies: - async "^2.6.1" - ethashjs "~0.0.7" - ethereumjs-block "~2.2.2" + async "^2.0.1" ethereumjs-common "^1.5.0" - ethereumjs-util "~6.1.0" - flow-stoplight "^1.0.0" - level-mem "^3.0.1" - lru-cache "^5.1.1" - rlp "^2.2.2" - semaphore "^1.1.0" - -ethereumjs-common@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.0.tgz#d3e82fc7c47c0cef95047f431a99485abc9bb1cd" - integrity sha512-SZOjgK1356hIY7MRj3/ma5qtfr/4B5BL+G4rP/XSMYr2z1H5el4RX5GReYCKmQmYI/nSBmRnwrZ17IfHuG0viQ== + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" -ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: +ethereumjs-common@^1.1.0, ethereumjs-common@^1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.1.tgz#4e75042473a64daec0ed9fe84323dd9576aa5dba" integrity sha512-aVUPRLgmXORGXXEVkFYgPhr9TGtpBY2tGhZ9Uh0A3lIUzUDr1x6kQx33SbjPUkLkX3eniPQnIL/2psjkjrOfcQ== -ethereumjs-tx@2.1.2, ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" - integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== - dependencies: - ethereumjs-common "^1.5.0" - ethereumjs-util "^6.0.0" - -ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: +ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.3: version "1.3.7" resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== @@ -11757,20 +11086,15 @@ ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@ ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" -ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" - integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== +ethereumjs-tx@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== dependencies: - "@types/bn.js" "^4.11.3" - bn.js "^4.11.0" - create-hash "^1.1.2" - elliptic "^6.5.2" - ethereum-cryptography "^0.1.3" - ethjs-util "0.1.6" - rlp "^2.2.3" + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" -ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#3e0c0d1741471acf1036052d048623dee54ad642" integrity sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA== @@ -11783,6 +11107,19 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum safe-buffer "^5.1.1" secp256k1 "^3.0.1" +ethereumjs-util@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.7, ethereumjs-util@^7.0.8, ethereumjs-util@^7.0.9, ethereumjs-util@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.0.tgz#e2b43a30bfcdbcb432a4eb42bd5f2393209b3fd5" @@ -11795,41 +11132,7 @@ ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.7, ethereu ethjs-util "0.1.6" rlp "^2.2.4" -ethereumjs-util@~6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz#e9c51e5549e8ebd757a339cc00f5380507e799c8" - integrity sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q== - dependencies: - bn.js "^4.11.0" - create-hash "^1.1.2" - ethjs-util "0.1.6" - keccak "^1.0.2" - rlp "^2.0.0" - safe-buffer "^5.1.1" - secp256k1 "^3.0.1" - -ethereumjs-vm@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-4.2.0.tgz#e885e861424e373dbc556278f7259ff3fca5edab" - integrity sha512-X6qqZbsY33p5FTuZqCnQ4+lo957iUJMM6Mpa6bL4UW0dxM6WmDSHuI4j/zOp1E2TDKImBGCJA9QPfc08PaNubA== - dependencies: - async "^2.1.2" - async-eventemitter "^0.2.2" - core-js-pure "^3.0.1" - ethereumjs-account "^3.0.0" - ethereumjs-block "^2.2.2" - ethereumjs-blockchain "^4.0.3" - ethereumjs-common "^1.5.0" - ethereumjs-tx "^2.1.2" - ethereumjs-util "^6.2.0" - fake-merkle-patricia-tree "^1.0.1" - functional-red-black-tree "^1.0.1" - merkle-patricia-tree "^2.3.2" - rustbn.js "~0.2.0" - safe-buffer "^5.1.1" - util.promisify "^1.0.0" - -ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: +ethereumjs-vm@^2.3.4: version "2.6.0" resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== @@ -11846,7 +11149,7 @@ ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: rustbn.js "~0.2.0" safe-buffer "^5.1.1" -ethereumjs-wallet@0.6.5, ethereumjs-wallet@^0.6.4: +ethereumjs-wallet@^0.6.4: version "0.6.5" resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz#685e9091645cee230ad125c007658833991ed474" integrity sha512-MDwjwB9VQVnpp/Dc1XzA6J1a3wgHQ4hSvA1uWNatdpOrtCbPVuQSKSyRnjLvS0a+KKMw2pvQ9Ybqpb3+eW8oNA== @@ -12162,11 +11465,6 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" - integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== - eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -12334,7 +11632,7 @@ explain-error@^1.0.4: resolved "https://registry.yarnpkg.com/explain-error/-/explain-error-1.0.4.tgz#a793d3ac0cad4c6ab571e9968fbbab6cb2532929" integrity sha1-p5PTrAytTGq1cemWj7urbLJTKSk= -express@^4.14.0, express@^4.17.1: +express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -12631,13 +11929,6 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fetch-ponyfill@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" - integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= - dependencies: - node-fetch "~1.7.1" - figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -12882,14 +12173,6 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find-yarn-workspace-root@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" - integrity sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q== - dependencies: - fs-extra "^4.0.3" - micromatch "^3.1.4" - find-yarn-workspace-root@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" @@ -13004,11 +12287,6 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== -flow-stoplight@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" - integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s= - fluent-syntax@0.13.0: version "0.13.0" resolved "https://registry.yarnpkg.com/fluent-syntax/-/fluent-syntax-0.13.0.tgz#417144d99cba94ff474c422b3e6623d5a842855a" @@ -13027,12 +12305,12 @@ fnv1a@^1.0.1: resolved "https://registry.yarnpkg.com/fnv1a/-/fnv1a-1.0.1.tgz#915e2d6d023c43d5224ad9f6d2a3c4156f5712f5" integrity sha1-kV4tbQI8Q9UiStn20qPEFW9XEvU= -follow-redirects@^1.14.0, follow-redirects@^1.14.7: +follow-redirects@^1.14.0: version "1.14.7" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== -for-each@^0.3.3, for-each@~0.3.3: +for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== @@ -13192,15 +12470,6 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^4.0.2, fs-extra@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -13316,7 +12585,7 @@ ftp@^0.3.10: readable-stream "1.1.x" xregexp "2.0.0" -function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.1: +function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== @@ -13346,51 +12615,19 @@ fuse.js@^3.2.0, fuse.js@^3.6.1: resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c" integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw== -ganache-cli@^6.12.1: - version "6.12.1" - resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.12.1.tgz#148cf6541494ef1691bd68a77e4414981910cb3e" - integrity sha512-zoefZLQpQyEJH9jgtVYgM+ENFLAC9iwys07IDCsju2Ieq9KSTLH89RxSP4bhizXKV/h/+qaWpfyCBGWnBfqgIQ== - dependencies: - ethereumjs-util "6.2.1" - source-map-support "0.5.12" - yargs "13.2.4" - -ganache-core@^2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.13.1.tgz#bf60399a2dd084e1090db91cbbc7ed3885dc01e4" - integrity sha512-Ewg+kNcDqXtOohe7jCcP+ZUv9EMzOx2MoqOYYP3BCfxrDh3KjBXXaKK+Let7li0TghAs9lxmBgevZku35j5YzA== - dependencies: - abstract-leveldown "3.0.0" - async "2.6.2" - bip39 "2.5.0" - cachedown "1.0.0" - clone "2.1.2" - debug "3.2.6" - encoding-down "5.0.4" - eth-sig-util "^2.0.0" - ethereumjs-abi "0.6.8" - ethereumjs-account "3.0.0" - ethereumjs-block "2.2.2" - ethereumjs-common "1.5.0" - ethereumjs-tx "2.1.2" - ethereumjs-util "6.2.1" - ethereumjs-vm "4.2.0" - heap "0.2.6" +ganache@^v7.0.0-rc.0: + version "7.0.0-rc.0" + resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.0.0-rc.0.tgz#250a588ba23a98ec2ed39b192733d8b9ec964e6f" + integrity sha512-4VNB0rhUJBTq3aGuQm4I/PHk/DkiJWfv/odguY40/G4BTgefhksK2miy615LM791iUKeNYnllBuOmd17xQhH/w== + dependencies: + "@trufflesuite/bigint-buffer" "1.1.9" + emittery "0.10.0" keccak "3.0.1" - level-sublevel "6.6.4" - levelup "3.1.1" - lodash "4.17.20" - lru-cache "5.1.1" - merkle-patricia-tree "3.0.0" - patch-package "6.2.2" - seedrandom "3.0.1" - source-map-support "0.5.12" - tmp "0.1.0" - web3-provider-engine "14.2.1" - websocket "1.0.32" + leveldown "6.1.0" + secp256k1 "4.0.2" optionalDependencies: - ethereumjs-wallet "0.6.5" - web3 "1.2.11" + bufferutil "4.0.5" + utf-8-validate "5.0.7" gar@^1.0.4: version "1.0.4" @@ -13710,7 +12947,7 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.4: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -13823,7 +13060,7 @@ globals@^13.6.0: dependencies: type-fest "^0.20.2" -globals@^9.14.0, globals@^9.18.0: +globals@^9.14.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== @@ -13944,23 +13181,6 @@ got@5.6.0: unzip-response "^1.0.0" url-parse-lax "^1.0.0" -got@9.6.0, got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -13978,25 +13198,22 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -got@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== dependencies: - decompress-response "^3.2.0" + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.6" @@ -14293,23 +13510,11 @@ has-localstorage@^1.0.1: resolved "https://registry.yarnpkg.com/has-localstorage/-/has-localstorage-1.0.1.tgz#fe62406c4767fbd6d784dac6905928108b82971b" integrity sha1-/mJAbEdn+9bXhNrGkFkoEIuClxs= -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== - dependencies: - has-symbol-support-x "^1.4.1" - has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -14351,7 +13556,7 @@ has-yarn@^2.1.0: resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== -has@^1.0.0, has@^1.0.1, has@^1.0.3, has@~1.0.3: +has@^1.0.0, has@^1.0.1, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -14499,7 +13704,7 @@ header-case@^1.0.0: no-case "^2.2.0" upper-case "^1.1.3" -heap@0.2.6, heap@~0.2.6: +heap@~0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= @@ -14559,14 +13764,6 @@ hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react- dependencies: react-is "^16.7.0" -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" - integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" - homedir-polyfill@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" @@ -14700,11 +13897,6 @@ http-errors@^1.6.3, http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-https@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" - integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= - http-parser-js@>=0.4.0: version "0.4.9" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.9.tgz#ea1a04fb64adff0242e9974f297dd4c3cad271e1" @@ -15091,7 +14283,7 @@ interpret@^2.2.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== -invariant@2, invariant@2.2.4, invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@2, invariant@2.2.4, invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -15103,11 +14295,6 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= -invert-kv@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" - integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== - invert-kv@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-3.0.0.tgz#9db0c4817a1ec754df8067df6acf6828286f6a84" @@ -15755,10 +14942,10 @@ is-buffer@^1.0.2, is-buffer@^1.1.0, is-buffer@^1.1.5, is-buffer@~1.1.6: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.0, is-buffer@^2.0.3, is-buffer@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" - integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== +is-buffer@^2.0.0, is-buffer@^2.0.3, is-buffer@^2.0.5, is-buffer@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" @@ -15885,13 +15072,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - is-fn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" @@ -16257,7 +15437,7 @@ is-ssh@^1.3.0: dependencies: protocols "^1.1.0" -is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= @@ -16505,14 +15685,6 @@ istanbul-reports@^3.0.0, istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - jake@^10.6.1: version "10.8.2" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b" @@ -16941,11 +16113,6 @@ js-sha3@0.8.0, js-sha3@^0.8.0, js-sha3@~0.8.0: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -js-sha3@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.6.1.tgz#5b89f77a7477679877f58c4a075240934b1f95c0" - integrity sha1-W4n3enR3Z5h39YxKB1JAk0sflcA= - js-sha3@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.7.0.tgz#0a5c57b36f79882573b2d84051f8bb85dd1bd63a" @@ -17070,11 +16237,6 @@ jsdom@^16.4.0: ws "^7.4.6" xml-name-validator "^3.0.0" -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" - integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -17114,18 +16276,6 @@ json-pointer@^0.6.0: dependencies: foreach "^2.0.4" -json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz#9d4ff447241792e1d0a232f6ef927302bb0c62a9" - integrity sha512-6QNcvm2gFuuK4TKU1uwfH0Qd/cOSb9c1lls0gbnIhciktIUQJwz6NQNAW4B1KiGPenv7IKu97V222Yo1bNhGuA== - dependencies: - async "^2.0.1" - babel-preset-env "^1.7.0" - babelify "^7.3.0" - json-rpc-error "^2.0.0" - promise-to-callback "^1.0.0" - safe-event-emitter "^1.0.1" - json-rpc-engine@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.3.0.tgz#7dc7291766b28766ebda33eb6d3f4c6301c44ff4" @@ -17142,13 +16292,6 @@ json-rpc-engine@^6.1.0: "@metamask/safe-event-emitter" "^2.0.0" eth-rpc-errors "^4.0.2" -json-rpc-error@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/json-rpc-error/-/json-rpc-error-2.0.0.tgz#a7af9c202838b5e905c7250e547f1aff77258a02" - integrity sha1-p6+cICg4tekFxyUOVH8a/3cligI= - dependencies: - inherits "^2.0.1" - json-rpc-middleware-stream@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/json-rpc-middleware-stream/-/json-rpc-middleware-stream-2.1.1.tgz#06e5409e201e7ddeae47bef29f7059eafd4d5325" @@ -17221,11 +16364,6 @@ json-text-sequence@~0.1.0: dependencies: delimit-stream "0.1.0" -json5@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - json5@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" @@ -17460,14 +16598,6 @@ keccak@^3.0.0, keccak@^3.0.2: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keccakjs@^0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/keccakjs/-/keccakjs-0.2.3.tgz#5e4e969ce39689a3861f445d7752ee3477f9fe72" - integrity sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg== - dependencies: - browserify-sha3 "^0.0.4" - sha3 "^1.2.2" - keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" @@ -17763,13 +16893,6 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lcid@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" - integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== - dependencies: - invert-kv "^2.0.0" - lcid@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-3.0.0.tgz#87b78ec4cdade463aa6380fd3d75972e07d0d20b" @@ -17808,6 +16931,13 @@ level-codec@~7.0.0: resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== +level-concat-iterator@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-3.1.0.tgz#5235b1f744bc34847ed65a50548aa88d22e881cf" + integrity sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ== + dependencies: + catering "^2.1.0" + level-concat-iterator@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" @@ -17834,15 +16964,6 @@ level-errors@~1.0.3: dependencies: errno "~0.1.1" -level-iterator-stream@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-2.0.3.tgz#ccfff7c046dcf47955ae9a86f46dfa06a31688b4" - integrity sha512-I6Heg70nfF+e5Y3/qfthJFexhRw/Gi3bIymCoXAlijZdAcLaPuWSJs3KXyTYf23ID6g0o2QF62Yh+grOXY3Rig== - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.5" - xtend "^4.0.0" - level-iterator-stream@~1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" @@ -17907,28 +17028,17 @@ level-packager@~4.0.0: encoding-down "~5.0.0" levelup "^3.0.0" -level-post@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/level-post/-/level-post-1.0.7.tgz#19ccca9441a7cc527879a0635000f06d5e8f27d0" - integrity sha512-PWYqG4Q00asOrLhX7BejSajByB4EmG2GaKHfj3h5UmmZ2duciXLPGYWIjBzLECFWUGOZWlm5B20h/n3Gs3HKew== - dependencies: - ltgt "^2.1.2" +level-supports@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-2.1.0.tgz#9af908d853597ecd592293b2fad124375be79c5f" + integrity sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA== -level-sublevel@6.6.4: - version "6.6.4" - resolved "https://registry.yarnpkg.com/level-sublevel/-/level-sublevel-6.6.4.tgz#f7844ae893919cd9d69ae19d7159499afd5352ba" - integrity sha512-pcCrTUOiO48+Kp6F1+UAzF/OtWqLcQVTVF39HLdZ3RO8XBoXt+XVPKZO1vVr1aUoxHZA9OtD2e1v7G+3S5KFDA== +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== dependencies: - bytewise "~1.1.0" - level-codec "^9.0.0" - level-errors "^2.0.0" - level-iterator-stream "^2.0.3" - ltgt "~2.1.1" - pull-defer "^0.2.2" - pull-level "^2.0.3" - pull-stream "^3.6.8" - typewiselite "~1.0.0" - xtend "~4.0.0" + xtend "^4.0.2" level-ws@0.0.0: version "0.0.0" @@ -17947,13 +17057,22 @@ level-ws@^1.0.0: readable-stream "^2.2.8" xtend "^4.0.1" -leveldown@^5.0.0, leveldown@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.1.1.tgz#5d3a043f0ec76e91e189117ec3627bef0436c0dc" - integrity sha512-4n2R/vEA/sssh5TKtFwM9gshW2tirNoURLqekLRUUzuF+eUBLFAufO8UW7bz8lBbG2jw8tQDF3LC+LcUCc12kg== +leveldown@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-6.1.0.tgz#7ab1297706f70c657d1a72b31b40323aa612b9ee" + integrity sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w== dependencies: - abstract-leveldown "~6.0.3" - napi-macros "~1.8.1" + abstract-leveldown "^7.2.0" + napi-macros "~2.0.0" + node-gyp-build "^4.3.0" + +leveldown@^5.0.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" + integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== + dependencies: + abstract-leveldown "~6.2.1" + napi-macros "~2.0.0" node-gyp-build "~4.1.0" leveldown@~5.0.3: @@ -17966,15 +17085,14 @@ leveldown@~5.0.3: napi-macros "~1.8.1" node-gyp-build "~3.8.0" -levelup@3.1.1, levelup@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" - integrity sha512-9N10xRkUU4dShSRRFTBdNaBxofz+PGaIZO962ckboJZiNmLuhVT6FZ6ZKAsICKfUBO76ySaYU6fJWX/jnj3Lcg== +leveldown@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.1.1.tgz#5d3a043f0ec76e91e189117ec3627bef0436c0dc" + integrity sha512-4n2R/vEA/sssh5TKtFwM9gshW2tirNoURLqekLRUUzuF+eUBLFAufO8UW7bz8lBbG2jw8tQDF3LC+LcUCc12kg== dependencies: - deferred-leveldown "~4.0.0" - level-errors "~2.0.0" - level-iterator-stream "~3.0.0" - xtend "~4.0.0" + abstract-leveldown "~6.0.3" + napi-macros "~1.8.1" + node-gyp-build "~4.1.0" levelup@^1.2.1: version "1.3.9" @@ -17989,6 +17107,16 @@ levelup@^1.2.1: semver "~5.4.1" xtend "~4.0.0" +levelup@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" + integrity sha512-9N10xRkUU4dShSRRFTBdNaBxofz+PGaIZO962ckboJZiNmLuhVT6FZ6ZKAsICKfUBO76ySaYU6fJWX/jnj3Lcg== + dependencies: + deferred-leveldown "~4.0.0" + level-errors "~2.0.0" + level-iterator-stream "~3.0.0" + xtend "~4.0.0" + levelup@^4.0.1, levelup@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.1.0.tgz#49ab5d3a341731cd102f91c6bc17a1acb1969a17" @@ -18734,7 +17862,7 @@ lodash.uniqby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= -lodash@4.17.20, lodash@=3.10.1, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.7.0, lodash@~4.17.2: +lodash@=3.10.1, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.7.0, lodash@~4.17.2: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -18785,11 +17913,6 @@ longest-streak@^2.0.1: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.3.tgz#3de7a3f47ee18e9074ded8575b5c091f5d0a4105" integrity sha512-9lz5IVdpwsKLMzQi0MQ+oD9EA0mIGcWYP7jXMTZVXP8D42PwuAk+M/HBFYQoxt1G5OR8m7aSIgb1UymfWGBWEw== -looper@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/looper/-/looper-2.0.0.tgz#66cd0c774af3d4fedac53794f742db56da8f09ec" - integrity sha1-Zs0Md0rz1P7axTeU90LbVtqPCew= - looper@^3.0.0, looper@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/looper/-/looper-3.0.0.tgz#2efa54c3b1cbaba9b94aee2e5914b0be57fbb749" @@ -18852,20 +17975,13 @@ lru-cache@4.1.x, lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@5.1.1, lru-cache@^5.1.1: +lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" -lru-cache@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" - integrity sha1-cXibO39Tmb7IVl3aOKow0qCX7+4= - dependencies: - pseudomap "^1.0.1" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -18892,11 +18008,6 @@ ltgt@^2.1.2, ltgt@~2.2.0: resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= -ltgt@~2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.1.3.tgz#10851a06d9964b971178441c23c9e52698eece34" - integrity sha1-EIUaBtmWS5cReEQcI8nlJpjuzjQ= - luxon@^1.26.0: version "1.26.0" resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.26.0.tgz#d3692361fda51473948252061d0f8561df02b578" @@ -18959,7 +18070,7 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" -map-age-cleaner@^0.1.1, map-age-cleaner@^0.1.3: +map-age-cleaner@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== @@ -19125,15 +18236,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -mem@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - mem@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/mem/-/mem-5.1.1.tgz#7059b67bf9ac2c924c9f1cff7155a064394adfb3" @@ -19285,19 +18387,6 @@ merkle-lib@^2.0.10: resolved "https://registry.yarnpkg.com/merkle-lib/-/merkle-lib-2.0.10.tgz#82b8dbae75e27a7785388b73f9d7725d0f6f3326" integrity sha1-grjbrnXieneFOItz+ddyXQ9vMyY= -merkle-patricia-tree@3.0.0, merkle-patricia-tree@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" - integrity sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ== - dependencies: - async "^2.6.1" - ethereumjs-util "^5.2.0" - level-mem "^3.0.1" - level-ws "^1.0.0" - readable-stream "^3.0.6" - rlp "^2.0.0" - semaphore ">=1.0.1" - merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" @@ -19312,6 +18401,19 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: rlp "^2.0.0" semaphore ">=1.0.1" +merkle-patricia-tree@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" + integrity sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ== + dependencies: + async "^2.6.1" + ethereumjs-util "^5.2.0" + level-mem "^3.0.1" + level-ws "^1.0.0" + readable-stream "^3.0.6" + rlp "^2.0.0" + semaphore ">=1.0.1" + mersenne-twister@^1.0.1, mersenne-twister@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" @@ -19403,7 +18505,7 @@ mime-types@2.1.18: dependencies: mime-db "~1.33.0" -mime-types@^2.1.12, mime-types@^2.1.16, mime-types@^2.1.18, mime-types@^2.1.21, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: +mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.21, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.28" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== @@ -19425,7 +18527,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" integrity sha1-5md4PZLonb00KBi1IwudYqZyrRg= -mimic-fn@^2.0.0, mimic-fn@^2.1.0: +mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -19487,7 +18589,7 @@ minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@~1.2.0: +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -19580,18 +18682,6 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp-promise@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" - integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= - dependencies: - mkdirp "*" - -mkdirp@*, mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -19599,6 +18689,11 @@ mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp dependencies: minimist "^1.2.5" +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mocha@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.2.0.tgz#01cc227b00d875ab1eed03a75106689cfed5a604" @@ -19629,11 +18724,6 @@ mocha@^7.2.0: yargs-parser "13.1.2" yargs-unparser "1.6.0" -mock-fs@^4.1.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.10.1.tgz#50a07a20114a6cdb119f35762f61f46266a1e323" - integrity sha512-w22rOL5ZYu6HbUehB5deurghGM0hS/xBVyHMGKOuQctkk93J9z9VEOhDsiWrXOprVNQpP9uzGKdl8v9mFspKuw== - module-definition@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-3.3.1.tgz#fedef71667713e36988b93d0626a4fe7b35aebfc" @@ -19826,13 +18916,6 @@ multicast-dns@^7.2.0: dns-packet "^4.0.0" thunky "^1.0.2" -multicodec@^0.5.5, multicodec@~0.5.0, multicodec@~0.5.1, multicodec@~0.5.3: - version "0.5.5" - resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.5.tgz#55c2535b44eca9ea40a13771420153fe075bb36d" - integrity sha512-1kOifvwAqp9IdiiTKmpK2tS+LY6GHZdKpk3S2EvW4T32vlwDyA3hJoZtGauzqdedUPVNGChnTksEotVOCVlC+Q== - dependencies: - varint "^5.0.0" - multicodec@^2.1.0: version "2.1.3" resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-2.1.3.tgz#b9850635ad4e2a285a933151b55b4a2294152a5d" @@ -19849,12 +18932,19 @@ multicodec@^3.0.1: uint8arrays "^2.1.5" varint "^6.0.0" +multicodec@~0.5.0, multicodec@~0.5.1, multicodec@~0.5.3: + version "0.5.5" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.5.tgz#55c2535b44eca9ea40a13771420153fe075bb36d" + integrity sha512-1kOifvwAqp9IdiiTKmpK2tS+LY6GHZdKpk3S2EvW4T32vlwDyA3hJoZtGauzqdedUPVNGChnTksEotVOCVlC+Q== + dependencies: + varint "^5.0.0" + multiformats@^9.5.2: version "9.5.2" resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.5.2.tgz#14256e49bac8b6a5ecb558c4d3c347bb94873d65" integrity sha512-nLQ9s7YOVtZdeNOVvCkNyFiZdS3wyq0gvCIvdm7Zy1zw3zBoColJKjMkIPXNdTqT7ruuq+G7HrezIN0cXiAZ0w== -multihashes@^0.4.12, multihashes@^0.4.15, multihashes@~0.4.12, multihashes@~0.4.13, multihashes@~0.4.14, multihashes@~0.4.15: +multihashes@^0.4.12, multihashes@~0.4.12, multihashes@~0.4.13, multihashes@~0.4.14, multihashes@~0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.15.tgz#6dbc55f7f312c6782f5367c03c9783681589d8a6" integrity sha512-G/Smj1GWqw1RQP3dRuRRPe3oyLqvPqUaEDIaoi7JF7Loxl4WAWvhJNk84oyDEodSucv0MmSW/ZT0RKUrsIFD3g== @@ -19988,27 +19078,17 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nan@2.13.2: - version "2.13.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" - integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== - nan@^2.11.1, nan@^2.12.1, nan@^2.13.2, nan@^2.14.0, nan@^2.2.1: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -nano-json-stream-parser@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" - integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= - nanoid@^2.0.0, nanoid@^2.1.6: version "2.1.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== -nanoid@^3.1.12, nanoid@^3.1.23, nanoid@^3.1.28: +nanoid@^3.1.12, nanoid@^3.1.23, nanoid@^3.1.28, nanoid@^3.1.31: version "3.2.0" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== @@ -20041,6 +19121,11 @@ napi-macros@~1.8.1: resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-1.8.2.tgz#299265c1d8aa401351ad0675107d751228c03eda" integrity sha512-Tr0DNY4RzTaBG2W2m3l7ZtFuJChTH6VZhXVhkGGjF/4cZTt+i8GcM9ozD+30Lmr4mDoZ5Xx34t2o4GJqYWDGcg== +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + native-url@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.2.6.tgz#ca1258f5ace169c716ff44eccbddb674e10399ae" @@ -20211,29 +19296,16 @@ node-fetch@2.6.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-fetch@~1.7.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - node-forge@^0.7.1, node-forge@^0.7.5, node-forge@^1.0.0, node-forge@~0.7.6: version "1.2.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.2.1.tgz#82794919071ef2eb5c509293325cec8afd0fd53c" integrity sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w== -node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-build@^4.2.3, node-gyp-build@^4.3.0: +node-gyp-build@4.3.0, node-gyp-build@^4.2.0, node-gyp-build@^4.2.2, node-gyp-build@^4.2.3, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -node-gyp-build@~3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" - integrity sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w== - node-gyp-build@~3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.8.0.tgz#0f57efeb1971f404dfcbfab975c284de7c70f14a" @@ -20643,7 +19715,7 @@ obj-multiplex@^1.0.0: once "^1.4.0" readable-stream "^2.3.3" -object-assign@4.X, object-assign@^4, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@4.X, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -20672,11 +19744,6 @@ object-inspect@^1.6.0, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== -object-inspect@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== - object-is@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" @@ -20749,7 +19816,7 @@ object.entries@^1.0.4, object.entries@^1.1.0, object.entries@^1.1.3: es-abstract "^1.18.0-next.2" has "^1.0.3" -object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: +object.getownpropertydescriptors@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== @@ -20803,13 +19870,6 @@ objectorarray@^1.0.4: resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.4.tgz#d69b2f0ff7dc2701903d308bb85882f4ddb49483" integrity sha512-91k8bjcldstRz1bG6zJo8lWD7c6QXcB4nTDUqiEvIL1xAsLoZlOOZZG+nd6YPz+V7zY1580J4Xxh1vZtyv4i/w== -oboe@2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" - integrity sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY= - dependencies: - http-https "^1.0.0" - obs-store@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/obs-store/-/obs-store-4.0.3.tgz#b632ec7814baa604fae084a4c97e87c0b7a6d14c" @@ -21094,16 +20154,7 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" - integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== - dependencies: - execa "^1.0.0" - lcid "^2.0.0" - mem "^4.0.0" - -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= @@ -21135,11 +20186,6 @@ p-all@^2.1.0: dependencies: p-map "^2.0.0" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -21186,7 +20232,7 @@ p-forever@^1.0.1: resolved "https://registry.yarnpkg.com/p-forever/-/p-forever-1.0.1.tgz#d8da0e9f88b3929e51596c2f8aa50cf2f1ad06ab" integrity sha512-9IVAxJdPk88BFMvPjzE+WTZLmAt/FBa47mYY49E2elBki4yJJmQ57XHu3o3Dm1GMde+Xf2d+PzElJIogAPwkug== -p-is-promise@^2.0.0, p-is-promise@^2.1.0: +p-is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== @@ -21296,13 +20342,6 @@ p-series@^1.1.0: "@sindresorhus/is" "^0.7.0" p-reduce "^1.0.0" -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= - dependencies: - p-finally "^1.0.0" - p-timeout@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" @@ -21590,24 +20629,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= -patch-package@6.2.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.2.2.tgz#71d170d650c65c26556f0d0fbbb48d92b6cc5f39" - integrity sha512-YqScVYkVcClUY0v8fF0kWOjDYopzIM8e3bj/RU1DPeEF14+dCGm6UeOYm4jvCyxqIEQ5/eJzmbWfDWnUleFNMg== - dependencies: - "@yarnpkg/lockfile" "^1.1.0" - chalk "^2.4.2" - cross-spawn "^6.0.5" - find-yarn-workspace-root "^1.2.1" - fs-extra "^7.0.1" - is-ci "^2.0.0" - klaw-sync "^6.0.0" - minimist "^1.2.0" - rimraf "^2.6.3" - semver "^5.6.0" - slash "^2.0.0" - tmp "^0.0.33" - patch-package@^6.4.7: version "6.4.7" resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" @@ -22481,11 +21502,6 @@ prismjs@^1.21.0, prismjs@~1.22.0: optionalDependencies: clipboard "^2.0.0" -private@^0.1.6, private@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== - probe-image-size@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-5.0.0.tgz#1b87d20340ab8fcdb4324ec77fbc8a5f53419878" @@ -22745,7 +21761,7 @@ prr@~1.0.1: resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= -pseudomap@^1.0.1, pseudomap@^1.0.2: +pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= @@ -22824,27 +21840,6 @@ pull-length-prefixed@^1.3.1, pull-length-prefixed@^1.3.2, pull-length-prefixed@^ safe-buffer "^5.1.2" varint "^5.0.0" -pull-level@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pull-level/-/pull-level-2.0.4.tgz#4822e61757c10bdcc7cf4a03af04c92734c9afac" - integrity sha512-fW6pljDeUThpq5KXwKbRG3X7Ogk3vc75d5OQU/TvXXui65ykm+Bn+fiktg+MOx2jJ85cd+sheufPL+rw9QSVZg== - dependencies: - level-post "^1.0.7" - pull-cat "^1.1.9" - pull-live "^1.0.1" - pull-pushable "^2.0.0" - pull-stream "^3.4.0" - pull-window "^2.1.4" - stream-to-pull-stream "^1.7.1" - -pull-live@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/pull-live/-/pull-live-1.0.1.tgz#a4ecee01e330155e9124bbbcf4761f21b38f51f5" - integrity sha1-pOzuAeMwFV6RJLu89HYfIbOPUfU= - dependencies: - pull-cat "^1.1.9" - pull-stream "^3.4.0" - pull-many@^1.0.8: version "1.0.9" resolved "https://registry.yarnpkg.com/pull-many/-/pull-many-1.0.9.tgz#eb78c2e4f5c003525d48e5b1e98fbf4f766c4202" @@ -22936,7 +21931,7 @@ pull-stream-to-stream@^1.3.4: resolved "https://registry.yarnpkg.com/pull-stream-to-stream/-/pull-stream-to-stream-1.3.4.tgz#3f81d8216bd18d2bfd1a198190471180e2738399" integrity sha1-P4HYIWvRjSv9GhmBkEcRgOJzg5k= -pull-stream@^3.2.3, pull-stream@^3.4.0, pull-stream@^3.4.5, pull-stream@^3.5.0, pull-stream@^3.6.13, pull-stream@^3.6.2, pull-stream@^3.6.8, pull-stream@^3.6.9: +pull-stream@^3.2.3, pull-stream@^3.4.5, pull-stream@^3.5.0, pull-stream@^3.6.13, pull-stream@^3.6.2, pull-stream@^3.6.8, pull-stream@^3.6.9: version "3.6.13" resolved "https://registry.yarnpkg.com/pull-stream/-/pull-stream-3.6.13.tgz#181435322841b0397c7ed44ee5a3fae32662ac1b" integrity sha512-enbnbnO+czsPuCq9s9HTTzDzzVQD5TSe60aO3nBioeJ9mevh8RzE4Hxbujo9TReg1fJlmNEL8uyQTUgn8+rSHg== @@ -22970,13 +21965,6 @@ pull-utf8-decoder@^1.0.2: resolved "https://registry.yarnpkg.com/pull-utf8-decoder/-/pull-utf8-decoder-1.0.2.tgz#a7afa2384d1e6415a5d602054126cc8de3bcbce7" integrity sha1-p6+iOE0eZBWl1gIFQSbMjeO8vOc= -pull-window@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/pull-window/-/pull-window-2.1.4.tgz#fc3b86feebd1920c7ae297691e23f705f88552f0" - integrity sha1-/DuG/uvRkgx64pdpHiP3BfiFUvA= - dependencies: - looper "^2.0.0" - pull-ws@^3.3.2, pull-ws@hugomrdias/pull-ws#fix/bundle-size: version "3.3.2" resolved "https://registry.yarnpkg.com/pull-ws/-/pull-ws-3.3.2.tgz#abac497a4800e74ab9a6d749c81a44d12d4e77b3" @@ -23114,6 +22102,16 @@ querystring@0.2.0, querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +queue-microtask@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue-tick@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.0.tgz#011104793a3309ae86bfeddd54e251dc94a36725" + integrity sha512-ULWhjjE8BmiICGn3G8+1L9wFpERNxkf8ysxkAer4+TFdRefDaXOCV5m92aMB9FtBVmn/8sETXLXY6BfW7hyaWQ== + quick-format-unescaped@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-3.0.2.tgz#0137e94d8fb37ffeb70040535111c378e75396fb" @@ -23924,7 +22922,7 @@ regenerate-unicode-properties@^8.2.0: dependencies: regenerate "^1.4.0" -regenerate@^1.2.1, regenerate@^1.4.0: +regenerate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== @@ -23934,15 +22932,6 @@ regenerator-runtime@0.13.3, regenerator-runtime@^0.11.0, regenerator-runtime@^0. resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== -regenerator-transform@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" - integrity sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q== - dependencies: - babel-runtime "^6.18.0" - babel-types "^6.19.0" - private "^0.1.6" - regenerator-transform@^0.14.2: version "0.14.5" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" @@ -23988,15 +22977,6 @@ regexpp@^3.0.0, regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== -regexpu-core@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" - integrity sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA= - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - regexpu-core@^4.7.1: version "4.7.1" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" @@ -24044,23 +23024,11 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -regjsgen@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" - integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= - regjsgen@^0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== -regjsparser@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" - integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= - dependencies: - jsesc "~0.5.0" - regjsparser@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" @@ -24257,13 +23225,6 @@ repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" @@ -24299,7 +23260,7 @@ request-promise-native@^1.0.3: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@^2.79.0, request@^2.83.0, request@^2.85.0, request@^2.88.2, request@~2.88.0: +request@^2.83.0, request@^2.85.0, request@^2.88.2, request@~2.88.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -24478,13 +23439,6 @@ resolve@^2.0.0-next.3: is-core-module "^2.2.0" path-parse "^1.0.6" -resolve@~1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" - integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== - dependencies: - path-parse "^1.0.6" - responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -24508,13 +23462,6 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" -resumer@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" - integrity sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k= - dependencies: - through "~2.3.4" - ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -24642,7 +23589,7 @@ rlp-browser@^1.0.1: dependencies: buffer "^5.4.2" -rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: +rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== @@ -24980,11 +23927,6 @@ secp256k1@^3.0.1, secp256k1@^3.6.1, secp256k1@^3.6.2: nan "^2.14.0" safe-buffer "^5.1.2" -seedrandom@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.1.tgz#eb3dde015bcf55df05a233514e5df44ef9dce083" - integrity sha512-1/02Y/rUeU1CJBAGLebiC5Lbo5FnB22gQbIFFYTLkwvp1xdABZJH1sn4ZT1MzXmPpzv+Rf/Lu2NcsLJiK4rcDg== - select@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" @@ -24999,7 +23941,7 @@ selenium-webdriver@^4.1.0: tmp "^0.2.1" ws ">=7.4.6" -semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: +semaphore@>=1.0.1, semaphore@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== @@ -25133,17 +24075,6 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" -servify@^0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" - integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== - dependencies: - body-parser "^1.16.0" - cors "^2.8.1" - express "^4.14.0" - request "^2.79.0" - xhr "^2.3.3" - ses@^0.12.4: version "0.12.4" resolved "https://registry.yarnpkg.com/ses/-/ses-0.12.4.tgz#f466f7199292b5c4454949c7d497f5569ade5805" @@ -25196,13 +24127,6 @@ sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8, sha.js@~2.4.4: inherits "^2.0.1" safe-buffer "^5.0.1" -sha3@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/sha3/-/sha3-1.2.3.tgz#ed5958fa8331df1b1b8529ca9fdf225a340c5418" - integrity sha512-sOWDZi8cDBRkLfWOw18wvJyNblXDHzwMGnRWut8zNNeIeLnmMRO17bjpLc7OzMuj1ASUgx2IyohzUCAl+Kx5vA== - dependencies: - nan "2.13.2" - shallow-clone@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" @@ -25625,14 +24549,6 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" -source-map-support@0.5.12: - version "0.5.12" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" - integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -25641,13 +24557,6 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" - integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== - dependencies: - source-map "^0.5.6" - source-map-support@^0.5.11, source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20, source-map-support@~0.5.4: version "0.5.20" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" @@ -26010,7 +24919,7 @@ stream-to-blob@^1.0.1: dependencies: once "^1.3.3" -stream-to-pull-stream@^1.7.1, stream-to-pull-stream@^1.7.2, stream-to-pull-stream@^1.7.3: +stream-to-pull-stream@^1.7.2, stream-to-pull-stream@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz#4161aa2d2eb9964de60bfa1af7feaf917e874ece" integrity sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg== @@ -26126,15 +25035,6 @@ string.prototype.trim@^1.1.2: es-abstract "^1.13.0" function-bind "^1.1.1" -string.prototype.trim@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea" - integrity sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.0" - function-bind "^1.0.2" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -26531,23 +25431,6 @@ swappable-obj-proxy@^1.1.0: resolved "https://registry.yarnpkg.com/swappable-obj-proxy/-/swappable-obj-proxy-1.1.0.tgz#fe23c60a0df22499e85d94b71297d9c39ff05fa4" integrity sha512-bXbKO85b0YNbZi/61TjRAbNtY49ABKu7rQ4k2+RFXPL7TA2mphttfqAqCeJ+lrlKlkYc5pvm6erFk6vOWJSpdw== -swarm-js@^0.1.40: - version "0.1.40" - resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" - integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== - dependencies: - bluebird "^3.5.0" - buffer "^5.0.5" - eth-lib "^0.1.26" - fs-extra "^4.0.2" - got "^7.1.0" - mime-types "^2.1.16" - mkdirp-promise "^5.0.1" - mock-fs "^4.1.0" - setimmediate "^1.0.5" - tar "^4.0.2" - xhr-request "^1.0.1" - symbol-observable@^1.2.0, symbol-observable@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" @@ -26619,25 +25502,6 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tape@^4.6.3, tape@^4.8.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.11.0.tgz#63d41accd95e45a23a874473051c57fdbc58edc1" - integrity sha512-yixvDMX7q7JIs/omJSzSZrqulOV51EC9dK8dM0TzImTIkHWfe2/kFyL5v+d9C+SrCMaICk59ujsqFAVidDqDaA== - dependencies: - deep-equal "~1.0.1" - defined "~1.0.0" - for-each "~0.3.3" - function-bind "~1.1.1" - glob "~7.1.4" - has "~1.0.3" - inherits "~2.0.4" - minimist "~1.2.0" - object-inspect "~1.6.0" - resolve "~1.11.1" - resumer "~0.0.0" - string.prototype.trim "~1.1.2" - through "~2.3.8" - tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" @@ -26671,7 +25535,7 @@ tar@6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^4, tar@^4.0.2: +tar@^4: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== @@ -26864,7 +25728,7 @@ through2@^4.0.2: dependencies: readable-stream "3" -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4, through@~2.3.8: +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -26986,13 +25850,6 @@ title-case@^2.1.0: no-case "^2.2.0" upper-case "^1.0.3" -tmp@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" - integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== - dependencies: - rimraf "^2.6.3" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -27035,11 +25892,6 @@ to-data-view@^1.1.0: resolved "https://registry.yarnpkg.com/to-data-view/-/to-data-view-1.1.0.tgz#08d6492b0b8deb9b29bdf1f61c23eadfa8994d00" integrity sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ== -to-fast-properties@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" - integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -27183,11 +26035,6 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - trim-trailing-lines@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.2.tgz#d2f1e153161152e9f02fabc670fb40bec2ea2e3a" @@ -27391,23 +26238,6 @@ typescript@^3.9.7: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== -typewise-core@^1.2, typewise-core@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/typewise-core/-/typewise-core-1.2.0.tgz#97eb91805c7f55d2f941748fa50d315d991ef195" - integrity sha1-l+uRgFx/VdL5QXSPpQ0xXZke8ZU= - -typewise@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/typewise/-/typewise-1.0.3.tgz#1067936540af97937cc5dcf9922486e9fa284651" - integrity sha1-EGeTZUCvl5N8xdz5kiSG6fooRlE= - dependencies: - typewise-core "^1.2.0" - -typewiselite@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typewiselite/-/typewiselite-1.0.0.tgz#c8882fa1bb1092c06005a97f34ef5c8508e3664e" - integrity sha1-yIgvobsQksBgBal/NO9chQjjZk4= - typical@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/typical/-/typical-5.1.0.tgz#7116ca103caf2574985fc84fbaa8fd0ee5ea1684" @@ -27463,11 +26293,6 @@ undeclared-identifiers@^1.1.2: simple-concat "^1.0.0" xtend "^4.0.1" -underscore@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" - integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== - underscore@~1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" @@ -27814,11 +26639,6 @@ url-set-query@^1.0.0: resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= - url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -27876,12 +26696,12 @@ user-home@^2.0.0: dependencies: os-homedir "^1.0.0" -utf-8-validate@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.2.tgz#63cfbccd85dc1f2b66cf7a1d0eebc08ed056bfb3" - integrity sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw== +utf-8-validate@5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" + integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== dependencies: - node-gyp-build "~3.7.0" + node-gyp-build "^4.3.0" utf8-byte-length@^1.0.1: version "1.0.4" @@ -27916,16 +26736,6 @@ util.promisify@1.0.0: define-properties "^1.1.2" object.getownpropertydescriptors "^2.0.3" -util.promisify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - util@0.10.3, util@~0.10.1: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -27977,11 +26787,6 @@ uuid@3.2.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== -uuid@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - uuid@^3.2.1, uuid@^3.2.2, uuid@^3.3.2, uuid@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" @@ -28060,7 +26865,7 @@ varuint-bitcoin@^1.0.4, varuint-bitcoin@^1.1.2: dependencies: safe-buffer "^5.1.1" -vary@^1, vary@^1.1.2, vary@~1.1.2: +vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= @@ -28287,207 +27092,6 @@ web-namespaces@^1.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== -web3-bzz@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.11.tgz#41bc19a77444bd5365744596d778b811880f707f" - integrity sha512-XGpWUEElGypBjeFyUhTkiPXFbDVD6Nr/S5jznE3t8cWUA0FxRf1n3n/NuIZeb0H9RkN2Ctd/jNma/k8XGa3YKg== - dependencies: - "@types/node" "^12.12.6" - got "9.6.0" - swarm-js "^0.1.40" - underscore "1.9.1" - -web3-core-helpers@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.11.tgz#84c681ed0b942c0203f3b324a245a127e8c67a99" - integrity sha512-PEPoAoZd5ME7UfbnCZBdzIerpe74GEvlwT4AjOmHeCVZoIFk7EqvOZDejJHt+feJA6kMVTdd0xzRNN295UhC1A== - dependencies: - underscore "1.9.1" - web3-eth-iban "1.2.11" - web3-utils "1.2.11" - -web3-core-method@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.11.tgz#f880137d1507a0124912bf052534f168b8d8fbb6" - integrity sha512-ff0q76Cde94HAxLDZ6DbdmKniYCQVtvuaYh+rtOUMB6kssa5FX0q3vPmixi7NPooFnbKmmZCM6NvXg4IreTPIw== - dependencies: - "@ethersproject/transactions" "^5.0.0-beta.135" - underscore "1.9.1" - web3-core-helpers "1.2.11" - web3-core-promievent "1.2.11" - web3-core-subscriptions "1.2.11" - web3-utils "1.2.11" - -web3-core-promievent@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.11.tgz#51fe97ca0ddec2f99bf8c3306a7a8e4b094ea3cf" - integrity sha512-il4McoDa/Ox9Agh4kyfQ8Ak/9ABYpnF8poBLL33R/EnxLsJOGQG2nZhkJa3I067hocrPSjEdlPt/0bHXsln4qA== - dependencies: - eventemitter3 "4.0.4" - -web3-core-requestmanager@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.11.tgz#fe6eb603fbaee18530293a91f8cf26d8ae28c45a" - integrity sha512-oFhBtLfOiIbmfl6T6gYjjj9igOvtyxJ+fjS+byRxiwFJyJ5BQOz4/9/17gWR1Cq74paTlI7vDGxYfuvfE/mKvA== - dependencies: - underscore "1.9.1" - web3-core-helpers "1.2.11" - web3-providers-http "1.2.11" - web3-providers-ipc "1.2.11" - web3-providers-ws "1.2.11" - -web3-core-subscriptions@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.11.tgz#beca908fbfcb050c16f45f3f0f4c205e8505accd" - integrity sha512-qEF/OVqkCvQ7MPs1JylIZCZkin0aKK9lDxpAtQ1F8niEDGFqn7DT8E/vzbIa0GsOjL2fZjDhWJsaW+BSoAW1gg== - dependencies: - eventemitter3 "4.0.4" - underscore "1.9.1" - web3-core-helpers "1.2.11" - -web3-core@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.11.tgz#1043cacc1becb80638453cc5b2a14be9050288a7" - integrity sha512-CN7MEYOY5ryo5iVleIWRE3a3cZqVaLlIbIzDPsvQRUfzYnvzZQRZBm9Mq+ttDi2STOOzc1MKylspz/o3yq/LjQ== - dependencies: - "@types/bn.js" "^4.11.5" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-core-requestmanager "1.2.11" - web3-utils "1.2.11" - -web3-eth-abi@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.11.tgz#a887494e5d447c2926d557a3834edd66e17af9b0" - integrity sha512-PkRYc0+MjuLSgg03QVWqWlQivJqRwKItKtEpRUaxUAeLE7i/uU39gmzm2keHGcQXo3POXAbOnMqkDvOep89Crg== - dependencies: - "@ethersproject/abi" "5.0.0-beta.153" - underscore "1.9.1" - web3-utils "1.2.11" - -web3-eth-accounts@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.11.tgz#a9e3044da442d31903a7ce035a86d8fa33f90520" - integrity sha512-6FwPqEpCfKIh3nSSGeo3uBm2iFSnFJDfwL3oS9pyegRBXNsGRVpgiW63yhNzL0796StsvjHWwQnQHsZNxWAkGw== - dependencies: - crypto-browserify "3.12.0" - eth-lib "0.2.8" - ethereumjs-common "^1.3.2" - ethereumjs-tx "^2.1.1" - scrypt-js "^3.0.1" - underscore "1.9.1" - uuid "3.3.2" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-utils "1.2.11" - -web3-eth-contract@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.11.tgz#917065902bc27ce89da9a1da26e62ef663663b90" - integrity sha512-MzYuI/Rq2o6gn7vCGcnQgco63isPNK5lMAan2E51AJLknjSLnOxwNY3gM8BcKoy4Z+v5Dv00a03Xuk78JowFow== - dependencies: - "@types/bn.js" "^4.11.5" - underscore "1.9.1" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-core-promievent "1.2.11" - web3-core-subscriptions "1.2.11" - web3-eth-abi "1.2.11" - web3-utils "1.2.11" - -web3-eth-ens@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.11.tgz#26d4d7f16d6cbcfff918e39832b939edc3162532" - integrity sha512-dbW7dXP6HqT1EAPvnniZVnmw6TmQEKF6/1KgAxbo8iBBYrVTMDGFQUUnZ+C4VETGrwwaqtX4L9d/FrQhZ6SUiA== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - underscore "1.9.1" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-promievent "1.2.11" - web3-eth-abi "1.2.11" - web3-eth-contract "1.2.11" - web3-utils "1.2.11" - -web3-eth-iban@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.11.tgz#f5f73298305bc7392e2f188bf38a7362b42144ef" - integrity sha512-ozuVlZ5jwFC2hJY4+fH9pIcuH1xP0HEFhtWsR69u9uDIANHLPQQtWYmdj7xQ3p2YT4bQLq/axKhZi7EZVetmxQ== - dependencies: - bn.js "^4.11.9" - web3-utils "1.2.11" - -web3-eth-personal@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.11.tgz#a38b3942a1d87a62070ce0622a941553c3d5aa70" - integrity sha512-42IzUtKq9iHZ8K9VN0vAI50iSU9tOA1V7XU2BhF/tb7We2iKBVdkley2fg26TxlOcKNEHm7o6HRtiiFsVK4Ifw== - dependencies: - "@types/node" "^12.12.6" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-net "1.2.11" - web3-utils "1.2.11" - -web3-eth@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.11.tgz#4c81fcb6285b8caf544058fba3ae802968fdc793" - integrity sha512-REvxW1wJ58AgHPcXPJOL49d1K/dPmuw4LjPLBPStOVkQjzDTVmJEIsiLwn2YeuNDd4pfakBwT8L3bz1G1/wVsQ== - dependencies: - underscore "1.9.1" - web3-core "1.2.11" - web3-core-helpers "1.2.11" - web3-core-method "1.2.11" - web3-core-subscriptions "1.2.11" - web3-eth-abi "1.2.11" - web3-eth-accounts "1.2.11" - web3-eth-contract "1.2.11" - web3-eth-ens "1.2.11" - web3-eth-iban "1.2.11" - web3-eth-personal "1.2.11" - web3-net "1.2.11" - web3-utils "1.2.11" - -web3-net@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.11.tgz#eda68ef25e5cdb64c96c39085cdb74669aabbe1b" - integrity sha512-sjrSDj0pTfZouR5BSTItCuZ5K/oZPVdVciPQ6981PPPIwJJkCMeVjD7I4zO3qDPCnBjBSbWvVnLdwqUBPtHxyg== - dependencies: - web3-core "1.2.11" - web3-core-method "1.2.11" - web3-utils "1.2.11" - -web3-provider-engine@14.2.1: - version "14.2.1" - resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.2.1.tgz#ef351578797bf170e08d529cb5b02f8751329b95" - integrity sha512-iSv31h2qXkr9vrL6UZDm4leZMc32SjWJFGOp/D92JXfcEboCqraZyuExDkpxKw8ziTufXieNM7LSXNHzszYdJw== - dependencies: - async "^2.5.0" - backoff "^2.5.0" - clone "^2.0.0" - cross-fetch "^2.1.0" - eth-block-tracker "^3.0.0" - eth-json-rpc-infura "^3.1.0" - eth-sig-util "^1.4.2" - ethereumjs-block "^1.2.2" - ethereumjs-tx "^1.2.0" - ethereumjs-util "^5.1.5" - ethereumjs-vm "^2.3.4" - json-rpc-error "^2.0.0" - json-stable-stringify "^1.0.1" - promise-to-callback "^1.0.0" - readable-stream "^2.2.9" - request "^2.85.0" - semaphore "^1.0.3" - ws "^5.1.1" - xhr "^2.2.0" - xtend "^4.0.1" - web3-provider-engine@^16.0.3: version "16.0.3" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-16.0.3.tgz#8ff93edf3a8da2f70d7f85c5116028c06a0d9f07" @@ -28516,43 +27120,6 @@ web3-provider-engine@^16.0.3: xhr "^2.2.0" xtend "^4.0.1" -web3-providers-http@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.11.tgz#1cd03442c61670572d40e4dcdf1faff8bd91e7c6" - integrity sha512-psh4hYGb1+ijWywfwpB2cvvOIMISlR44F/rJtYkRmQ5jMvG4FOCPlQJPiHQZo+2cc3HbktvvSJzIhkWQJdmvrA== - dependencies: - web3-core-helpers "1.2.11" - xhr2-cookies "1.1.0" - -web3-providers-ipc@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.11.tgz#d16d6c9be1be6e0b4f4536c4acc16b0f4f27ef21" - integrity sha512-yhc7Y/k8hBV/KlELxynWjJDzmgDEDjIjBzXK+e0rHBsYEhdCNdIH5Psa456c+l0qTEU2YzycF8VAjYpWfPnBpQ== - dependencies: - oboe "2.1.4" - underscore "1.9.1" - web3-core-helpers "1.2.11" - -web3-providers-ws@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.11.tgz#a1dfd6d9778d840561d9ec13dd453046451a96bb" - integrity sha512-ZxnjIY1Er8Ty+cE4migzr43zA/+72AF1myzsLaU5eVgdsfV7Jqx7Dix1hbevNZDKFlSoEyq/3j/jYalh3So1Zg== - dependencies: - eventemitter3 "4.0.4" - underscore "1.9.1" - web3-core-helpers "1.2.11" - websocket "^1.0.31" - -web3-shh@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.11.tgz#f5d086f9621c9a47e98d438010385b5f059fd88f" - integrity sha512-B3OrO3oG1L+bv3E1sTwCx66injW1A8hhwpknDUbV+sw3fehFazA06z9SGXUefuFI1kVs4q2vRi0n4oCcI4dZDg== - dependencies: - web3-core "1.2.11" - web3-core-method "1.2.11" - web3-core-subscriptions "1.2.11" - web3-net "1.2.11" - web3-stream-provider@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/web3-stream-provider/-/web3-stream-provider-4.0.0.tgz#a7f01cb9f7b33b6f0295f2475bf3826f6b27fa9d" @@ -28561,20 +27128,6 @@ web3-stream-provider@^4.0.0: readable-stream "^2.0.5" uuid "^3.3.3" -web3-utils@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.11.tgz#af1942aead3fb166ae851a985bed8ef2c2d95a82" - integrity sha512-3Tq09izhD+ThqHEaWYX4VOT7dNPdZiO+c/1QMA0s5X2lDFKK/xHJb7cyTRRVzN2LvlHbR7baS1tmQhSua51TcQ== - dependencies: - bn.js "^4.11.9" - eth-lib "0.2.8" - ethereum-bloom-filters "^1.0.6" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - underscore "1.9.1" - utf8 "3.0.0" - web3-utils@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.5.3.tgz#e914c9320cd663b2a09a5cb920ede574043eb437" @@ -28588,19 +27141,6 @@ web3-utils@1.5.3: randombytes "^2.1.0" utf8 "3.0.0" -web3@1.2.11: - version "1.2.11" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975" - integrity sha512-mjQ8HeU41G6hgOYm1pmeH0mRAeNKJGnJEUzDMoerkpw7QUQT4exVREgF1MYPvL/z6vAshOXei25LE/t/Bxl8yQ== - dependencies: - web3-bzz "1.2.11" - web3-core "1.2.11" - web3-eth "1.2.11" - web3-eth-personal "1.2.11" - web3-net "1.2.11" - web3-shh "1.2.11" - web3-utils "1.2.11" - web3@^0.20.7: version "0.20.7" resolved "https://registry.yarnpkg.com/web3/-/web3-0.20.7.tgz#1605e6d81399ed6f85a471a4f3da0c8be57df2f7" @@ -28745,18 +27285,6 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -websocket@1.0.32, websocket@^1.0.31: - version "1.0.32" - resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.32.tgz#1f16ddab3a21a2d929dec1687ab21cfdc6d3dbb1" - integrity sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q== - dependencies: - bufferutil "^4.0.1" - debug "^2.2.0" - es5-ext "^0.10.50" - typedarray-to-buffer "^3.1.5" - utf-8-validate "^5.0.2" - yaeti "^0.0.6" - whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -28989,7 +27517,7 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -ws@7.1.0, ws@7.4.6, ws@>=7.4.6, ws@^1.1.0, ws@^3.0.0, ws@^5.1.1, ws@^7, ws@^7.2.0, ws@^7.4.0, ws@^7.4.6, ws@~7.4.2: +ws@7.1.0, ws@7.4.6, ws@>=7.4.6, ws@^1.1.0, ws@^5.1.1, ws@^7, ws@^7.2.0, ws@^7.4.0, ws@^7.4.6, ws@~7.4.2: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== @@ -29019,7 +27547,7 @@ xhr-request@^1.0.1: url-set-query "^1.0.0" xhr "^2.0.4" -xhr2-cookies@1.1.0, xhr2-cookies@^1.1.0: +xhr2-cookies@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= @@ -29031,7 +27559,7 @@ xhr2@0.1.3: resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.3.tgz#cbfc4759a69b4a888e78cf4f20b051038757bd11" integrity sha1-y/xHWaabSoiOeM9PILBRA4dXvRE= -xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: +xhr@^2.0.4, xhr@^2.2.0: version "2.5.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.5.0.tgz#bed8d1676d5ca36108667692b74b316c496e49dd" integrity sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ== @@ -29113,11 +27641,6 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== -yaeti@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" - integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= - yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -29138,7 +27661,7 @@ yaml@^1.10.0, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.1, yargs-parser@^13.1.2: +yargs-parser@13.1.2, yargs-parser@^13.1.1, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== @@ -29181,23 +27704,6 @@ yargs-unparser@1.6.0: lodash "^4.17.15" yargs "^13.3.0" -yargs@13.2.4: - version "13.2.4" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" - integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - os-locale "^3.1.0" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.0" - yargs@13.3.2, yargs@^13.2.2, yargs@^13.2.4, yargs@^13.3.0: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"