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

Merge branch 'develop' into feature/17670-cancel-speedup-popover

This commit is contained in:
Matthias Kretschmann 2023-06-01 12:22:03 +01:00 committed by GitHub
commit ac5922eaae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
171 changed files with 6780 additions and 4057 deletions

View File

@ -237,6 +237,7 @@ module.exports = {
],
excludedFiles: [
'app/scripts/controllers/app-state.test.js',
'app/scripts/controllers/mmi-controller.test.js',
'app/scripts/controllers/network/**/*.test.js',
'app/scripts/controllers/permissions/**/*.test.js',
'app/scripts/lib/**/*.test.js',
@ -265,6 +266,7 @@ module.exports = {
files: [
'**/__snapshots__/*.snap',
'app/scripts/controllers/app-state.test.js',
'app/scripts/controllers/mmi-controller.test.js',
'app/scripts/controllers/network/**/*.test.js',
'app/scripts/controllers/network/**/*.test.ts',
'app/scripts/controllers/network/provider-api-tests/*.ts',

View File

@ -8,6 +8,7 @@ module.exports = {
'./app/scripts/controllers/app-state.test.js',
'./app/scripts/controllers/network/**/*.test.js',
'./app/scripts/controllers/permissions/**/*.test.js',
'./app/scripts/controllers/mmi-controller.test.js',
'./app/scripts/constants/error-utils.test.js',
],
recursive: true,

View File

@ -1,5 +1,5 @@
diff --git a/dist/squirrelly.cjs.js b/dist/squirrelly.cjs.js
index 4680ee747900853b9af01b480c749880dedb9824..95dce7623bc115508063a78c7de9ed7b6e4d3d82 100644
index b87580be5da8a9a5af4cf60c8c5acf910c8f9ed2..e037b8a986d331f7075bbd448c80104de8a3453c 100644
--- a/dist/squirrelly.cjs.js
+++ b/dist/squirrelly.cjs.js
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });

View File

@ -6,6 +6,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [10.31.0]
### Added
- Add extra friction to enable eth_sign in advanced settings ([#18848](https://github.com/MetaMask/metamask-extension/pull/18848))
- Fix for wrong type being assigned to the transaction ([#18818](https://github.com/MetaMask/metamask-extension/pull/18818))
- Update Snaps icon in settings search and fix missing icon ([#18803](https://github.com/MetaMask/metamask-extension/pull/18803))
- Update Korean transactions ([#18799](https://github.com/MetaMask/metamask-extension/pull/18799))
- Show Bridge button in TokenOverview component ([#18630](https://github.com/MetaMask/metamask-extension/pull/18630))
- Update trezor-connect to v9. Introduced trezor changes are documented at [trezor-suite](https://github.com/trezor/trezor-suite/blob/develop/packages/connect/CHANGELOG.md). ([#18302](https://github.com/MetaMask/metamask-extension/pull/18302))
### Changed
- Adding new icons ([#18870](https://github.com/MetaMask/metamask-extension/pull/18870))
- Changed Chinese translation for "Average" password strength([#18897](https://github.com/MetaMask/metamask-extension/pull/18897))
- Update Ledger instruction banner for transactions ([#17937](https://github.com/MetaMask/metamask-extension/pull/17937))
- Enable editing L2 gas on optimism ([#18217](https://github.com/MetaMask/metamask-extension/pull/18217))
- Update keystone links ([#18792](https://github.com/MetaMask/metamask-extension/pull/18792))
- Remove Goerli buy link and disable button ([#18137](https://github.com/MetaMask/metamask-extension/pull/18137))
- No long show best quote, only present a list of quotes. ([#19284](https://github.com/MetaMask/metamask-extension/pull/19284))
- [FLASK] Don't show the title on Install/Update when it's loading ([#19012](https://github.com/MetaMask/metamask-extension/pull/19012))
- [FLASK] Add updated version of the Snaps settings UI ([#18438](https://github.com/MetaMask/metamask-extension/pull/18438), ([#18775](https://github.com/MetaMask/metamask-extension/pull/18775)))
### Fixed
- Fix terms of use popover scroll button hiding when scollbar is at the bottom. ([#18843](https://github.com/MetaMask/metamask-extension/pull/18843))
- Fix in approve header to show correct account name ([#18849](https://github.com/MetaMask/metamask-extension/pull/18849))
- Fix error when switching to Linea testnet using wallet_switchEthereumChain ([#18710](https://github.com/MetaMask/metamask-extension/pull/18710))
- Fix for persistant currency conversion on multi layer transactions when the show balance setting is off ([#18833](https://github.com/MetaMask/metamask-extension/pull/18833))
- Show the right "balance needed" value if we fallback from STX to regular Swaps ([#19230](https://github.com/MetaMask/metamask-extension/pull/19230))
- [FLASK] Fix overflowing notification content ([#18881](https://github.com/MetaMask/metamask-extension/pull/18881))
- [FLASK] Fix missing icon for webassembly endowment ([#18781](https://github.com/MetaMask/metamask-extension/pull/18781))
- [FLASK] Fix text selection bug in snap ui ([#18719](https://github.com/MetaMask/metamask-extension/pull/18719))
## [10.30.4]
### Fixed
- Fix error upon submitting multiple requests that require approval ([#19050](https://github.com/MetaMask/metamask-extension/pull/19050))
@ -3727,7 +3757,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Uncategorized
- Added the ability to restore accounts from seed words.
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.30.4...HEAD
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.31.0...HEAD
[10.31.0]: https://github.com/MetaMask/metamask-extension/compare/v10.30.4...v10.31.0
[10.30.4]: https://github.com/MetaMask/metamask-extension/compare/v10.30.3...v10.30.4
[10.30.3]: https://github.com/MetaMask/metamask-extension/compare/v10.30.2...v10.30.3
[10.30.2]: https://github.com/MetaMask/metamask-extension/compare/v10.30.1...v10.30.2

View File

@ -19,7 +19,7 @@ To learn how to contribute to the MetaMask project itself, visit our [Internal D
- Install [Yarn v3](https://yarnpkg.com/getting-started/install)
- ONLY follow the steps in the "Install Corepack" and "Updating the global Yarn version" sections
- DO NOT take any of the steps in the "Initializing your project", "Updating to the latest versions" or "Installing the latest build fresh from master" sections. These steps could result in your repo being reset or installing the wrong yarn version, which can break your build.
- Copy the `.metamaskrc.dist` file to `.metamaskrc`
- Duplicate `.metamaskrc.dist` within the root and rename it to `.metamaskrc`
- Replace the `INFURA_PROJECT_ID` value with your own personal [Infura Project ID](https://infura.io/docs).
- If debugging MetaMetrics, you'll need to add a value for `SEGMENT_WRITE_KEY` [Segment write key](https://segment.com/docs/connections/find-writekey/), see [Developing on MetaMask - Segment](./development/README.md#segment).
- If debugging unhandled exceptions, you'll need to add a value for `SENTRY_DSN` [Sentry Dsn](https://docs.sentry.io/product/sentry-basics/dsn-explainer/), see [Developing on MetaMask - Sentry](./development/README.md#sentry).

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Suchergebnisse"
},
"searchSettings": {
"message": "In Einstellungen suchen"
},
"searchTokens": {
"message": "Token suchen"
},
@ -3576,10 +3573,6 @@
"message": "Sie benötigen $1 mehr $2, um diesen Swap abzuschließen",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Die besten $1 Kurse.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Keine Token verfügbar mit $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3047,12 +3047,6 @@
"searchAccounts": {
"message": "Αναζήτηση Λογαριασμών"
},
"searchResults": {
"message": "Αποτελέσματα Αναζήτησης"
},
"searchSettings": {
"message": "Αναζήτηση στις Ρυθμίσεις"
},
"searchTokens": {
"message": "Αναζήτηση Tokens"
},
@ -3576,10 +3570,6 @@
"message": "Χρειάζεστε $1 περισσότερα $2 για να ολοκληρώσετε αυτήν την ανταλλαγή",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Το καλύτερο από $1 προσφορές.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Δεν υπάρχουν διαθέσιμα tokens που να ταιριάζουν σε $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -654,6 +654,9 @@
"clearActivityDescription": {
"message": "This resets the account's nonce and erases data from the activity tab in your wallet. Only the current account and network will be affected. Your balances and incoming transactions won't change."
},
"click": {
"message": "Click"
},
"clickToConnectLedgerViaWebHID": {
"message": "Click here to connect your Ledger via WebHID",
"description": "Text that can be clicked to open a browser popup for connecting the ledger device via webhid"
@ -1418,6 +1421,9 @@
"enterMaxSpendLimit": {
"message": "Enter max spend limit"
},
"enterOptionalPassword": {
"message": "Enter optional password"
},
"enterPassword": {
"message": "Enter password"
},
@ -1559,6 +1565,10 @@
"followUsOnTwitter": {
"message": "Follow us on Twitter"
},
"forMoreDetails": {
"message": "for more details.",
"description": "Click for more details message in popup modal displayed when installing a snap for the first time."
},
"forbiddenIpfsGateway": {
"message": "Forbidden IPFS Gateway: Please specify a CID gateway"
},
@ -3522,9 +3532,6 @@
"searchResults": {
"message": "Search results"
},
"searchSettings": {
"message": "Search in Settings"
},
"searchTokens": {
"message": "Search tokens"
},
@ -3843,9 +3850,29 @@
"snapsNoInsight": {
"message": "The snap didn't return any insight"
},
"snapsPrivacyWarningFirstMessage": {
"message": "Installing a snap retrieves data from third parties. They may collect your personal information.",
"description": "First part of a message in popup modal displayed when installing a snap for the first time."
},
"snapsPrivacyWarningSecondMessage": {
"message": "MetaMask has no access to this information.",
"description": "Second part of a message in popup modal displayed when installing a snap for the first time."
},
"snapsSettingsDescription": {
"message": "Manage your Snaps"
},
"snapsThirdPartyNoticeReadMorePartOne": {
"message": "Any information you share with third-party-developed snaps will be collected directly by those snaps in accordance with their privacy policies. ",
"description": "First part of a tooltip content in popup modal displayed when installing a snap for the first time."
},
"snapsThirdPartyNoticeReadMorePartThree": {
"message": "MetaMask has no access to information you share with these third parties.",
"description": "Third part of a tooltip content in popup modal displayed when installing a snap for the first time."
},
"snapsThirdPartyNoticeReadMorePartTwo": {
"message": "During the installation of a snap, npmjs (npmjs.com) and AWS (aws.amazon.com) may collect your IP address. Please refer to their privacy policies for more information.",
"description": "Second part of a tooltip content in popup modal displayed when installing a snap for the first time."
},
"snapsToggle": {
"message": "A snap will only run if it is enabled"
},
@ -4117,10 +4144,6 @@
"message": "You need $1 more $2 to complete this swap",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Best of $1 quotes.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "No tokens available matching $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"
@ -4470,6 +4493,10 @@
"thingsToKeep": {
"message": "Things to keep in mind:"
},
"thirdPartySoftware": {
"message": "Third party software",
"description": "Title of a popup modal displayed when installing a snap for the first time."
},
"thisCollection": {
"message": "this collection"
},
@ -4933,6 +4960,9 @@
"viewOnOpensea": {
"message": "View on Opensea"
},
"viewinCustodianApp": {
"message": "View in custodian app"
},
"viewinExplorer": {
"message": "View $1 in explorer",
"description": "$1 is the action type. e.g (Account, Transaction, Swap)"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Resultados de la búsqueda"
},
"searchSettings": {
"message": "Buscar en Configuración"
},
"searchTokens": {
"message": "Buscar tokens"
},
@ -3576,10 +3573,6 @@
"message": "Necesita $1 más $2 para completar este canje",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Mejor cotización de $1.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "No hay tokens disponibles que coincidan con $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -2238,10 +2238,6 @@
"message": "Necesita $1 más $2 para realizar este canje",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Mejor cotización de $1.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "No hay tokens disponibles que coincidan con $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Résultats de la recherche"
},
"searchSettings": {
"message": "Rechercher dans les paramètres"
},
"searchTokens": {
"message": "Rechercher des jetons"
},
@ -3576,10 +3573,6 @@
"message": "Vous avez besoin de $1 $2 de plus pour effectuer ce swap",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Meilleures cotations de $1.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Aucun jeton disponible correspondant à $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "खोज परिणाम"
},
"searchSettings": {
"message": "सेटिंग्स में खोजें"
},
"searchTokens": {
"message": "टोकन खोजें"
},
@ -3576,10 +3573,6 @@
"message": "इस स्वैप को पूरा करने के लिए आपको अधिक $1 और $2 की आवश्यकता होगी",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "$1 उद्धरणों में से सर्वश्रेष्ठ।",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "$1 के मिलान वाले कोई भी टोकन उपलब्ध नहीं हैं",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Cari hasil"
},
"searchSettings": {
"message": "Cari di Pengaturan"
},
"searchTokens": {
"message": "Cari token"
},
@ -3576,10 +3573,6 @@
"message": "Anda memerlukan $1 $2 lagi untuk menyelesaikan swap",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Kuotasi terbaik dari $1.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Tidak ada token yang cocok yang tersedia $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "検索結果"
},
"searchSettings": {
"message": "設定で検索"
},
"searchTokens": {
"message": "トークンを検索"
},
@ -3576,10 +3573,6 @@
"message": "このスワップを完了させるには、さらに$1の$2が必要です。",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "最善の$1の見積もり。",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "$1と一致するトークンがありません",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "검색 결과"
},
"searchSettings": {
"message": "설정에서 찾기"
},
"searchTokens": {
"message": "토큰 검색"
},
@ -3576,10 +3573,6 @@
"message": "이 스왑을 완료하려면 $1개의 추가 $2이(가) 필요합니다.",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "$1의 최고 견적",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "$1와(과) 일치하는 토큰이 없습니다.",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Resultados da busca"
},
"searchSettings": {
"message": "Buscar nas configurações"
},
"searchTokens": {
"message": "Buscar tokens"
},
@ -3576,10 +3573,6 @@
"message": "Você precisa de mais $1 $2 para concluir essa troca",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Melhores cotações de $1.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Nenhum token disponível correspondente a $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -2238,10 +2238,6 @@
"message": "Você precisa de mais $1 $2 para concluir essa troca",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Melhores cotações de $1.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Nenhum token disponível correspondente a $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Результаты поиска"
},
"searchSettings": {
"message": "Поиск в настройках"
},
"searchTokens": {
"message": "Поиск токенов"
},
@ -3576,10 +3573,6 @@
"message": "Вам нужно еще $1 $2 для завершения этого обмена",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Лучшие котировки $1.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Нет доступных токенов, соответствующих $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Mga Resulta ng Paghahanap"
},
"searchSettings": {
"message": "Maghanap sa mga Setting"
},
"searchTokens": {
"message": "Maghanap ng Mga Token"
},
@ -3576,10 +3573,6 @@
"message": "Kailangan mo ng $1 pa $2 para makumpleto ang pag-swap na ito",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Pinakamaganda ng $1 na quote.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Walang available na token na tumutugma sa $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Arama sonuçları"
},
"searchSettings": {
"message": "Ayarlarda ara"
},
"searchTokens": {
"message": "Token ara"
},
@ -3576,10 +3573,6 @@
"message": "Bu takası tamamlamak için $1 tane daha $2 gerekli",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "En iyi $1 teklifleri.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "$1 ile eşleşen token yok",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "Kết quả tìm kiếm"
},
"searchSettings": {
"message": "Tìm kiếm trong phần Cài đặt"
},
"searchTokens": {
"message": "Tìm kiếm token"
},
@ -3576,10 +3573,6 @@
"message": "Bạn cần $1 $2 nữa để hoàn tất giao dịch hoán đổi này",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "Tốt nhất trong $1 báo giá.",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "Không có token nào khớp với $1",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -3050,9 +3050,6 @@
"searchResults": {
"message": "搜索结果"
},
"searchSettings": {
"message": "在设置中搜索"
},
"searchTokens": {
"message": "搜索代币"
},
@ -3576,10 +3573,6 @@
"message": "您还需要 $1 的 $2 来完成这笔交换",
"description": "Tells the user how many more of a given token they need for a specific swap. $1 is an amount of tokens and $2 is the token symbol."
},
"swapBestOfNQuotes": {
"message": "$1 报价中最棒的。",
"description": "$1 is the number of quotes that the user can select from when opening the list of quotes on the 'view quote' screen"
},
"swapBuildQuotePlaceHolderText": {
"message": "没有与 $1 匹配的代币",
"description": "Tells the user that a given search string does not match any tokens in our token lists. $1 can be any string of text"

View File

@ -6,5 +6,5 @@
"matches": ["https://metamask.io/*"],
"ids": ["*"]
},
"minimum_chrome_version": "80"
"minimum_chrome_version": "88"
}

View File

@ -32,7 +32,6 @@ import {
import { checkForLastErrorAndLog } from '../../shared/modules/browser-runtime.utils';
import { isManifestV3 } from '../../shared/modules/mv3.utils';
import { maskObject } from '../../shared/modules/object.utils';
import { getEnvironmentType, deferredPromise, getPlatform } from './lib/util';
import migrations from './migrations';
import Migrator from './lib/migrator';
import ExtensionPlatform from './platforms/extension';
@ -51,6 +50,7 @@ import rawFirstTimeState from './first-time-state';
import getFirstPreferredLangCode from './lib/get-first-preferred-lang-code';
import getObjStructure from './lib/getObjStructure';
import setupEnsIpfsResolver from './lib/ens-ipfs/setup';
import { deferredPromise, getPlatform } from './lib/util';
/* eslint-enable import/first */
@ -84,7 +84,6 @@ let popupIsOpen = false;
let notificationIsOpen = false;
let uiIsTriggering = false;
const openMetamaskTabsIDs = {};
const openMetamaskConnections = new Map();
const requestAccountTabIds = {};
let controller;
@ -185,28 +184,10 @@ let connectExternal;
browser.runtime.onConnect.addListener(async (...args) => {
// Queue up connection attempts here, waiting until after initialization
await isInitialized;
const remotePort = args[0];
const { sender } = remotePort;
const url = sender?.url;
const detectedProcessName = url ? getEnvironmentType(url) : '';
const connectionId = generateConnectionId(remotePort, detectedProcessName);
const openConnections = openMetamaskConnections.get(connectionId) || 0;
if (
openConnections === 0 ||
(detectedProcessName === 'background' && openConnections < 2)
// 2 background connections are allowed, one for phishing warning page and one for the ledger bridge keyring
) {
// This is set in `setupController`, which is called as part of initialization
connectRemote(...args);
openMetamaskConnections.set(connectionId, openConnections + 1);
} else {
throw new Error('CONNECTION_ALREADY_EXISTS');
}
// This is set in `setupController`, which is called as part of initialization
connectRemote(...args);
});
browser.runtime.onConnectExternal.addListener(async (...args) => {
// Queue up connection attempts here, waiting until after initialization
await isInitialized;
@ -435,21 +416,6 @@ export async function loadStateFromPersistence() {
return versionedData.data;
}
function generateConnectionId(remotePort, detectedProcessName) {
const { sender } = remotePort;
const id = sender?.tab ? sender.tab.id : sender?.id;
if (!id || !detectedProcessName) {
console.error(
'Must provide id and detectedProcessName to generate connection id.',
id,
detectedProcessName,
); // eslint-disable-line no-console
throw new Error(
'Must provide id and detectedProcessName to generate connection id.',
);
}
return `${id}-${detectedProcessName}`;
}
/**
* Initializes the MetaMask Controller with any initial state and default language.
* Configures platform-specific error reporting strategy.
@ -604,6 +570,7 @@ export function setupController(
// communication with popup
controller.isClientOpen = true;
controller.setupTrustedCommunication(portStream, remotePort.sender);
if (isManifestV3) {
// If we get a WORKER_KEEP_ALIVE message, we respond with an ACK
remotePort.onMessage.addListener((message) => {
@ -618,11 +585,9 @@ export function setupController(
});
}
const connectionId = generateConnectionId(remotePort, processName);
if (processName === ENVIRONMENT_TYPE_POPUP) {
popupIsOpen = true;
endOfStream(portStream, () => {
openMetamaskConnections.set(connectionId, 0);
popupIsOpen = false;
const isClientOpen = isClientOpenStatus();
controller.isClientOpen = isClientOpen;
@ -632,8 +597,8 @@ export function setupController(
if (processName === ENVIRONMENT_TYPE_NOTIFICATION) {
notificationIsOpen = true;
endOfStream(portStream, () => {
openMetamaskConnections.set(connectionId, 0);
notificationIsOpen = false;
const isClientOpen = isClientOpenStatus();
controller.isClientOpen = isClientOpen;
@ -649,7 +614,6 @@ export function setupController(
openMetamaskTabsIDs[tabId] = true;
endOfStream(portStream, () => {
openMetamaskConnections.set(connectionId, 0);
delete openMetamaskTabsIDs[tabId];
const isClientOpen = isClientOpenStatus();
controller.isClientOpen = isClientOpen;

View File

@ -183,6 +183,20 @@ export default class AppStateController extends EventEmitter {
});
}
///: BEGIN:ONLY_INCLUDE_IN(snaps)
/**
* Record if popover for snaps privacy warning has been shown
* on the first install of a snap.
*
* @param {boolean} shown - shown status
*/
setSnapsInstallPrivacyWarningShownStatus(shown) {
this.store.updateState({
snapsInstallPrivacyWarningShown: shown,
});
}
///: END:ONLY_INCLUDE_IN
/**
* Record the timestamp of the last time the user has seen the outdated browser warning
*
@ -374,6 +388,25 @@ export default class AppStateController extends EventEmitter {
this.store.updateState({ usedNetworks });
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
/**
* Set the interactive replacement token with a url and the old refresh token
*
* @param {object} opts
* @param opts.url
* @param opts.oldRefreshToken
* @returns {void}
*/
showInteractiveReplacementTokenBanner({ url, oldRefreshToken }) {
this.store.updateState({
interactiveReplacementToken: {
url,
oldRefreshToken,
},
});
}
///: END:ONLY_INCLUDE_IN
/**
* A setter for the currentPopupId which indicates the id of popup window that's currently active
*

View File

@ -346,4 +346,23 @@ describe('AppStateController', () => {
);
});
});
describe('setSnapsInstallPrivacyWarningShownStatus', () => {
it('updates the status of snaps install privacy warning', () => {
appStateController = createAppStateController();
const updateStateSpy = jest.spyOn(
appStateController.store,
'updateState',
);
appStateController.setSnapsInstallPrivacyWarningShownStatus(true);
expect(updateStateSpy).toHaveBeenCalledTimes(1);
expect(updateStateSpy).toHaveBeenCalledWith({
snapsInstallPrivacyWarningShown: true,
});
updateStateSpy.mockRestore();
});
});
});

View File

@ -13,7 +13,7 @@ import { convertHexToDecimal } from '@metamask/controller-utils';
import { NETWORK_TYPES } from '../../../shared/constants/network';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
import DetectTokensController from './detect-tokens';
import { NetworkController, NetworkControllerEventType } from './network';
import { NetworkController } from './network';
import PreferencesController from './preferences';
describe('DetectTokensController', function () {
@ -248,7 +248,7 @@ describe('DetectTokensController', function () {
),
onNetworkStateChange: (cb) =>
networkControllerMessenger.subscribe(
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
() => {
const networkState = network.store.getState();
const modifiedNetworkState = {

View File

@ -625,10 +625,21 @@ export default class MetaMetricsController {
* @returns {MetaMetricsContext}
*/
_buildContext(referrer, page = METAMETRICS_BACKGROUND_PAGE_OBJECT) {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const mmiProps = {};
if (this.extension?.runtime?.id) {
mmiProps.extensionId = this.extension.runtime.id;
}
///: END:ONLY_INCLUDE_IN
return {
app: {
name: 'MetaMask Extension',
version: this.version,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
...mmiProps,
///: END:ONLY_INCLUDE_IN
},
userAgent: window.navigator.userAgent,
page,
@ -658,6 +669,15 @@ export default class MetaMetricsController {
referrer,
environmentType = ENVIRONMENT_TYPE_BACKGROUND,
} = rawPayload;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const mmiProps = {};
if (this.extension?.runtime?.id) {
mmiProps.extensionId = this.extension.runtime.id;
}
///: END:ONLY_INCLUDE_IN
return {
event,
messageId: buildUniqueMessageId(rawPayload),
@ -676,6 +696,9 @@ export default class MetaMetricsController {
locale: this.locale,
chain_id: properties?.chain_id ?? this.chainId,
environment_type: environmentType,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
...mmiProps,
///: END:ONLY_INCLUDE_IN
},
context: this._buildContext(referrer, page),
};
@ -689,6 +712,13 @@ export default class MetaMetricsController {
* @returns {MetaMetricsTraits | null} traits that have changed since last update
*/
_buildUserTraitsObject(metamaskState) {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const mmiAccountAddress =
metamaskState.custodyAccountDetails &&
Object.keys(metamaskState.custodyAccountDetails).length
? Object.keys(metamaskState.custodyAccountDetails)[0]
: null;
///: END:ONLY_INCLUDE_IN
const { traits, previousUserTraits } = this.store.getState();
/** @type {MetaMetricsTraits} */
const currentTraits = {
@ -728,6 +758,11 @@ export default class MetaMetricsController {
[MetaMetricsUserTrait.DesktopEnabled]:
metamaskState.desktopEnabled || false,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
[MetaMetricsUserTrait.MmiExtensionId]: this.extension?.runtime?.id,
[MetaMetricsUserTrait.MmiAccountAddress]: mmiAccountAddress,
[MetaMetricsUserTrait.MmiIsCustodian]: Boolean(mmiAccountAddress),
///: END:ONLY_INCLUDE_IN
[MetaMetricsUserTrait.SecurityProviders]:
metamaskState.transactionSecurityCheckEnabled ? ['opensea'] : [],
};

View File

@ -23,6 +23,14 @@ const FAKE_CHAIN_ID = '0x1338';
const LOCALE = 'en_US';
const TEST_META_METRICS_ID = '0xabc';
const DUMMY_ACTION_ID = 'DUMMY_ACTION_ID';
const MOCK_EXTENSION_ID = 'testid';
const MOCK_EXTENSION = {
runtime: {
id: MOCK_EXTENSION_ID,
setUninstallURL: () => undefined,
},
};
const MOCK_TRAITS = {
test_boolean: true,
@ -39,7 +47,11 @@ const MOCK_INVALID_TRAITS = {
};
const DEFAULT_TEST_CONTEXT = {
app: { name: 'MetaMask Extension', version: VERSION },
app: {
name: 'MetaMask Extension',
version: VERSION,
extensionId: MOCK_EXTENSION_ID,
},
page: METAMETRICS_BACKGROUND_PAGE_OBJECT,
referrer: undefined,
userAgent: window.navigator.userAgent,
@ -56,6 +68,7 @@ const DEFAULT_EVENT_PROPERTIES = {
revenue: undefined,
value: undefined,
currency: undefined,
extensionId: MOCK_EXTENSION_ID,
...DEFAULT_SHARED_PROPERTIES,
};
@ -149,6 +162,7 @@ function getMetaMetricsController({
},
events: {},
},
extension: MOCK_EXTENSION,
});
}
describe('MetaMetricsController', function () {
@ -973,6 +987,11 @@ describe('MetaMetricsController', function () {
[MetaMetricsUserTrait.TokenDetectionEnabled]: true,
[MetaMetricsUserTrait.DesktopEnabled]: false,
[MetaMetricsUserTrait.SecurityProviders]: [],
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
[MetaMetricsUserTrait.MmiExtensionId]: 'testid',
[MetaMetricsUserTrait.MmiAccountAddress]: null,
[MetaMetricsUserTrait.MmiIsCustodian]: false,
///: END:ONLY_INCLUDE_IN
});
});

View File

@ -0,0 +1,612 @@
import EventEmitter from 'events';
import log from 'loglevel';
import { captureException } from '@sentry/browser';
import {
PersonalMessageManager,
TypedMessageManager,
} from '@metamask/message-manager';
import { CUSTODIAN_TYPES } from '@metamask-institutional/custody-keyring';
import {
updateCustodianTransactions,
custodianEventHandlerFactory,
} from '@metamask-institutional/extension';
import {
REFRESH_TOKEN_CHANGE_EVENT,
INTERACTIVE_REPLACEMENT_TOKEN_CHANGE_EVENT,
} from '@metamask-institutional/sdk';
import { handleMmiPortfolio } from '@metamask-institutional/portfolio-dashboard';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
import { CHAIN_IDS } from '../../../shared/constants/network';
import {
BUILD_QUOTE_ROUTE,
CONNECT_HARDWARE_ROUTE,
} from '../../../ui/helpers/constants/routes';
import { getPermissionBackgroundApiMethods } from './permissions';
export default class MMIController extends EventEmitter {
constructor(opts) {
super();
this.opts = opts;
this.mmiConfigurationController = opts.mmiConfigurationController;
this.keyringController = opts.keyringController;
this.txController = opts.txController;
this.securityProviderRequest = opts.securityProviderRequest;
this.preferencesController = opts.preferencesController;
this.appStateController = opts.appStateController;
this.transactionUpdateController = opts.transactionUpdateController;
this.custodyController = opts.custodyController;
this.institutionalFeaturesController = opts.institutionalFeaturesController;
this.getState = opts.getState;
this.getPendingNonce = opts.getPendingNonce;
this.accountTracker = opts.accountTracker;
this.metaMetricsController = opts.metaMetricsController;
this.networkController = opts.networkController;
this.permissionController = opts.permissionController;
this.platform = opts.platform;
this.extension = opts.extension;
this.personalMessageManager = new PersonalMessageManager(
undefined,
undefined,
this.securityProviderRequest,
);
this.typedMessageManager = new TypedMessageManager(
undefined,
undefined,
this.securityProviderRequest,
);
// Prepare event listener after transactionUpdateController gets initiated
this.transactionUpdateController.prepareEventListener(
this.custodianEventHandlerFactory.bind(this),
);
// Get configuration from MMIConfig controller
if (!process.env.IN_TEST) {
this.mmiConfigurationController.storeConfiguration().then(() => {
// This must happen after the configuration is fetched
// Otherwise websockets will always be disabled in the first run
this.transactionUpdateController.subscribeToEvents();
});
}
} // End of constructor
async persistKeyringsAfterRefreshTokenChange() {
this.keyringController.persistAllKeyrings();
}
async trackTransactionEventFromCustodianEvent(txMeta, event) {
this.txController._trackTransactionMetricsEvent(txMeta, event);
}
async addKeyringIfNotExists(type) {
let keyring = await this.keyringController.getKeyringsByType(type)[0];
if (!keyring) {
keyring = await this.keyringController.addNewKeyring(type);
}
return keyring;
}
custodianEventHandlerFactory() {
return custodianEventHandlerFactory({
log,
getState: () => this.getState(),
getPendingNonce: (address) => this.getPendingNonce(address),
setTxHash: (txId, txHash) => this.txController.setTxHash(txId, txHash),
typedMessageManager: this.typedMessageManager,
personalMessageManager: this.personalMessageManager,
txStateManager: this.txController.txStateManager,
custodyController: this.custodyController,
trackTransactionEvent:
this.trackTransactionEventFromCustodianEvent.bind(this),
});
}
async storeCustodianSupportedChains(address) {
const custodyType = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(address),
);
const keyring = await this.addKeyringIfNotExists(custodyType);
const supportedChains = await keyring.getSupportedChains(address);
if (supportedChains?.status === 401) {
return;
}
const accountDetails = this.custodyController.getAccountDetails(address);
await this.custodyController.storeSupportedChainsForAddress(
toChecksumHexAddress(address),
supportedChains,
accountDetails.custodianName,
);
}
async onSubmitPassword() {
// Create a keyring for each custodian type
let addresses = [];
const custodyTypes = this.custodyController.getAllCustodyTypes();
for (const type of custodyTypes) {
try {
const keyring = await this.addKeyringIfNotExists(type);
keyring.on(REFRESH_TOKEN_CHANGE_EVENT, () => {
log.info(`Refresh token change event for ${type}`);
this.persistKeyringsAfterRefreshTokenChange();
});
// Trigger this event, listen to sdk, sdk change the state and then Ui is listening for the state changed
keyring.on(INTERACTIVE_REPLACEMENT_TOKEN_CHANGE_EVENT, (payload) => {
log.info(`Interactive refresh token change event for ${payload}`);
this.appStateController.showInteractiveReplacementTokenBanner(
payload,
);
});
// store the supported chains for this custodian type
const accounts = await keyring.getAccounts();
addresses = addresses.concat(...accounts);
for (const address of accounts) {
try {
await this.storeCustodianSupportedChains(address);
} catch (error) {
captureException(error);
log.error('Error while unlocking extension.', error);
}
}
const txList = this.txController.txStateManager.getTransactions(
{},
[],
false,
); // Includes all transactions, but we are looping through keyrings. Currently filtering is done in updateCustodianTransactions :-/
try {
updateCustodianTransactions({
keyring,
type,
txList,
getPendingNonce: this.getPendingNonce.bind(this),
txStateManager: this.txController.txStateManager,
setTxHash: this.txController.setTxHash.bind(this.txController),
custodyController: this.custodyController,
transactionUpdateController: this.transactionUpdateController,
});
} catch (error) {
log.error('Error doing offline transaction updates', error);
captureException(error);
}
} catch (error) {
log.error(
`Error while unlocking extension with custody type ${type}`,
error,
);
captureException(error);
}
}
try {
await this.mmiConfigurationController.storeConfiguration();
} catch (error) {
log.error('Error while unlocking extension.', error);
}
try {
await this.transactionUpdateController.subscribeToEvents();
} catch (error) {
log.error('Error while unlocking extension.', error);
}
const mmiConfigData =
await this.mmiConfigurationController.store.getState();
if (
mmiConfigData &&
mmiConfigData.mmiConfiguration.features?.websocketApi
) {
this.transactionUpdateController.getCustomerProofForAddresses(addresses);
}
try {
if (this.institutionalFeaturesController.getComplianceProjectId()) {
this.institutionalFeaturesController.startPolling();
}
} catch (e) {
log.error('Failed to start Compliance polling');
log.error(e);
}
}
async connectCustodyAddresses(custodianType, custodianName, accounts) {
if (!custodianType) {
throw new Error('No custodian');
}
const custodian = CUSTODIAN_TYPES[custodianType.toUpperCase()];
if (!custodian) {
throw new Error('No such custodian');
}
const newAccounts = Object.keys(accounts);
// Check if any address is already added
const identities = Object.keys(
this.preferencesController.store.getState().identities,
);
if (newAccounts.some((address) => identities.indexOf(address) !== -1)) {
throw new Error('Cannot import duplicate accounts');
}
const keyring = await this.addKeyringIfNotExists(
custodian.keyringClass.type,
);
keyring.on(REFRESH_TOKEN_CHANGE_EVENT, () => {
log.info(`Refresh token change event for ${keyring.type}`);
this.persistKeyringsAfterRefreshTokenChange();
});
// Trigger this event, listen to sdk, sdk change the state and then Ui is listening for the state changed
keyring.on(INTERACTIVE_REPLACEMENT_TOKEN_CHANGE_EVENT, (payload) => {
log.info(`Interactive refresh token change event for ${payload}`);
this.appStateController.showInteractiveReplacementTokenBanner(payload);
});
if (!keyring) {
throw new Error('Unable to get keyring');
}
const oldAccounts = await this.keyringController.getAccounts();
await keyring.setSelectedAddresses(
newAccounts.map((item) => ({
address: toChecksumHexAddress(item),
name: accounts[item].name,
custodianDetails: accounts[item].custodianDetails,
labels: accounts[item].labels,
token: accounts[item].token,
apiUrl: accounts[item].apiUrl,
custodyType: custodian.keyringClass.type,
chainId: accounts[item].chainId,
})),
);
this.custodyController.setAccountDetails(
newAccounts.map((item) => ({
address: toChecksumHexAddress(item),
name: accounts[item].name,
custodianDetails: accounts[item].custodianDetails,
labels: accounts[item].labels,
apiUrl: accounts[item].apiUrl,
custodyType: custodian.keyringClass.type,
custodianName,
chainId: accounts[item].chainId,
})),
);
newAccounts.forEach(
async () => await this.keyringController.addNewAccount(keyring),
);
const allAccounts = await this.keyringController.getAccounts();
this.preferencesController.setAddresses(allAccounts);
const accountsToTrack = [
...new Set(oldAccounts.concat(allAccounts.map((a) => a.toLowerCase()))),
];
allAccounts.forEach((address) => {
if (!oldAccounts.includes(address.toLowerCase())) {
const label = newAccounts
.filter((item) => item.toLowerCase() === address)
.map((item) => accounts[item].name)[0];
this.preferencesController.setAccountLabel(address, label);
}
});
this.accountTracker.syncWithAddresses(accountsToTrack);
for (const address of newAccounts) {
try {
await this.storeCustodianSupportedChains(address);
} catch (error) {
captureException(error);
}
}
// FIXME: status maps are not a thing anymore
this.custodyController.storeCustodyStatusMap(
custodian.name,
keyring.getStatusMap(),
);
// MMI - get a WS stream for this account
const mmiConfigData =
await this.mmiConfigurationController.store.getState();
if (
mmiConfigData &&
mmiConfigData.mmiConfiguration.features?.websocketApi
) {
this.transactionUpdateController.getCustomerProofForAddresses(
newAccounts,
);
}
return newAccounts;
}
async getCustodianAccounts(
token,
apiUrl,
custodianType,
getNonImportedAccounts,
) {
let currentCustodyType;
if (!custodianType) {
const address = this.preferencesController.getSelectedAddress();
currentCustodyType = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(address),
);
}
let keyring;
if (custodianType) {
const custodian = CUSTODIAN_TYPES[custodianType.toUpperCase()];
if (!custodian) {
throw new Error('No such custodian');
}
keyring = await this.addKeyringIfNotExists(custodian.keyringClass.type);
} else if (currentCustodyType) {
keyring = await this.addKeyringIfNotExists(currentCustodyType);
} else {
throw new Error('No custodian specified');
}
const accounts = await keyring.getCustodianAccounts(
token,
apiUrl,
null,
getNonImportedAccounts,
);
return accounts;
}
async getCustodianAccountsByAddress(token, apiUrl, address, custodianType) {
let keyring;
if (custodianType) {
const custodian = CUSTODIAN_TYPES[custodianType.toUpperCase()];
if (!custodian) {
throw new Error('No such custodian');
}
keyring = await this.addKeyringIfNotExists(custodian.keyringClass.type);
} else {
throw new Error('No custodian specified');
}
const accounts = await keyring.getCustodianAccounts(token, apiUrl, address);
return accounts;
}
async getCustodianTransactionDeepLink(address, txId) {
const custodyType = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(address),
);
const keyring = await this.addKeyringIfNotExists(custodyType);
return keyring.getTransactionDeepLink(address, txId);
}
async getCustodianConfirmDeepLink(txId) {
const txMeta = this.txController.txStateManager.getTransaction(txId);
const address = txMeta.txParams.from;
const custodyType = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(address),
);
const keyring = await this.addKeyringIfNotExists(custodyType);
return {
deepLink: await keyring.getTransactionDeepLink(
txMeta.txParams.from,
txMeta.custodyId,
),
custodyId: txMeta.custodyId,
};
}
async getCustodianSignMessageDeepLink(from, custodyTxId) {
const custodyType = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(from),
);
const keyring = await this.addKeyringIfNotExists(custodyType);
return keyring.getTransactionDeepLink(from, custodyTxId);
}
async getCustodianToken(custodianType) {
let currentCustodyType;
const address = this.preferencesController.getSelectedAddress();
if (!custodianType) {
const resultCustody = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(address),
);
currentCustodyType = resultCustody;
}
let keyring = await this.keyringController.getKeyringsByType(
currentCustodyType || `Custody - ${custodianType}`,
)[0];
if (!keyring) {
keyring = await this.keyringController.addNewKeyring(
currentCustodyType || `Custody - ${custodianType}`,
);
}
const { authDetails } = keyring.getAccountDetails(address);
return keyring ? authDetails.jwt || authDetails.refreshToken : '';
}
// Based on a custodian name, get all the tokens associated with that custodian
async getCustodianJWTList(custodianName) {
console.log('getCustodianJWTList', custodianName);
const { identities } = this.preferencesController.store.getState();
const { mmiConfiguration } =
this.mmiConfigurationController.store.getState();
const addresses = Object.keys(identities);
const tokenList = [];
const { custodians } = mmiConfiguration;
const custodian = custodians.find((item) => item.name === custodianName);
if (!custodian) {
return [];
}
const keyrings = await this.keyringController.getKeyringsByType(
`Custody - ${custodian.type}`,
);
for (const address of addresses) {
for (const keyring of keyrings) {
// Narrow down to custodian Type
const accountDetails = keyring.getAccountDetails(address);
if (!accountDetails) {
log.debug(`${address} does not belong to ${custodian.type} keyring`);
continue;
}
const custodyAccountDetails =
this.custodyController.getAccountDetails(address);
if (
!custodyAccountDetails ||
custodyAccountDetails.custodianName !== custodianName
) {
log.debug(`${address} does not belong to ${custodianName} keyring`);
continue;
}
const { authDetails } = accountDetails;
let token;
if (authDetails.jwt) {
token = authDetails.jwt;
} else if (authDetails.refreshToken) {
token = authDetails.refreshToken;
}
if (!tokenList.includes(token)) {
tokenList.push(token);
}
}
}
return tokenList;
}
async getAllCustodianAccountsWithToken(custodyType, token) {
const keyring = await this.keyringController.getKeyringsByType(
`Custody - ${custodyType}`,
)[0];
return keyring ? keyring.getAllAccountsWithToken(token) : [];
}
async setCustodianNewRefreshToken({ address, newAuthDetails }) {
const custodyType = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(address),
);
const keyring = await this.addKeyringIfNotExists(custodyType);
await keyring.replaceRefreshTokenAuthDetails(address, newAuthDetails);
}
async handleMmiCheckIfTokenIsPresent(req) {
const { token, apiUrl } = req.params;
const custodyType = 'Custody - JSONRPC'; // Only JSONRPC is supported for now
// This can only work if the extension is unlocked
await this.appStateController.getUnlockPromise(true);
const keyring = await this.addKeyringIfNotExists(custodyType);
return await this.custodyController.handleMmiCheckIfTokenIsPresent({
token,
apiUrl,
keyring,
});
}
async setMmiPortfolioCookie() {
await this.appStateController.getUnlockPromise(true);
const keyringAccounts = await this.keyringController.getAccounts();
const { identities } = this.preferencesController.store.getState();
const { metaMetricsId } = this.metaMetricsController.store.getState();
const getAccountDetails = (address) =>
this.custodyController.getAccountDetails(address);
const extensionId = this.extension.runtime.id;
const networks = [
...this.preferencesController.getRpcMethodPreferences(),
{ chainId: CHAIN_IDS.MAINNET },
{ chainId: CHAIN_IDS.GOERLI },
];
handleMmiPortfolio({
keyringAccounts,
identities,
metaMetricsId,
networks,
getAccountDetails,
extensionId,
});
}
async setAccountAndNetwork(origin, address, chainId) {
await this.appStateController.getUnlockPromise(true);
const selectedAddress = this.preferencesController.getSelectedAddress();
if (selectedAddress.toLowerCase() !== address.toLowerCase()) {
this.preferencesController.setSelectedAddress(address);
}
const selectedChainId = parseInt(
this.networkController.getCurrentChainId(),
16,
);
if (selectedChainId !== chainId && chainId === 1) {
this.networkController.setProviderType('mainnet');
} else if (selectedChainId !== chainId) {
const network = this.preferencesController
.getFrequentRpcListDetail()
.find((item) => parseInt(item.chainId, 16) === chainId);
this.networkController.setRpcTarget(
network.rpcUrl,
network.chainId,
network.ticker,
network.nickname,
);
}
getPermissionBackgroundApiMethods(
this.permissionController,
).addPermittedAccount(origin, address);
return true;
}
async handleMmiOpenSwaps(origin, address, chainId) {
await this.setAccountAndNetwork(origin, address, chainId);
this.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE);
return true;
}
async handleMmiOpenAddHardwareWallet() {
await this.appStateController.getUnlockPromise(true);
this.platform.openExtensionInBrowser(CONNECT_HARDWARE_ROUTE);
return true;
}
}

View File

@ -0,0 +1,129 @@
/* eslint-disable */
import { KeyringController } from '@metamask/eth-keyring-controller';
import { MmiConfigurationController } from '@metamask-institutional/custody-keyring';
import { TransactionUpdateController } from '@metamask-institutional/transaction-update';
import MMIController from './mmi-controller';
import TransactionController from './transactions';
import PreferencesController from './preferences';
import AppStateController from './app-state';
describe('MMIController', function () {
let mmiController;
beforeEach(function () {
mmiController = new MMIController({
mmiConfigurationController: new MmiConfigurationController(),
keyringController: new KeyringController({
initState: {},
}),
transactionUpdateController: new TransactionUpdateController({
getCustodyKeyring: jest.fn(),
}),
txController: new TransactionController({
initState: {},
provider: {
chainId: 'fail',
nickname: '',
rpcTarget: 'https://api.myetherwallet.com/eth',
ticker: 'ETH',
type: 'rinkeby',
},
getCurrentChainId: jest.fn(),
getNetworkId: jest.fn(),
onNetworkStateChange: jest.fn(),
}),
preferencesController: new PreferencesController({
initState: {},
onInfuraIsBlocked: jest.fn(),
onInfuraIsUnblocked: jest.fn(),
provider: {},
}),
appStateController: new AppStateController({
addUnlockListener: jest.fn(),
isUnlocked: jest.fn(() => true),
initState: {},
onInactiveTimeout: jest.fn(),
showUnlockRequest: jest.fn(),
preferencesStore: {
subscribe: jest.fn(),
getState: jest.fn(() => ({
preferences: {
autoLockTimeLimit: 0,
},
})),
},
qrHardwareStore: {
subscribe: jest.fn(),
},
messenger: {
call: jest.fn(() => ({
catch: jest.fn(),
})),
},
}),
custodianEventHandlerFactory: jest.fn(),
});
});
describe('mmiController constructor', function () {
it('should instantiate correctly', function () {
expect(mmiController).toBeInstanceOf(MMIController);
});
it('should have all required properties', function () {
expect(mmiController.opts).toBeDefined();
expect(mmiController.mmiConfigurationController).toBeDefined();
expect(mmiController.preferencesController).toBeDefined();
expect(mmiController.transactionUpdateController).toBeDefined();
});
});
describe('persistKeyringsAfterRefreshTokenChange', function () {
it('should call keyringController.persistAllKeyrings', async function () {
mmiController.keyringController.persistAllKeyrings = jest.fn();
await mmiController.persistKeyringsAfterRefreshTokenChange();
expect(
mmiController.keyringController.persistAllKeyrings,
).toHaveBeenCalled();
});
});
describe('trackTransactionEventFromCustodianEvent', function () {
it('should call txController._trackTransactionMetricsEvent', function () {
const txMeta = {};
const event = 'event';
mmiController.txController._trackTransactionMetricsEvent = jest.fn();
mmiController.trackTransactionEventFromCustodianEvent(txMeta, event);
expect(
mmiController.txController._trackTransactionMetricsEvent,
).toHaveBeenCalledWith(txMeta, event);
});
});
describe('custodianEventHandlerFactory', function () {
it('should call custodianEventHandlerFactory', async function () {
mmiController.custodianEventHandlerFactory = jest.fn();
mmiController.custodianEventHandlerFactory();
expect(mmiController.custodianEventHandlerFactory).toHaveBeenCalled();
});
});
describe('storeCustodianSupportedChains', function () {
it('should call storeCustodianSupportedChains', async function () {
mmiController.storeCustodianSupportedChains = jest.fn();
mmiController.storeCustodianSupportedChains('0x1');
expect(mmiController.storeCustodianSupportedChains).toHaveBeenCalledWith(
'0x1',
);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@ -82,35 +82,13 @@ type NetworkConfigurationId = string;
*/
type ChainId = Hex;
/**
* The set of event types that NetworkController can publish via its messenger.
*/
export enum NetworkControllerEventType {
/**
* @see {@link NetworkControllerNetworkDidChangeEvent}
*/
NetworkDidChange = 'NetworkController:networkDidChange',
/**
* @see {@link NetworkControllerNetworkWillChangeEvent}
*/
NetworkWillChange = 'NetworkController:networkWillChange',
/**
* @see {@link NetworkControllerInfuraIsBlockedEvent}
*/
InfuraIsBlocked = 'NetworkController:infuraIsBlocked',
/**
* @see {@link NetworkControllerInfuraIsUnblockedEvent}
*/
InfuraIsUnblocked = 'NetworkController:infuraIsUnblocked',
}
/**
* `networkWillChange` is published when the current network is about to be
* switched, but the new provider has not been created and no state changes have
* occurred yet.
*/
export type NetworkControllerNetworkWillChangeEvent = {
type: NetworkControllerEventType.NetworkWillChange;
type: 'NetworkController:networkWillChange';
payload: [];
};
@ -119,7 +97,7 @@ export type NetworkControllerNetworkWillChangeEvent = {
* switched network (but before the network has been confirmed to be available).
*/
export type NetworkControllerNetworkDidChangeEvent = {
type: NetworkControllerEventType.NetworkDidChange;
type: 'NetworkController:networkDidChange';
payload: [];
};
@ -129,7 +107,7 @@ export type NetworkControllerNetworkDidChangeEvent = {
* location.
*/
export type NetworkControllerInfuraIsBlockedEvent = {
type: NetworkControllerEventType.InfuraIsBlocked;
type: 'NetworkController:infuraIsBlocked';
payload: [];
};
@ -139,7 +117,7 @@ export type NetworkControllerInfuraIsBlockedEvent = {
* their location, or the network is switched to a non-Infura network.
*/
export type NetworkControllerInfuraIsUnblockedEvent = {
type: NetworkControllerEventType.InfuraIsUnblocked;
type: 'NetworkController:infuraIsUnblocked';
payload: [];
};
@ -152,15 +130,22 @@ export type NetworkControllerEvent =
| NetworkControllerInfuraIsBlockedEvent
| NetworkControllerInfuraIsUnblockedEvent;
export type NetworkControllerGetProviderConfigAction = {
type: `NetworkController:getProviderConfig`;
handler: () => ProviderConfiguration;
};
export type NetworkControllerAction = NetworkControllerGetProviderConfigAction;
/**
* The messenger that the NetworkController uses to publish events.
*/
export type NetworkControllerMessenger = RestrictedControllerMessenger<
typeof name,
never,
NetworkControllerAction,
NetworkControllerEvent,
never,
NetworkControllerEventType
string,
string
>;
/**
@ -499,6 +484,9 @@ export class NetworkController extends EventEmitter {
}
this.#infuraProjectId = infuraProjectId;
this.#trackMetaMetricsEvent = trackMetaMetricsEvent;
this.#messenger.registerActionHandler(`${name}:getProviderConfig`, () => {
return this.store.getState().providerConfig;
});
}
/**
@ -582,7 +570,7 @@ export class NetworkController extends EventEmitter {
* blocking requests, or if the network is not Infura-supported.
*/
async lookupNetwork(): Promise<void> {
const { chainId, type } = this.store.getState().providerConfig;
const { type } = this.store.getState().providerConfig;
const { provider } = this.getProviderAndBlockTracker();
let networkChanged = false;
let networkId: NetworkIdState = null;
@ -596,29 +584,16 @@ export class NetworkController extends EventEmitter {
return;
}
if (!chainId) {
log.warn(
'NetworkController - lookupNetwork aborted due to missing chainId',
);
this.#resetNetworkId();
this.#resetNetworkStatus();
this.#resetNetworkDetails();
return;
}
const isInfura = isInfuraProviderType(type);
const listener = () => {
networkChanged = true;
this.#messenger.unsubscribe(
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
listener,
);
};
this.#messenger.subscribe(
NetworkControllerEventType.NetworkDidChange,
listener,
);
this.#messenger.subscribe('NetworkController:networkDidChange', listener);
try {
const results = await Promise.all([
@ -664,10 +639,7 @@ export class NetworkController extends EventEmitter {
// in the process of being called, so we don't need to go further.
return;
}
this.#messenger.unsubscribe(
NetworkControllerEventType.NetworkDidChange,
listener,
);
this.#messenger.unsubscribe('NetworkController:networkDidChange', listener);
this.store.updateState({
networkStatus,
@ -692,15 +664,15 @@ export class NetworkController extends EventEmitter {
if (isInfura) {
if (networkStatus === NetworkStatus.Available) {
this.#messenger.publish(NetworkControllerEventType.InfuraIsUnblocked);
this.#messenger.publish('NetworkController:infuraIsUnblocked');
} else if (networkStatus === NetworkStatus.Blocked) {
this.#messenger.publish(NetworkControllerEventType.InfuraIsBlocked);
this.#messenger.publish('NetworkController:infuraIsBlocked');
}
} else {
// Always publish infuraIsUnblocked regardless of network status to
// prevent consumers from being stuck in a blocked state if they were
// previously connected to an Infura network that was blocked
this.#messenger.publish(NetworkControllerEventType.InfuraIsUnblocked);
this.#messenger.publish('NetworkController:infuraIsUnblocked');
}
}
@ -892,12 +864,13 @@ export class NetworkController extends EventEmitter {
* the new network.
*/
async #switchNetwork(providerConfig: ProviderConfiguration) {
this.#messenger.publish(NetworkControllerEventType.NetworkWillChange);
const { type, rpcUrl, chainId } = providerConfig;
this.#messenger.publish('NetworkController:networkWillChange');
this.#resetNetworkId();
this.#resetNetworkStatus();
this.#resetNetworkDetails();
this.#configureProvider(providerConfig);
this.#messenger.publish(NetworkControllerEventType.NetworkDidChange);
this.#configureProvider({ type, rpcUrl, chainId });
this.#messenger.publish('NetworkController:networkDidChange');
await this.lookupNetwork();
}
@ -906,8 +879,7 @@ export class NetworkController extends EventEmitter {
* block tracker) to talk to a network.
*
* @param args - The arguments.
* @param args.type - The shortname of an Infura-supported network (see
* {@link NETWORK_TYPES}).
* @param args.type - The provider type.
* @param args.rpcUrl - The URL of the RPC endpoint that represents the
* network. Only used for non-Infura networks.
* @param args.chainId - The chain ID of the network (as per EIP-155). Only
@ -915,16 +887,28 @@ export class NetworkController extends EventEmitter {
* any Infura-supported network).
* @throws if the `type` if not a known Infura-supported network.
*/
#configureProvider({ type, rpcUrl, chainId }: ProviderConfiguration): void {
#configureProvider({
type,
rpcUrl,
chainId,
}: {
type: ProviderType;
rpcUrl: string | undefined;
chainId: Hex | undefined;
}): void {
const isInfura = isInfuraProviderType(type);
if (isInfura) {
// infura type-based endpoints
this.#configureInfuraProvider({
type,
infuraProjectId: this.#infuraProjectId,
});
} else if (type === NETWORK_TYPES.RPC && rpcUrl) {
// url-based rpc endpoints
} else if (type === NETWORK_TYPES.RPC) {
if (chainId === undefined) {
throw new Error('chainId must be provided for custom RPC endpoints');
}
if (rpcUrl === undefined) {
throw new Error('rpcUrl must be provided for custom RPC endpoints');
}
this.#configureStandardProvider(rpcUrl, chainId);
} else {
throw new Error(

View File

@ -1,5 +1,8 @@
import { ObservableStore } from '@metamask/obs-store';
import { normalize as normalizeAddress } from 'eth-sig-util';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { setDashboardCookie } from '@metamask-institutional/portfolio-dashboard';
///: END:ONLY_INCLUDE_IN
import { IPFS_DEFAULT_GATEWAY_URL } from '../../../shared/constants/network';
import { LedgerTransportTypes } from '../../../shared/constants/hardware-wallets';
import { ThemeType } from '../../../shared/constants/preferences';
@ -69,12 +72,25 @@ export default class PreferencesController {
...opts.initState,
};
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
initState.useTokenDetection = Boolean(process.env.TOKEN_DETECTION_V2);
///: END:ONLY_INCLUDE_IN
this.network = opts.network;
this._onInfuraIsBlocked = opts.onInfuraIsBlocked;
this._onInfuraIsUnblocked = opts.onInfuraIsUnblocked;
this.store = new ObservableStore(initState);
this.store.setMaxListeners(13);
this.tokenListController = opts.tokenListController;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.handleMmiPortfolio = opts.handleMmiPortfolio;
if (!process.env.IN_TEST) {
this.mmiConfigurationStore = opts.mmiConfigurationStore.getState();
}
///: END:ONLY_INCLUDE_IN
this._subscribeToInfuraAvailability();
global.setPreference = (key, value) => {
@ -245,6 +261,10 @@ export default class PreferencesController {
return ids;
}, {});
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.prepareMmiPortfolio();
///: END:ONLY_INCLUDE_IN
this.store.updateState({ identities });
}
@ -269,6 +289,11 @@ export default class PreferencesController {
const [selected] = Object.keys(identities);
this.setSelectedAddress(selected);
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.prepareMmiPortfolio();
///: END:ONLY_INCLUDE_IN
return address;
}
@ -325,6 +350,10 @@ export default class PreferencesController {
this.store.updateState({ identities, lostIdentities });
this.addAddresses(addresses);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.prepareMmiPortfolio();
///: END:ONLY_INCLUDE_IN
// If the selected account is no longer valid,
// select an arbitrary other account:
let selected = this.getSelectedAddress();
@ -505,6 +534,21 @@ export default class PreferencesController {
return this.store.getState().disabledRpcMethodPreferences;
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
async prepareMmiPortfolio() {
if (!process.env.IN_TEST) {
try {
const mmiDashboardData = await this.handleMmiPortfolio();
const cookieSetUrls =
this.mmiConfigurationStore.mmiConfiguration?.portfolio?.cookieSetUrls;
setDashboardCookie(mmiDashboardData, cookieSetUrls);
} catch (error) {
console.error(error);
}
}
}
///: END:ONLY_INCLUDE_IN
//
// PRIVATE METHODS
//

View File

@ -63,8 +63,28 @@ import {
} from '@metamask/snaps-controllers';
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import {
CUSTODIAN_TYPES,
MmiConfigurationController,
} from '@metamask-institutional/custody-keyring';
import { InstitutionalFeaturesController } from '@metamask-institutional/institutional-features';
import { CustodyController } from '@metamask-institutional/custody-controller';
import { TransactionUpdateController } from '@metamask-institutional/transaction-update';
///: END:ONLY_INCLUDE_IN
import { SignatureController } from '@metamask/signature-controller';
///: BEGIN:ONLY_INCLUDE_IN(desktop)
// eslint-disable-next-line import/order
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
///: END:ONLY_INCLUDE_IN
import { ApprovalType } from '@metamask/controller-utils';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
///: END:ONLY_INCLUDE_IN
import {
AssetType,
TransactionStatus,
@ -116,15 +136,16 @@ import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens';
import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils';
import { isManifestV3 } from '../../shared/modules/mv3.utils';
import { hexToDecimal } from '../../shared/modules/conversion.utils';
///: BEGIN:ONLY_INCLUDE_IN(desktop)
// eslint-disable-next-line import/order
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
///: END:ONLY_INCLUDE_IN
import { ACTION_QUEUE_METRICS_E2E_TEST } from '../../shared/constants/test-flags';
import {
onMessageReceived,
checkForMultipleVersionsRunning,
} from './detect-multiple-instances';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import MMIController from './controllers/mmi-controller';
import { mmiKeyringBuilderFactory } from './mmi-keyring-builder-factory';
///: END:ONLY_INCLUDE_IN
import ComposableObservableStore from './lib/ComposableObservableStore';
import AccountTracker from './lib/account-tracker';
import createDupeReqFilterMiddleware from './lib/createDupeReqFilterMiddleware';
@ -140,10 +161,7 @@ import createTabIdMiddleware from './lib/createTabIdMiddleware';
import createOnboardingMiddleware from './lib/createOnboardingMiddleware';
import { setupMultiplex } from './lib/stream-utils';
import EnsController from './controllers/ens';
import {
NetworkController,
NetworkControllerEventType,
} from './controllers/network';
import { NetworkController } from './controllers/network';
import PreferencesController from './controllers/preferences';
import AppStateController from './controllers/app-state';
import CachedBalancesController from './controllers/cached-balances';
@ -267,10 +285,23 @@ export default class MetamaskController extends EventEmitter {
],
});
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.mmiConfigurationController = new MmiConfigurationController({
initState: initState.MmiConfigurationController,
mmiConfigurationServiceUrl: process.env.MMI_CONFIGURATION_SERVICE_URL,
});
///: END:ONLY_INCLUDE_IN
const networkControllerMessenger = this.controllerMessenger.getRestricted({
name: 'NetworkController',
allowedEvents: Object.values(NetworkControllerEventType),
allowedEvents: [
'NetworkController:networkWillChange',
'NetworkController:networkDidChange',
'NetworkController:infuraIsBlocked',
'NetworkController:infuraIsUnblocked',
],
});
this.networkController = new NetworkController({
messenger: networkControllerMessenger,
state: initState.NetworkController,
@ -316,11 +347,11 @@ export default class MetamaskController extends EventEmitter {
initLangCode: opts.initLangCode,
onInfuraIsBlocked: networkControllerMessenger.subscribe.bind(
networkControllerMessenger,
NetworkControllerEventType.InfuraIsBlocked,
'NetworkController:infuraIsBlocked',
),
onInfuraIsUnblocked: networkControllerMessenger.subscribe.bind(
networkControllerMessenger,
NetworkControllerEventType.InfuraIsUnblocked,
'NetworkController:infuraIsUnblocked',
),
tokenListController: this.tokenListController,
provider: this.provider,
@ -366,7 +397,7 @@ export default class MetamaskController extends EventEmitter {
// network provider by one update.
onNetworkStateChange: (cb) =>
networkControllerMessenger.subscribe(
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
() => {
const networkState = this.networkController.store.getState();
const modifiedNetworkState = {
@ -477,7 +508,7 @@ export default class MetamaskController extends EventEmitter {
preferencesStore: this.preferencesController.store,
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
networkControllerMessenger,
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
),
getNetworkIdentifier: () => {
const { type, rpcUrl } =
@ -516,7 +547,7 @@ export default class MetamaskController extends EventEmitter {
// onNetworkDidChange
onNetworkStateChange: networkControllerMessenger.subscribe.bind(
networkControllerMessenger,
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
),
getCurrentNetworkEIP1559Compatibility:
this.networkController.getEIP1559Compatibility.bind(
@ -642,7 +673,7 @@ export default class MetamaskController extends EventEmitter {
this.networkController.store.getState().providerConfig.chainId,
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
networkControllerMessenger,
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
),
});
@ -654,7 +685,7 @@ export default class MetamaskController extends EventEmitter {
blockTracker: this.blockTracker,
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
networkControllerMessenger,
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
),
getCurrentChainId: () =>
this.networkController.store.getState().providerConfig.chainId,
@ -723,9 +754,21 @@ export default class MetamaskController extends EventEmitter {
keyringOverrides?.lattice || LatticeKeyring,
QRHardwareKeyring,
];
additionalKeyrings = additionalKeyringTypes.map((keyringType) =>
keyringBuilderFactory(keyringType),
);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
for (const custodianType of Object.keys(CUSTODIAN_TYPES)) {
additionalKeyrings.push(
mmiKeyringBuilderFactory(
CUSTODIAN_TYPES[custodianType].keyringClass,
{ mmiConfigurationController: this.mmiConfigurationController },
),
);
}
///: END:ONLY_INCLUDE_IN
}
this.keyringController = new KeyringController({
@ -982,6 +1025,22 @@ export default class MetamaskController extends EventEmitter {
preferencesStore: this.preferencesController.store,
});
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.custodyController = new CustodyController({
initState: initState.CustodyController,
});
this.institutionalFeaturesController = new InstitutionalFeaturesController({
initState: initState.InstitutionalFeaturesController,
showConfirmRequest: opts.showUserConfirmation,
});
this.transactionUpdateController = new TransactionUpdateController({
initState: initState.TransactionUpdateController,
getCustodyKeyring: this.getCustodyKeyringIfExists.bind(this),
mmiConfigurationController: this.mmiConfigurationController,
captureException,
});
///: END:ONLY_INCLUDE_IN
this.backupController = new BackupController({
preferencesController: this.preferencesController,
addressBookController: this.addressBookController,
@ -1052,6 +1111,9 @@ export default class MetamaskController extends EventEmitter {
getDeviceModel: this.getDeviceModel.bind(this),
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
securityProviderRequest: this.securityProviderRequest.bind(this),
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
transactionUpdateController: this.transactionUpdateController,
///: END:ONLY_INCLUDE_IN
messenger: this.controllerMessenger.getRestricted({
name: 'TransactionController',
allowedActions: [
@ -1062,6 +1124,28 @@ export default class MetamaskController extends EventEmitter {
}),
});
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.mmiController = new MMIController({
mmiConfigurationController: this.mmiConfigurationController,
keyringController: this.keyringController,
txController: this.txController,
securityProviderRequest: this.securityProviderRequest.bind(this),
preferencesController: this.preferencesController,
appStateController: this.appStateController,
transactionUpdateController: this.transactionUpdateController,
custodyController: this.custodyController,
institutionalFeaturesController: this.institutionalFeaturesController,
getState: this.getState.bind(this),
getPendingNonce: this.getPendingNonce.bind(this),
accountTracker: this.accountTracker,
metaMetricsController: this.metaMetricsController,
networkController: this.networkController,
permissionController: this.permissionController,
platform: this.platform,
extension: this.extension,
});
///: END:ONLY_INCLUDE_IN
this.txController.on(`tx:status-update`, async (txId, status) => {
if (
status === TransactionStatus.confirmed ||
@ -1151,7 +1235,7 @@ export default class MetamaskController extends EventEmitter {
});
networkControllerMessenger.subscribe(
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
async () => {
const { ticker } =
this.networkController.store.getState().providerConfig;
@ -1216,16 +1300,19 @@ export default class MetamaskController extends EventEmitter {
this.networkController.store.getState().providerConfig.chainId,
});
this.signatureController.hub.on('cancelWithReason', (message, reason) => {
this.metaMetricsController.trackEvent({
event: reason,
category: MetaMetricsEventCategory.Transactions,
properties: {
action: 'Sign Request',
type: message.type,
},
});
});
this.signatureController.hub.on(
'cancelWithReason',
({ message, reason }) => {
this.metaMetricsController.trackEvent({
event: reason,
category: MetaMetricsEventCategory.Transactions,
properties: {
action: 'Sign Request',
type: message.type,
},
});
},
);
this.swapsController = new SwapsController({
getBufferedGasLimit: this.txController.txGasUtil.getBufferedGasLimit.bind(
@ -1276,7 +1363,7 @@ export default class MetamaskController extends EventEmitter {
// ensure accountTracker updates balances after network change
networkControllerMessenger.subscribe(
NetworkControllerEventType.NetworkDidChange,
'NetworkController:networkDidChange',
() => {
this.accountTracker._updateAccounts();
},
@ -1284,7 +1371,7 @@ export default class MetamaskController extends EventEmitter {
// clear unapproved transactions and messages when the network will change
networkControllerMessenger.subscribe(
NetworkControllerEventType.NetworkWillChange,
'NetworkController:networkWillChange',
() => {
this.txController.txStateManager.clearUnapprovedTxs();
this.encryptionPublicKeyController.clearUnapproved();
@ -1426,6 +1513,13 @@ export default class MetamaskController extends EventEmitter {
///: BEGIN:ONLY_INCLUDE_IN(desktop)
DesktopController: this.desktopController.store,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
CustodyController: this.custodyController.store,
InstitutionalFeaturesController:
this.institutionalFeaturesController.store,
MmiConfigurationController: this.mmiConfigurationController.store,
///: END:ONLY_INCLUDE_IN
...resetOnRestartStore,
});
@ -1462,6 +1556,13 @@ export default class MetamaskController extends EventEmitter {
///: BEGIN:ONLY_INCLUDE_IN(desktop)
DesktopController: this.desktopController.store,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
CustodyController: this.custodyController.store,
InstitutionalFeaturesController:
this.institutionalFeaturesController.store,
MmiConfigurationController: this.mmiConfigurationController.store,
///: END:ONLY_INCLUDE_IN
...resetOnRestartStore,
},
controllerMessenger: this.controllerMessenger,
@ -2106,6 +2207,12 @@ export default class MetamaskController extends EventEmitter {
),
setTermsOfUseLastAgreed:
appStateController.setTermsOfUseLastAgreed.bind(appStateController),
///: BEGIN:ONLY_INCLUDE_IN(snaps)
setSnapsInstallPrivacyWarningShownStatus:
appStateController.setSnapsInstallPrivacyWarningShownStatus.bind(
appStateController,
),
///: END:ONLY_INCLUDE_IN
setOutdatedBrowserWarningLastShown:
appStateController.setOutdatedBrowserWarningLastShown.bind(
appStateController,
@ -2231,6 +2338,72 @@ export default class MetamaskController extends EventEmitter {
rejectPermissionsRequest: this.rejectPermissionsRequest,
...getPermissionBackgroundApiMethods(permissionController),
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
connectCustodyAddresses:
this.mmiController.connectCustodyAddresses.bind(this),
getCustodianAccounts: this.mmiController.getCustodianAccounts.bind(this),
getCustodianAccountsByAddress:
this.mmiController.getCustodianAccountsByAddress.bind(this),
getCustodianTransactionDeepLink:
this.mmiController.getCustodianTransactionDeepLink.bind(this),
getCustodianConfirmDeepLink:
this.mmiController.getCustodianConfirmDeepLink.bind(this),
getCustodianSignMessageDeepLink:
this.mmiController.getCustodianSignMessageDeepLink.bind(this),
getCustodianToken: this.mmiController.getCustodianToken.bind(this),
getCustodianJWTList: this.mmiController.getCustodianJWTList.bind(this),
setWaitForConfirmDeepLinkDialog:
this.custodyController.setWaitForConfirmDeepLinkDialog.bind(
this.custodyController,
),
setCustodianConnectRequest:
this.custodyController.setCustodianConnectRequest.bind(
this.custodyController,
),
getCustodianConnectRequest:
this.custodyController.getCustodianConnectRequest.bind(
this.custodyController,
),
getAllCustodianAccountsWithToken:
this.mmiController.getAllCustodianAccountsWithToken.bind(this),
getMmiConfiguration:
this.mmiConfigurationController.getConfiguration.bind(
this.mmiConfigurationController,
),
setComplianceAuthData:
this.institutionalFeaturesController.setComplianceAuthData.bind(
this.institutionalFeaturesController,
),
deleteComplianceAuthData:
this.institutionalFeaturesController.deleteComplianceAuthData.bind(
this.institutionalFeaturesController,
),
generateComplianceReport:
this.institutionalFeaturesController.generateComplianceReport.bind(
this.institutionalFeaturesController,
),
syncReportsInProgress:
this.institutionalFeaturesController.syncReportsInProgress.bind(
this.institutionalFeaturesController,
),
removeConnectInstitutionalFeature:
this.institutionalFeaturesController.removeConnectInstitutionalFeature.bind(
this.institutionalFeaturesController,
),
getComplianceHistoricalReportsByAddress:
this.institutionalFeaturesController.getComplianceHistoricalReportsByAddress.bind(
this.institutionalFeaturesController,
),
removeAddTokenConnectRequest:
this.institutionalFeaturesController.removeAddTokenConnectRequest.bind(
this.institutionalFeaturesController,
),
setCustodianNewRefreshToken:
this.mmiController.setCustodianNewRefreshToken.bind(this),
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(snaps)
// snaps
removeSnapError: this.controllerMessenger.call.bind(
@ -2661,6 +2834,10 @@ export default class MetamaskController extends EventEmitter {
async submitPassword(password) {
await this.keyringController.submitPassword(password);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.mmiController.onSubmitPassword();
///: END:ONLY_INCLUDE_IN
try {
await this.blockTracker.checkForLatestBlock();
} catch (error) {
@ -2780,6 +2957,16 @@ export default class MetamaskController extends EventEmitter {
return keyring.mnemonic;
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
async getCustodyKeyringIfExists(address) {
const custodyType = this.custodyController.getCustodyTypeByAddress(
toChecksumHexAddress(address),
);
const keyring = this.keyringController.getKeyringsByType(custodyType)[0];
return keyring?.getAccountDetails(address) ? keyring : undefined;
}
///: END:ONLY_INCLUDE_IN
//
// Hardware
//
@ -3168,6 +3355,10 @@ export default class MetamaskController extends EventEmitter {
// Remove account from the account tracker controller
this.accountTracker.removeAccount([address]);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.custodyController.removeAccount(address);
///: END:ONLY_INCLUDE_IN(build-mmi)
const keyring = await this.keyringController.getKeyringForAccount(address);
// Remove account from the keyring
await this.keyringController.removeAccount(address);
@ -3726,6 +3917,21 @@ export default class MetamaskController extends EventEmitter {
this.alertController.setWeb3ShimUsageRecorded.bind(
this.alertController,
),
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
handleMmiAuthenticate:
this.institutionalFeaturesController.handleMmiAuthenticate.bind(
this.institutionalFeaturesController,
),
handleMmiCheckIfTokenIsPresent:
this.mmiController.handleMmiCheckIfTokenIsPresent.bind(this),
handleMmiPortfolio: this.mmiController.setMmiPortfolioCookie.bind(this),
handleMmiOpenSwaps: this.mmiController.handleMmiOpenSwaps.bind(this),
handleMmiSetAccountAndNetwork:
this.mmiController.setAccountAndNetwork.bind(this),
handleMmiOpenAddHardwareWallet:
this.mmiController.handleMmiOpenAddHardwareWallet.bind(this),
///: END:ONLY_INCLUDE_IN
}),
);

View File

@ -0,0 +1,17 @@
/**
* Get builder function for MMI keyrings which require an additional `opts`
* parameter, used to pass MMI configuration.
*
* Returns a builder function for `Keyring` with a `type` property.
*
* @param {Keyring} Keyring - The Keyring class for the builder.
* @param {Keyring} opts - Optional parameters to be passed to the builder.
* @returns {Function} A builder function for the given Keyring.
*/
export function mmiKeyringBuilderFactory(Keyring, opts) {
const builder = () => new Keyring(opts);
builder.type = Keyring.type;
return builder;
}

View File

@ -6,10 +6,10 @@
// subset of files to check against these targets.
module.exports = {
global: {
lines: 71.12,
branches: 58.94,
statements: 70.58,
functions: 63.84,
lines: 70.85,
branches: 59.07,
statements: 70.3,
functions: 63.52,
},
transforms: {
branches: 100,

View File

@ -37,6 +37,7 @@ module.exports = {
testMatch: [
'<rootDir>/app/scripts/constants/error-utils.test.js',
'<rootDir>/app/scripts/controllers/app-state.test.js',
'<rootDir>/app/scripts/controllers/mmi-controller.test.js',
'<rootDir>/app/scripts/controllers/network/**/*.test.js',
'<rootDir>/app/scripts/controllers/network/**/*.test.ts',
'<rootDir>/app/scripts/controllers/permissions/**/*.test.js',

View File

@ -142,7 +142,6 @@
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true
}
@ -150,8 +149,6 @@
"@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"@ethereumjs/tx>@chainsafe/ssz>case": true,
"browserify": true,
"browserify>buffer": true
}
},
@ -160,7 +157,8 @@
"WeakRef": true
},
"packages": {
"browserify": true
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/as-sha256": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/rlp": {
@ -175,50 +173,56 @@
"packages": {
"@ethereumjs/tx>@chainsafe/ssz": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>insert-module-globals>is-buffer": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
"globals": {
"Headers": true,
"TextDecoder": true,
"URL": true,
"btoa": true,
"fetch": true
},
"packages": {
"browserify>browserify-zlib": true,
"browserify>buffer": true,
"browserify>https-browserify": true,
"browserify>process": true,
"browserify>stream-http": true,
"browserify>url": true,
"browserify>util": true
}
},
"@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": true,
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
"TextEncoder": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": {
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethersproject/abi": {
@ -524,8 +528,8 @@
"@keystonehq/metamask-airgapped-keyring": {
"packages": {
"@ethereumjs/tx": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true,
"browserify>buffer": true,
"browserify>events": true,
@ -537,60 +541,18 @@
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": {
"globals": {
"TextEncoder": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": {
"packages": {
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true,
@ -822,11 +784,11 @@
"@ethersproject/contracts": true,
"@ethersproject/providers": true,
"@metamask/assets-controllers>@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/base-controller": true,
"@metamask/assets-controllers>@metamask/controller-utils": true,
"@metamask/assets-controllers>abort-controller": true,
"@metamask/assets-controllers>multiformats": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/metamask-eth-abis": true,
"@metamask/utils": true,
"browserify>events": true,
@ -856,6 +818,28 @@
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/assets-controllers>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/assets-controllers>abort-controller": {
"globals": {
"AbortController": true
@ -1031,22 +1015,74 @@
},
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"@metamask/scure-bip39": true,
"browserify>buffer": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": {
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"browserify>buffer": true,
"browserify>events": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>obs-store": {
"packages": {
"@metamask/eth-token-tracker>safe-event-emitter": true,
@ -1082,25 +1118,17 @@
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": {
"packages": {
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": true,
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1257,7 +1285,7 @@
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": true,
"bn.js": true,
"browserify>buffer": true,
@ -1265,6 +1293,21 @@
"eth-sig-util>tweetnacl-util": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1440,14 +1483,36 @@
"setInterval": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/gas-fee-controller>@metamask/base-controller": true,
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
"eth-query": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true,
"uuid": true
}
},
"@metamask/gas-fee-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/gas-fee-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/jazzicon": {
"globals": {
"document.createElement": true,
@ -1546,8 +1611,8 @@
},
"@metamask/message-manager": {
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager>@metamask/base-controller": true,
"@metamask/message-manager>@metamask/controller-utils": true,
"@metamask/message-manager>jsonschema": true,
"browserify>buffer": true,
"browserify>events": true,
@ -1556,6 +1621,28 @@
"uuid": true
}
},
"@metamask/message-manager>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/message-manager>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/message-manager>jsonschema": {
"packages": {
"browserify>url": true
@ -1586,8 +1673,8 @@
"console.error": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
"@metamask/permission-controller>nanoid": true,
"deep-freeze-strict": true,
"eth-rpc-errors": true,
@ -1595,6 +1682,28 @@
"json-rpc-engine": true
}
},
"@metamask/permission-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/permission-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@ -1605,12 +1714,34 @@
"fetch": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/phishing-controller>@metamask/base-controller": true,
"@metamask/phishing-controller>@metamask/controller-utils": true,
"@metamask/phishing-warning>eth-phishing-detect": true,
"punycode": true
}
},
"@metamask/phishing-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/phishing-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/phishing-warning>eth-phishing-detect": {
"packages": {
"eslint>optionator>fast-levenshtein": true
@ -1647,8 +1778,14 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
"@metamask/key-tree>@scure/base": true,
"@metamask/scure-bip39>@noble/hashes": true
}
},
"@metamask/scure-bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/signature-controller": {
@ -1656,15 +1793,37 @@
"console.info": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager": true,
"@metamask/signature-controller>@metamask/base-controller": true,
"@metamask/signature-controller>@metamask/controller-utils": true,
"browserify>buffer": true,
"browserify>events": true,
"eth-rpc-errors": true,
"ethereumjs-util": true
}
},
"@metamask/signature-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/signature-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/smart-transactions-controller": {
"globals": {
"URLSearchParams": true,
@ -1748,7 +1907,12 @@
},
"@metamask/subject-metadata-controller": {
"packages": {
"@metamask/base-controller": true
"@metamask/subject-metadata-controller>@metamask/base-controller": true
}
},
"@metamask/subject-metadata-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/utils": {
@ -1800,15 +1964,6 @@
"browserify>buffer": true
}
},
"@ngraveio/bc-ur>crc>buffer": {
"globals": {
"console": true
},
"packages": {
"base64-js": true,
"browserify>buffer>ieee754": true
}
},
"@ngraveio/bc-ur>jsbi": {
"globals": {
"define": true
@ -2535,6 +2690,16 @@
"ethjs-query>babel-runtime>core-js": true
}
},
"browserify>browserify-zlib": {
"packages": {
"browserify>assert": true,
"browserify>browserify-zlib>pako": true,
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-browserify": true,
"browserify>util": true
}
},
"browserify>buffer": {
"globals": {
"console": true
@ -2701,6 +2866,12 @@
"browserify>has>function-bind": true
}
},
"browserify>https-browserify": {
"packages": {
"browserify>stream-http": true,
"browserify>url": true
}
},
"browserify>os-browserify": {
"globals": {
"location": true,
@ -2730,6 +2901,41 @@
"readable-stream": true
}
},
"browserify>stream-http": {
"globals": {
"AbortController": true,
"Blob": true,
"MSStreamReader": true,
"ReadableStream": true,
"WritableStream": true,
"XDomainRequest": true,
"XMLHttpRequest": true,
"clearTimeout": true,
"fetch": true,
"location.protocol.search": true,
"setTimeout": true
},
"packages": {
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-http>builtin-status-codes": true,
"browserify>stream-http>readable-stream": true,
"browserify>url": true,
"pumpify>inherits": true,
"watchify>xtend": true
}
},
"browserify>stream-http>readable-stream": {
"packages": {
"browserify>browser-resolve": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>process": true,
"browserify>string_decoder": true,
"pumpify>inherits": true,
"readable-stream>util-deprecate": true
}
},
"browserify>string_decoder": {
"packages": {
"ethereumjs-wallet>safe-buffer": true
@ -2951,16 +3157,59 @@
"setInterval": true
},
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>crypto-browserify": true,
"browserify>events": true,
"eth-lattice-keyring>@ethereumjs/tx": true,
"eth-lattice-keyring>bn.js": true,
"eth-lattice-keyring>gridplus-sdk": true,
"eth-lattice-keyring>rlp": true
}
},
"eth-lattice-keyring>@ethereumjs/tx": {
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>bn.js": {
"globals": {
"Buffer": true
@ -2984,12 +3233,12 @@
"setTimeout": true
},
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx": true,
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,
"eth-lattice-keyring>gridplus-sdk>bignumber.js": true,
"eth-lattice-keyring>gridplus-sdk>bitwise": true,
@ -3006,6 +3255,65 @@
"lodash": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": {
"packages": {
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>gridplus-sdk>bignumber.js": {
"globals": {
"crypto": true,
@ -3135,22 +3443,14 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"eth-sig-util>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3189,21 +3489,13 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-abi>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ganache>secp256k1>elliptic": true
}
},
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-abi>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3388,22 +3680,14 @@
"ethereumjs-wallet>safe-buffer": true
}
},
"ethereumjs-wallet>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-wallet>ethereumjs-util": {
"packages": {
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>ethereum-cryptography": true,
"ethereumjs-wallet>ethereumjs-util>ethjs-util": true,
"ganache>secp256k1>elliptic": true
}

View File

@ -142,7 +142,6 @@
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true
}
@ -150,8 +149,6 @@
"@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"@ethereumjs/tx>@chainsafe/ssz>case": true,
"browserify": true,
"browserify>buffer": true
}
},
@ -160,7 +157,8 @@
"WeakRef": true
},
"packages": {
"browserify": true
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/as-sha256": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/rlp": {
@ -175,50 +173,56 @@
"packages": {
"@ethereumjs/tx>@chainsafe/ssz": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>insert-module-globals>is-buffer": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
"globals": {
"Headers": true,
"TextDecoder": true,
"URL": true,
"btoa": true,
"fetch": true
},
"packages": {
"browserify>browserify-zlib": true,
"browserify>buffer": true,
"browserify>https-browserify": true,
"browserify>process": true,
"browserify>stream-http": true,
"browserify>url": true,
"browserify>util": true
}
},
"@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": true,
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
"TextEncoder": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": {
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethersproject/abi": {
@ -524,8 +528,8 @@
"@keystonehq/metamask-airgapped-keyring": {
"packages": {
"@ethereumjs/tx": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true,
"browserify>buffer": true,
"browserify>events": true,
@ -537,60 +541,18 @@
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": {
"globals": {
"TextEncoder": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": {
"packages": {
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true,
@ -822,11 +784,11 @@
"@ethersproject/contracts": true,
"@ethersproject/providers": true,
"@metamask/assets-controllers>@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/base-controller": true,
"@metamask/assets-controllers>@metamask/controller-utils": true,
"@metamask/assets-controllers>abort-controller": true,
"@metamask/assets-controllers>multiformats": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/metamask-eth-abis": true,
"@metamask/utils": true,
"browserify>events": true,
@ -856,6 +818,28 @@
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/assets-controllers>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/assets-controllers>abort-controller": {
"globals": {
"AbortController": true
@ -1102,22 +1086,74 @@
},
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"@metamask/scure-bip39": true,
"browserify>buffer": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": {
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"browserify>buffer": true,
"browserify>events": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>obs-store": {
"packages": {
"@metamask/eth-token-tracker>safe-event-emitter": true,
@ -1153,25 +1189,17 @@
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": {
"packages": {
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": true,
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1328,7 +1356,7 @@
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": true,
"bn.js": true,
"browserify>buffer": true,
@ -1336,6 +1364,21 @@
"eth-sig-util>tweetnacl-util": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1511,14 +1554,36 @@
"setInterval": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/gas-fee-controller>@metamask/base-controller": true,
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
"eth-query": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true,
"uuid": true
}
},
"@metamask/gas-fee-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/gas-fee-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/jazzicon": {
"globals": {
"document.createElement": true,
@ -1617,8 +1682,8 @@
},
"@metamask/message-manager": {
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager>@metamask/base-controller": true,
"@metamask/message-manager>@metamask/controller-utils": true,
"@metamask/message-manager>jsonschema": true,
"browserify>buffer": true,
"browserify>events": true,
@ -1627,6 +1692,28 @@
"uuid": true
}
},
"@metamask/message-manager>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/message-manager>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/message-manager>jsonschema": {
"packages": {
"browserify>url": true
@ -1635,8 +1722,8 @@
"@metamask/notification-controller": {
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/notification-controller>nanoid": true
"@metamask/notification-controller>nanoid": true,
"@metamask/utils": true
}
},
"@metamask/notification-controller>nanoid": {
@ -1664,8 +1751,8 @@
"console.error": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
"@metamask/permission-controller>nanoid": true,
"deep-freeze-strict": true,
"eth-rpc-errors": true,
@ -1673,6 +1760,28 @@
"json-rpc-engine": true
}
},
"@metamask/permission-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/permission-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@ -1683,12 +1792,34 @@
"fetch": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/phishing-controller>@metamask/base-controller": true,
"@metamask/phishing-controller>@metamask/controller-utils": true,
"@metamask/phishing-warning>eth-phishing-detect": true,
"punycode": true
}
},
"@metamask/phishing-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/phishing-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/phishing-warning>eth-phishing-detect": {
"packages": {
"eslint>optionator>fast-levenshtein": true
@ -1755,10 +1886,15 @@
"setTimeout": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/rate-limit-controller>@metamask/base-controller": true,
"eth-rpc-errors": true
}
},
"@metamask/rate-limit-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/rpc-methods-flask": {
"packages": {
"@metamask/key-tree": true,
@ -1823,8 +1959,14 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
"@metamask/key-tree>@scure/base": true,
"@metamask/scure-bip39>@noble/hashes": true
}
},
"@metamask/scure-bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/signature-controller": {
@ -1832,15 +1974,37 @@
"console.info": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager": true,
"@metamask/signature-controller>@metamask/base-controller": true,
"@metamask/signature-controller>@metamask/controller-utils": true,
"browserify>buffer": true,
"browserify>events": true,
"eth-rpc-errors": true,
"ethereumjs-util": true
}
},
"@metamask/signature-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/signature-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/smart-transactions-controller": {
"globals": {
"URLSearchParams": true,
@ -1923,10 +2087,10 @@
"setTimeout": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/permission-controller": true,
"@metamask/post-message-stream": true,
"@metamask/providers>@metamask/object-multiplex": true,
"@metamask/snaps-controllers-flask>@metamask/base-controller": true,
"@metamask/snaps-controllers-flask>@metamask/rpc-methods": true,
"@metamask/snaps-controllers-flask>@metamask/snaps-utils": true,
"@metamask/snaps-controllers-flask>concat-stream": true,
@ -1943,6 +2107,11 @@
"pump": true
}
},
"@metamask/snaps-controllers-flask>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/snaps-controllers-flask>@metamask/rpc-methods": {
"packages": {
"@metamask/key-tree": true,
@ -2194,7 +2363,12 @@
},
"@metamask/subject-metadata-controller": {
"packages": {
"@metamask/base-controller": true
"@metamask/subject-metadata-controller>@metamask/base-controller": true
}
},
"@metamask/subject-metadata-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/utils": {
@ -2246,15 +2420,6 @@
"browserify>buffer": true
}
},
"@ngraveio/bc-ur>crc>buffer": {
"globals": {
"console": true
},
"packages": {
"base64-js": true,
"browserify>buffer>ieee754": true
}
},
"@ngraveio/bc-ur>jsbi": {
"globals": {
"define": true
@ -2981,6 +3146,16 @@
"ethjs-query>babel-runtime>core-js": true
}
},
"browserify>browserify-zlib": {
"packages": {
"browserify>assert": true,
"browserify>browserify-zlib>pako": true,
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-browserify": true,
"browserify>util": true
}
},
"browserify>buffer": {
"globals": {
"console": true
@ -3147,6 +3322,12 @@
"browserify>has>function-bind": true
}
},
"browserify>https-browserify": {
"packages": {
"browserify>stream-http": true,
"browserify>url": true
}
},
"browserify>os-browserify": {
"globals": {
"location": true,
@ -3176,6 +3357,41 @@
"readable-stream": true
}
},
"browserify>stream-http": {
"globals": {
"AbortController": true,
"Blob": true,
"MSStreamReader": true,
"ReadableStream": true,
"WritableStream": true,
"XDomainRequest": true,
"XMLHttpRequest": true,
"clearTimeout": true,
"fetch": true,
"location.protocol.search": true,
"setTimeout": true
},
"packages": {
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-http>builtin-status-codes": true,
"browserify>stream-http>readable-stream": true,
"browserify>url": true,
"pumpify>inherits": true,
"watchify>xtend": true
}
},
"browserify>stream-http>readable-stream": {
"packages": {
"browserify>browser-resolve": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>process": true,
"browserify>string_decoder": true,
"pumpify>inherits": true,
"readable-stream>util-deprecate": true
}
},
"browserify>string_decoder": {
"packages": {
"ethereumjs-wallet>safe-buffer": true
@ -3397,16 +3613,59 @@
"setInterval": true
},
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>crypto-browserify": true,
"browserify>events": true,
"eth-lattice-keyring>@ethereumjs/tx": true,
"eth-lattice-keyring>bn.js": true,
"eth-lattice-keyring>gridplus-sdk": true,
"eth-lattice-keyring>rlp": true
}
},
"eth-lattice-keyring>@ethereumjs/tx": {
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>bn.js": {
"globals": {
"Buffer": true
@ -3430,12 +3689,12 @@
"setTimeout": true
},
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx": true,
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,
"eth-lattice-keyring>gridplus-sdk>bignumber.js": true,
"eth-lattice-keyring>gridplus-sdk>bitwise": true,
@ -3452,6 +3711,65 @@
"lodash": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": {
"packages": {
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>gridplus-sdk>bignumber.js": {
"globals": {
"crypto": true,
@ -3581,22 +3899,14 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"eth-sig-util>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3635,21 +3945,13 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-abi>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ganache>secp256k1>elliptic": true
}
},
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-abi>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3834,22 +4136,14 @@
"ethereumjs-wallet>safe-buffer": true
}
},
"ethereumjs-wallet>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-wallet>ethereumjs-util": {
"packages": {
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>ethereum-cryptography": true,
"ethereumjs-wallet>ethereumjs-util>ethjs-util": true,
"ganache>secp256k1>elliptic": true
}

View File

@ -142,7 +142,6 @@
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true
}
@ -150,8 +149,6 @@
"@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"@ethereumjs/tx>@chainsafe/ssz>case": true,
"browserify": true,
"browserify>buffer": true
}
},
@ -160,7 +157,8 @@
"WeakRef": true
},
"packages": {
"browserify": true
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/as-sha256": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/rlp": {
@ -175,50 +173,56 @@
"packages": {
"@ethereumjs/tx>@chainsafe/ssz": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>insert-module-globals>is-buffer": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
"globals": {
"Headers": true,
"TextDecoder": true,
"URL": true,
"btoa": true,
"fetch": true
},
"packages": {
"browserify>browserify-zlib": true,
"browserify>buffer": true,
"browserify>https-browserify": true,
"browserify>process": true,
"browserify>stream-http": true,
"browserify>url": true,
"browserify>util": true
}
},
"@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": true,
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
"TextEncoder": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": {
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethersproject/abi": {
@ -524,8 +528,8 @@
"@keystonehq/metamask-airgapped-keyring": {
"packages": {
"@ethereumjs/tx": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true,
"browserify>buffer": true,
"browserify>events": true,
@ -537,60 +541,18 @@
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": {
"globals": {
"TextEncoder": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": {
"packages": {
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true,
@ -822,11 +784,11 @@
"@ethersproject/contracts": true,
"@ethersproject/providers": true,
"@metamask/assets-controllers>@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/base-controller": true,
"@metamask/assets-controllers>@metamask/controller-utils": true,
"@metamask/assets-controllers>abort-controller": true,
"@metamask/assets-controllers>multiformats": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/metamask-eth-abis": true,
"@metamask/utils": true,
"browserify>events": true,
@ -856,6 +818,28 @@
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/assets-controllers>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/assets-controllers>abort-controller": {
"globals": {
"AbortController": true
@ -1102,22 +1086,74 @@
},
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"@metamask/scure-bip39": true,
"browserify>buffer": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": {
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"browserify>buffer": true,
"browserify>events": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>obs-store": {
"packages": {
"@metamask/eth-token-tracker>safe-event-emitter": true,
@ -1153,25 +1189,17 @@
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": {
"packages": {
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": true,
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1328,7 +1356,7 @@
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": true,
"bn.js": true,
"browserify>buffer": true,
@ -1336,6 +1364,21 @@
"eth-sig-util>tweetnacl-util": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1511,14 +1554,36 @@
"setInterval": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/gas-fee-controller>@metamask/base-controller": true,
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
"eth-query": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true,
"uuid": true
}
},
"@metamask/gas-fee-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/gas-fee-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/jazzicon": {
"globals": {
"document.createElement": true,
@ -1617,8 +1682,8 @@
},
"@metamask/message-manager": {
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager>@metamask/base-controller": true,
"@metamask/message-manager>@metamask/controller-utils": true,
"@metamask/message-manager>jsonschema": true,
"browserify>buffer": true,
"browserify>events": true,
@ -1627,6 +1692,28 @@
"uuid": true
}
},
"@metamask/message-manager>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/message-manager>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/message-manager>jsonschema": {
"packages": {
"browserify>url": true
@ -1635,8 +1722,8 @@
"@metamask/notification-controller": {
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/notification-controller>nanoid": true
"@metamask/notification-controller>nanoid": true,
"@metamask/utils": true
}
},
"@metamask/notification-controller>nanoid": {
@ -1664,8 +1751,8 @@
"console.error": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
"@metamask/permission-controller>nanoid": true,
"deep-freeze-strict": true,
"eth-rpc-errors": true,
@ -1673,6 +1760,28 @@
"json-rpc-engine": true
}
},
"@metamask/permission-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/permission-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@ -1683,12 +1792,34 @@
"fetch": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/phishing-controller>@metamask/base-controller": true,
"@metamask/phishing-controller>@metamask/controller-utils": true,
"@metamask/phishing-warning>eth-phishing-detect": true,
"punycode": true
}
},
"@metamask/phishing-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/phishing-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/phishing-warning>eth-phishing-detect": {
"packages": {
"eslint>optionator>fast-levenshtein": true
@ -1755,10 +1886,15 @@
"setTimeout": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/rate-limit-controller>@metamask/base-controller": true,
"eth-rpc-errors": true
}
},
"@metamask/rate-limit-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/rpc-methods-flask": {
"packages": {
"@metamask/key-tree": true,
@ -1823,8 +1959,14 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
"@metamask/key-tree>@scure/base": true,
"@metamask/scure-bip39>@noble/hashes": true
}
},
"@metamask/scure-bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/signature-controller": {
@ -1832,15 +1974,37 @@
"console.info": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager": true,
"@metamask/signature-controller>@metamask/base-controller": true,
"@metamask/signature-controller>@metamask/controller-utils": true,
"browserify>buffer": true,
"browserify>events": true,
"eth-rpc-errors": true,
"ethereumjs-util": true
}
},
"@metamask/signature-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/signature-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/smart-transactions-controller": {
"globals": {
"URLSearchParams": true,
@ -1923,10 +2087,10 @@
"setTimeout": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/permission-controller": true,
"@metamask/post-message-stream": true,
"@metamask/providers>@metamask/object-multiplex": true,
"@metamask/snaps-controllers-flask>@metamask/base-controller": true,
"@metamask/snaps-controllers-flask>@metamask/rpc-methods": true,
"@metamask/snaps-controllers-flask>@metamask/snaps-utils": true,
"@metamask/snaps-controllers-flask>concat-stream": true,
@ -1943,6 +2107,11 @@
"pump": true
}
},
"@metamask/snaps-controllers-flask>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/snaps-controllers-flask>@metamask/rpc-methods": {
"packages": {
"@metamask/key-tree": true,
@ -2194,7 +2363,12 @@
},
"@metamask/subject-metadata-controller": {
"packages": {
"@metamask/base-controller": true
"@metamask/subject-metadata-controller>@metamask/base-controller": true
}
},
"@metamask/subject-metadata-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/utils": {
@ -2246,15 +2420,6 @@
"browserify>buffer": true
}
},
"@ngraveio/bc-ur>crc>buffer": {
"globals": {
"console": true
},
"packages": {
"base64-js": true,
"browserify>buffer>ieee754": true
}
},
"@ngraveio/bc-ur>jsbi": {
"globals": {
"define": true
@ -2981,6 +3146,16 @@
"ethjs-query>babel-runtime>core-js": true
}
},
"browserify>browserify-zlib": {
"packages": {
"browserify>assert": true,
"browserify>browserify-zlib>pako": true,
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-browserify": true,
"browserify>util": true
}
},
"browserify>buffer": {
"globals": {
"console": true
@ -3147,6 +3322,12 @@
"browserify>has>function-bind": true
}
},
"browserify>https-browserify": {
"packages": {
"browserify>stream-http": true,
"browserify>url": true
}
},
"browserify>os-browserify": {
"globals": {
"location": true,
@ -3176,6 +3357,41 @@
"readable-stream": true
}
},
"browserify>stream-http": {
"globals": {
"AbortController": true,
"Blob": true,
"MSStreamReader": true,
"ReadableStream": true,
"WritableStream": true,
"XDomainRequest": true,
"XMLHttpRequest": true,
"clearTimeout": true,
"fetch": true,
"location.protocol.search": true,
"setTimeout": true
},
"packages": {
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-http>builtin-status-codes": true,
"browserify>stream-http>readable-stream": true,
"browserify>url": true,
"pumpify>inherits": true,
"watchify>xtend": true
}
},
"browserify>stream-http>readable-stream": {
"packages": {
"browserify>browser-resolve": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>process": true,
"browserify>string_decoder": true,
"pumpify>inherits": true,
"readable-stream>util-deprecate": true
}
},
"browserify>string_decoder": {
"packages": {
"ethereumjs-wallet>safe-buffer": true
@ -3397,16 +3613,59 @@
"setInterval": true
},
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>crypto-browserify": true,
"browserify>events": true,
"eth-lattice-keyring>@ethereumjs/tx": true,
"eth-lattice-keyring>bn.js": true,
"eth-lattice-keyring>gridplus-sdk": true,
"eth-lattice-keyring>rlp": true
}
},
"eth-lattice-keyring>@ethereumjs/tx": {
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>bn.js": {
"globals": {
"Buffer": true
@ -3430,12 +3689,12 @@
"setTimeout": true
},
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx": true,
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,
"eth-lattice-keyring>gridplus-sdk>bignumber.js": true,
"eth-lattice-keyring>gridplus-sdk>bitwise": true,
@ -3452,6 +3711,65 @@
"lodash": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": {
"packages": {
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>gridplus-sdk>bignumber.js": {
"globals": {
"crypto": true,
@ -3581,22 +3899,14 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"eth-sig-util>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3635,21 +3945,13 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-abi>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ganache>secp256k1>elliptic": true
}
},
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-abi>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3834,22 +4136,14 @@
"ethereumjs-wallet>safe-buffer": true
}
},
"ethereumjs-wallet>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-wallet>ethereumjs-util": {
"packages": {
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>ethereum-cryptography": true,
"ethereumjs-wallet>ethereumjs-util>ethjs-util": true,
"ganache>secp256k1>elliptic": true
}

View File

@ -142,7 +142,6 @@
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true
}
@ -150,8 +149,6 @@
"@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"@ethereumjs/tx>@chainsafe/ssz>case": true,
"browserify": true,
"browserify>buffer": true
}
},
@ -160,7 +157,8 @@
"WeakRef": true
},
"packages": {
"browserify": true
"@ethereumjs/tx>@chainsafe/ssz>@chainsafe/as-sha256": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/rlp": {
@ -175,50 +173,56 @@
"packages": {
"@ethereumjs/tx>@chainsafe/ssz": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": true,
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>insert-module-globals>is-buffer": true
}
},
"@ethereumjs/tx>@ethereumjs/util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": true,
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>@ethereumjs/util>micro-ftch": {
"globals": {
"Headers": true,
"TextDecoder": true,
"URL": true,
"btoa": true,
"fetch": true
},
"packages": {
"browserify>browserify-zlib": true,
"browserify>buffer": true,
"browserify>https-browserify": true,
"browserify>process": true,
"browserify>stream-http": true,
"browserify>url": true,
"browserify>util": true
}
},
"@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true,
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": true,
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"@ethereumjs/tx>ethereum-cryptography>@noble/curves": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
"TextEncoder": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32": {
"packages": {
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@ethereumjs/tx>ethereum-cryptography>@scure/bip32>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
"@metamask/key-tree>@noble/hashes": true
}
},
"@ethersproject/abi": {
@ -524,8 +528,8 @@
"@keystonehq/metamask-airgapped-keyring": {
"packages": {
"@ethereumjs/tx": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": true,
"browserify>buffer": true,
"browserify>events": true,
@ -537,60 +541,18 @@
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/base-eth-keyring>rlp": {
"globals": {
"TextEncoder": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@keystonehq/bc-ur-registry-eth>hdkey": true,
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": true,
"browserify>buffer": true,
"uuid": true
}
},
"@keystonehq/metamask-airgapped-keyring>@keystonehq/bc-ur-registry-eth>@keystonehq/bc-ur-registry": {
"globals": {
"define": true
},
"packages": {
"@ngraveio/bc-ur": true,
"@ngraveio/bc-ur>crc>buffer": true,
"browserify>buffer": true,
"ethereumjs-wallet>bs58check": true,
"wait-on>rxjs>tslib": true
}
},
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store": {
"packages": {
"@keystonehq/metamask-airgapped-keyring>@metamask/obs-store>through2": true,
@ -822,11 +784,11 @@
"@ethersproject/contracts": true,
"@ethersproject/providers": true,
"@metamask/assets-controllers>@metamask/abi-utils": true,
"@metamask/assets-controllers>@metamask/base-controller": true,
"@metamask/assets-controllers>@metamask/controller-utils": true,
"@metamask/assets-controllers>abort-controller": true,
"@metamask/assets-controllers>multiformats": true,
"@metamask/base-controller": true,
"@metamask/contract-metadata": true,
"@metamask/controller-utils": true,
"@metamask/metamask-eth-abis": true,
"@metamask/utils": true,
"browserify>events": true,
@ -856,6 +818,28 @@
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/assets-controllers>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/assets-controllers>abort-controller": {
"globals": {
"AbortController": true
@ -1031,22 +1015,74 @@
},
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"@metamask/scure-bip39": true,
"browserify>buffer": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": {
"globals": {
"crypto": true
},
"packages": {
"browserify>browser-resolve": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip32": {
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@noble/secp256k1": true,
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-hd-keyring>ethereum-cryptography>@scure/bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": true,
"browserify>buffer": true,
"browserify>events": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-keyring-controller>@metamask/eth-simple-keyring>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-keyring-controller>obs-store": {
"packages": {
"@metamask/eth-token-tracker>safe-event-emitter": true,
@ -1082,25 +1118,17 @@
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util": {
"packages": {
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": true,
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"@metamask/eth-ledger-bridge-keyring>eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1257,7 +1285,7 @@
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util": {
"packages": {
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethereumjs/tx>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": true,
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": true,
"bn.js": true,
"browserify>buffer": true,
@ -1265,6 +1293,21 @@
"eth-sig-util>tweetnacl-util": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/eth-trezor-keyring>@metamask/eth-sig-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -1440,14 +1483,36 @@
"setInterval": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/gas-fee-controller>@metamask/base-controller": true,
"@metamask/gas-fee-controller>@metamask/controller-utils": true,
"eth-query": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true,
"uuid": true
}
},
"@metamask/gas-fee-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/gas-fee-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/jazzicon": {
"globals": {
"document.createElement": true,
@ -1546,8 +1611,8 @@
},
"@metamask/message-manager": {
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager>@metamask/base-controller": true,
"@metamask/message-manager>@metamask/controller-utils": true,
"@metamask/message-manager>jsonschema": true,
"browserify>buffer": true,
"browserify>events": true,
@ -1556,6 +1621,28 @@
"uuid": true
}
},
"@metamask/message-manager>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/message-manager>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/message-manager>jsonschema": {
"packages": {
"browserify>url": true
@ -1586,8 +1673,8 @@
"console.error": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/permission-controller>@metamask/base-controller": true,
"@metamask/permission-controller>@metamask/controller-utils": true,
"@metamask/permission-controller>nanoid": true,
"deep-freeze-strict": true,
"eth-rpc-errors": true,
@ -1595,6 +1682,28 @@
"json-rpc-engine": true
}
},
"@metamask/permission-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/permission-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/permission-controller>nanoid": {
"globals": {
"crypto.getRandomValues": true
@ -1605,12 +1714,34 @@
"fetch": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/phishing-controller>@metamask/base-controller": true,
"@metamask/phishing-controller>@metamask/controller-utils": true,
"@metamask/phishing-warning>eth-phishing-detect": true,
"punycode": true
}
},
"@metamask/phishing-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/phishing-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/phishing-warning>eth-phishing-detect": {
"packages": {
"eslint>optionator>fast-levenshtein": true
@ -1647,8 +1778,14 @@
"TextEncoder": true
},
"packages": {
"@metamask/key-tree>@noble/hashes": true,
"@metamask/key-tree>@scure/base": true
"@metamask/key-tree>@scure/base": true,
"@metamask/scure-bip39>@noble/hashes": true
}
},
"@metamask/scure-bip39>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"@metamask/signature-controller": {
@ -1656,15 +1793,37 @@
"console.info": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/message-manager": true,
"@metamask/signature-controller>@metamask/base-controller": true,
"@metamask/signature-controller>@metamask/controller-utils": true,
"browserify>buffer": true,
"browserify>events": true,
"eth-rpc-errors": true,
"ethereumjs-util": true
}
},
"@metamask/signature-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/signature-controller>@metamask/controller-utils": {
"globals": {
"URL": true,
"console.error": true,
"fetch": true,
"setTimeout": true
},
"packages": {
"@metamask/controller-utils>@spruceid/siwe-parser": true,
"@metamask/utils": true,
"browserify>buffer": true,
"eslint>fast-deep-equal": true,
"eth-ens-namehash": true,
"ethereumjs-util": true,
"ethjs>ethjs-unit": true
}
},
"@metamask/smart-transactions-controller": {
"globals": {
"URLSearchParams": true,
@ -1748,7 +1907,12 @@
},
"@metamask/subject-metadata-controller": {
"packages": {
"@metamask/base-controller": true
"@metamask/subject-metadata-controller>@metamask/base-controller": true
}
},
"@metamask/subject-metadata-controller>@metamask/base-controller": {
"packages": {
"immer": true
}
},
"@metamask/utils": {
@ -1800,15 +1964,6 @@
"browserify>buffer": true
}
},
"@ngraveio/bc-ur>crc>buffer": {
"globals": {
"console": true
},
"packages": {
"base64-js": true,
"browserify>buffer>ieee754": true
}
},
"@ngraveio/bc-ur>jsbi": {
"globals": {
"define": true
@ -2535,6 +2690,16 @@
"ethjs-query>babel-runtime>core-js": true
}
},
"browserify>browserify-zlib": {
"packages": {
"browserify>assert": true,
"browserify>browserify-zlib>pako": true,
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-browserify": true,
"browserify>util": true
}
},
"browserify>buffer": {
"globals": {
"console": true
@ -2701,6 +2866,12 @@
"browserify>has>function-bind": true
}
},
"browserify>https-browserify": {
"packages": {
"browserify>stream-http": true,
"browserify>url": true
}
},
"browserify>os-browserify": {
"globals": {
"location": true,
@ -2730,6 +2901,41 @@
"readable-stream": true
}
},
"browserify>stream-http": {
"globals": {
"AbortController": true,
"Blob": true,
"MSStreamReader": true,
"ReadableStream": true,
"WritableStream": true,
"XDomainRequest": true,
"XMLHttpRequest": true,
"clearTimeout": true,
"fetch": true,
"location.protocol.search": true,
"setTimeout": true
},
"packages": {
"browserify>buffer": true,
"browserify>process": true,
"browserify>stream-http>builtin-status-codes": true,
"browserify>stream-http>readable-stream": true,
"browserify>url": true,
"pumpify>inherits": true,
"watchify>xtend": true
}
},
"browserify>stream-http>readable-stream": {
"packages": {
"browserify>browser-resolve": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>process": true,
"browserify>string_decoder": true,
"pumpify>inherits": true,
"readable-stream>util-deprecate": true
}
},
"browserify>string_decoder": {
"packages": {
"ethereumjs-wallet>safe-buffer": true
@ -2951,16 +3157,59 @@
"setInterval": true
},
"packages": {
"@ethereumjs/tx": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>crypto-browserify": true,
"browserify>events": true,
"eth-lattice-keyring>@ethereumjs/tx": true,
"eth-lattice-keyring>bn.js": true,
"eth-lattice-keyring>gridplus-sdk": true,
"eth-lattice-keyring>rlp": true
}
},
"eth-lattice-keyring>@ethereumjs/tx": {
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>bn.js": {
"globals": {
"Buffer": true
@ -2984,12 +3233,12 @@
"setTimeout": true
},
"packages": {
"@ethereumjs/common": true,
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx": true,
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,
"eth-lattice-keyring>gridplus-sdk>bignumber.js": true,
"eth-lattice-keyring>gridplus-sdk>bitwise": true,
@ -3006,6 +3255,65 @@
"lodash": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": {
"packages": {
"@ethereumjs/tx>@ethereumjs/rlp": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"@ethersproject/providers": true,
"browserify>buffer": true,
"browserify>insert-module-globals>is-buffer": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz": {
"packages": {
"browserify": true,
"browserify>buffer": true,
"eth-lattice-keyring>@ethereumjs/tx>@chainsafe/ssz>case": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@chainsafe/ssz>@chainsafe/persistent-merkle-tree": {
"globals": {
"WeakRef": true
},
"packages": {
"browserify": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>@ethereumjs/common": {
"packages": {
"@ethereumjs/common>crc-32": true,
"@ethereumjs/tx>@ethereumjs/util": true,
"browserify>buffer": true,
"browserify>events": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography": {
"globals": {
"TextDecoder": true,
"crypto": true
},
"packages": {
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": true
}
},
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx>ethereum-cryptography>@noble/hashes": {
"globals": {
"TextEncoder": true,
"crypto": true
}
},
"eth-lattice-keyring>gridplus-sdk>bignumber.js": {
"globals": {
"crypto": true,
@ -3135,22 +3443,14 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"eth-sig-util>ethereumjs-util>ethereum-cryptography": true,
"eth-sig-util>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>safe-buffer": true,
"ganache>secp256k1>elliptic": true
}
},
"eth-sig-util>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"eth-sig-util>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3189,21 +3489,13 @@
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-abi>ethereumjs-util>ethjs-util": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ganache>secp256k1>elliptic": true
}
},
"ethereumjs-abi>ethereumjs-util>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-abi>ethereumjs-util>ethjs-util": {
"packages": {
"browserify>buffer": true,
@ -3388,22 +3680,14 @@
"ethereumjs-wallet>safe-buffer": true
}
},
"ethereumjs-wallet>ethereum-cryptography": {
"packages": {
"browserify>buffer": true,
"ethereumjs-util>ethereum-cryptography>keccak": true,
"ethereumjs-util>ethereum-cryptography>secp256k1": true,
"ethereumjs-wallet>randombytes": true
}
},
"ethereumjs-wallet>ethereumjs-util": {
"packages": {
"bn.js": true,
"browserify>assert": true,
"browserify>buffer": true,
"ethereumjs-util>create-hash": true,
"ethereumjs-util>ethereum-cryptography": true,
"ethereumjs-util>rlp": true,
"ethereumjs-wallet>ethereum-cryptography": true,
"ethereumjs-wallet>ethereumjs-util>ethjs-util": true,
"ganache>secp256k1>elliptic": true
}

View File

@ -1,6 +1,6 @@
{
"name": "metamask-crx",
"version": "10.30.4",
"version": "10.31.0",
"private": true,
"repository": {
"type": "git",
@ -100,6 +100,7 @@
"resolutions": {
"@babel/core": "patch:@babel/core@npm%3A7.21.5#./.yarn/patches/@babel-core-npm-7.21.5-c72c337956.patch",
"@babel/runtime": "patch:@babel/runtime@npm%3A7.18.9#./.yarn/patches/@babel-runtime-npm-7.18.9-28ca6b5f61.patch",
"@metamask/approval-controller": "^3.0.0",
"@types/react": "^16.9.53",
"analytics-node/axios": "^0.21.2",
"ganache-core/lodash": "^4.17.21",
@ -181,7 +182,7 @@
"sass@^1.32.4": "patch:sass@npm%3A1.35.2#./.yarn/patches/sass-npm-1.35.2-6df4e15d13.patch",
"sass@^1.26.3": "patch:sass@npm%3A1.35.2#./.yarn/patches/sass-npm-1.35.2-6df4e15d13.patch",
"sass@^1.29.0": "patch:sass@npm%3A1.35.2#./.yarn/patches/sass-npm-1.35.2-6df4e15d13.patch",
"squirrelly@^8.0.8": "patch:squirrelly@npm%3A8.0.8#./.yarn/patches/squirrelly-npm-8.0.8-1d17420d8d.patch",
"squirrelly@^9.0.0": "patch:squirrelly@npm%3A9.0.0#./.yarn/patches/squirrelly-npm-9.0.0-3cf710c7bb.patch",
"stylelint@^13.6.1": "patch:stylelint@npm%3A13.6.1#./.yarn/patches/stylelint-npm-13.6.1-47aaddf62b.patch",
"luxon@^3.0.1": "patch:luxon@npm%3A3.2.1#./.yarn/patches/luxon-npm-3.2.1-56f8d97395.patch",
"luxon@^3.2.1": "patch:luxon@npm%3A3.2.1#./.yarn/patches/luxon-npm-3.2.1-56f8d97395.patch",
@ -212,19 +213,25 @@
"@ethersproject/providers": "^5.7.2",
"@formatjs/intl-relativetimeformat": "^5.2.6",
"@fortawesome/fontawesome-free": "^5.13.0",
"@keystonehq/bc-ur-registry-eth": "^0.12.1",
"@keystonehq/metamask-airgapped-keyring": "^0.9.2",
"@keystonehq/bc-ur-registry-eth": "^0.19.1",
"@keystonehq/metamask-airgapped-keyring": "^0.13.1",
"@lavamoat/snow": "^1.5.0",
"@material-ui/core": "^4.11.0",
"@metamask-institutional/portfolio-dashboard": "1.1.2",
"@metamask/address-book-controller": "^2.0.0",
"@metamask/announcement-controller": "^3.0.0",
"@metamask/approval-controller": "^2.1.0",
"@metamask-institutional/custody-controller": "0.2.6",
"@metamask-institutional/custody-keyring": "0.0.23",
"@metamask-institutional/extension": "^0.1.3",
"@metamask-institutional/institutional-features": "^1.1.8",
"@metamask-institutional/portfolio-dashboard": "^1.1.3",
"@metamask-institutional/sdk": "^0.1.17",
"@metamask-institutional/transaction-update": "^0.1.21",
"@metamask/address-book-controller": "^3.0.0",
"@metamask/announcement-controller": "^4.0.0",
"@metamask/approval-controller": "^3.0.0",
"@metamask/assets-controllers": "^7.0.0",
"@metamask/base-controller": "^2.0.0",
"@metamask/base-controller": "^3.0.0",
"@metamask/browser-passworder": "^4.1.0",
"@metamask/contract-metadata": "^2.3.1",
"@metamask/controller-utils": "^3.3.0",
"@metamask/controller-utils": "^4.0.0",
"@metamask/design-tokens": "^1.9.0",
"@metamask/desktop": "^0.3.0",
"@metamask/eth-json-rpc-infura": "^8.0.0",
@ -241,7 +248,7 @@
"@metamask/logo": "^3.1.1",
"@metamask/message-manager": "^5.0.0",
"@metamask/metamask-eth-abis": "^3.0.0",
"@metamask/notification-controller": "^2.0.0",
"@metamask/notification-controller": "^3.0.0",
"@metamask/obs-store": "^8.1.0",
"@metamask/permission-controller": "^3.2.0",
"@metamask/phishing-controller": "^3.0.0",
@ -524,7 +531,7 @@
"sinon": "^9.0.0",
"source-map": "^0.7.2",
"source-map-explorer": "^2.4.2",
"squirrelly": "^8.0.8",
"squirrelly": "^9.0.0",
"storybook": "^7.0.11",
"storybook-dark-mode": "^3.0.0",
"stream-browserify": "^3.0.0",

View File

@ -165,6 +165,18 @@ export const IN_PROGRESS_TRANSACTION_STATUSES = [
TransactionStatus.pending,
];
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
/**
* Status for finalized transactions.
*/
export const FINALIZED_TRANSACTION_STATUSES = [
TransactionStatus.rejected,
TransactionStatus.failed,
TransactionStatus.dropped,
TransactionStatus.confirmed,
];
///: END:ONLY_INCLUDE_IN
/**
* Transaction Group Status is a MetaMask construct to track the status of groups
* of transactions.

View File

@ -158,6 +158,7 @@ function defaultFixture() {
[CHAIN_IDS.GOERLI]: true,
[CHAIN_IDS.LOCALHOST]: true,
},
snapsInstallPrivacyWarningShown: true,
},
CachedBalancesController: {
cachedBalances: {

View File

@ -481,10 +481,60 @@ const defaultGanacheOptions = {
const SERVICE_WORKER_URL = 'chrome://inspect/#service-workers';
const sendTransaction = async (driver, recipientAddress, quantity) => {
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill('[data-testid="ens-input"]', recipientAddress);
await driver.fill('.unit-input__input', quantity);
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.clickElement('[data-testid="home__activity-tab"]');
await driver.findElement('.transaction-list-item');
};
const findAnotherAccountFromAccountList = async (
driver,
itemNumber,
accountName,
) => {
await driver.clickElement('.account-menu__icon');
const accountMenuItemSelector = `.account-menu__account:nth-child(${itemNumber})`;
const fourthAccountName = await driver.findElement(
`${accountMenuItemSelector} .account-menu__name`,
);
assert.equal(await fourthAccountName.getText(), accountName);
return accountMenuItemSelector;
};
const TEST_SEED_PHRASE =
'forum vessel pink push lonely enact gentle tail admit parrot grunt dress';
const TEST_SEED_PHRASE_TWO =
'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent';
// Usually happens when onboarded to make sure the state is retrieved from metamaskState properly
const assertAccountBalanceForDOM = async (driver, ganacheServer) => {
const balance = await ganacheServer.getBalance();
const balanceElement = await driver.findElement(
'[data-testid="eth-overview__primary-currency"]',
);
assert.equal(`${balance}\nETH`, await balanceElement.getText());
};
// Usually happens after txn is made
const locateAccountBalanceDOM = async (driver, ganacheServer) => {
const balance = await ganacheServer.getBalance();
await driver.waitForSelector({
css: '[data-testid="eth-overview__primary-currency"]',
text: `${balance} ETH`,
});
};
module.exports = {
DAPP_URL,
DAPP_ONE_URL,
SERVICE_WORKER_URL,
TEST_SEED_PHRASE,
TEST_SEED_PHRASE_TWO,
getWindowHandles,
convertToHexValue,
tinyDelayMs,
@ -503,4 +553,8 @@ module.exports = {
mockPhishingDetection,
setupPhishingDetectionMocks,
defaultGanacheOptions,
sendTransaction,
findAnotherAccountFromAccountList,
assertAccountBalanceForDOM,
locateAccountBalanceDOM,
};

View File

@ -4,6 +4,7 @@ const path = require('path');
const enLocaleMessages = require('../../app/_locales/en/messages.json');
const createStaticServer = require('../../development/create-static-server');
const {
TEST_SEED_PHRASE_TWO,
tinyDelayMs,
regularDelayMs,
largeDelayMs,
@ -21,9 +22,6 @@ describe('MetaMask', function () {
let dappServer;
let tokenAddress;
const testSeedPhrase =
'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent';
this.bail(true);
let failed = false;
@ -151,7 +149,7 @@ describe('MetaMask', function () {
await driver.pasteIntoField(
'[data-testid="import-srp__srp-word-0"]',
testSeedPhrase,
TEST_SEED_PHRASE_TWO,
);
await driver.fill('#password', 'correct horse battery staple');

View File

@ -1,72 +0,0 @@
const { strict: assert } = require('assert');
const {
convertToHexValue,
withFixtures,
openDapp,
SERVICE_WORKER_URL,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('MV3 - Dapp interactions', function () {
let windowHandles;
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
},
],
concurrent: { port: 8546, chainId: 1338 },
};
it('should continue to support dapp interactions after service worker re-start', async function () {
await withFixtures(
{
dapp: true,
fixtures: new FixtureBuilder()
.withPermissionControllerConnectedToTestDapp()
.build(),
ganacheOptions: {
...ganacheOptions,
},
title: this.test.title,
},
async ({ driver }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await openDapp(driver);
// Terminate Service Worker
await driver.openNewPage(SERVICE_WORKER_URL);
await driver.clickElement({
text: 'Service workers',
tag: 'button',
});
await driver.clickElement({
text: 'terminate',
tag: 'span',
});
// Trigger Notification
windowHandles = await driver.getAllWindowHandles();
await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles);
await driver.clickElement('#addEthereumChain');
await driver.waitUntilXWindowHandles(4);
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
const notification = await driver.isElementPresent({
text: 'Allow this site to add a network?',
tag: 'h3',
});
assert.ok(notification, 'Dapp action does not appear in Metamask');
},
);
});
});

View File

@ -1,3 +1,3 @@
module.exports = {
TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/test-snaps/5.4.0/',
TEST_SNAPS_WEBSITE_URL: 'https://metamask.github.io/test-snaps/5.5.0/',
};

View File

@ -0,0 +1,106 @@
const { strict: assert } = require('assert');
const { withFixtures } = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
const { TEST_SNAPS_WEBSITE_URL } = require('./enums');
describe('Test Snap networkAccess', function () {
it('test the network-access endowment', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: 25000000000000000000,
},
],
};
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions,
failOnConsoleError: false,
title: this.test.title,
},
async ({ driver }) => {
await driver.navigate();
// enter pw into extension
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
// navigate to test snaps page and connect to dialog snap
await driver.openNewPage(TEST_SNAPS_WEBSITE_URL);
await driver.delay(1000);
const dialogButton = await driver.findElement(
'#connectNetworkAccessSnap',
);
await driver.scrollToElement(dialogButton);
await driver.delay(1000);
await driver.clickElement('#connectNetworkAccessSnap');
await driver.delay(1000);
// switch to metamask extension and click connect
let windowHandles = await driver.waitUntilXWindowHandles(
3,
1000,
10000,
);
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.clickElement({
text: 'Connect',
tag: 'button',
});
await driver.waitForSelector({ text: 'Approve & install' });
await driver.clickElement({
text: 'Approve & install',
tag: 'button',
});
await driver.waitForSelector({ text: 'Ok' });
await driver.clickElement({
text: 'Ok',
tag: 'button',
});
// switch to test snaps tab
await driver.switchToWindowWithTitle('Test Snaps', windowHandles);
// wait for npm installation success
await driver.waitForSelector({
css: '#connectNetworkAccessSnap',
text: 'Reconnect to networkAccess Snap',
});
// click on alert dialog
await driver.clickElement('#sendNetworkAccessTest');
await driver.delay(500);
// switch to dialog popup
windowHandles = await driver.waitUntilXWindowHandles(3, 1000, 10000);
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
);
await driver.delay(500);
// check dialog contents
const result = await driver.findElement('.snap-ui-renderer__panel');
await driver.scrollToElement(result);
await driver.delay(500);
assert.equal(await result.getText(), 'FETCHED_SUCCESSFULLY');
// click ok button
await driver.clickElement({
text: 'Ok',
tag: 'button',
});
},
);
});
});

View File

@ -1,16 +1,17 @@
const { strict: assert } = require('assert');
const {
TEST_SEED_PHRASE,
convertToHexValue,
withFixtures,
regularDelayMs,
completeImportSRPOnboardingFlow,
sendTransaction,
findAnotherAccountFromAccountList,
} = require('../helpers');
const enLocaleMessages = require('../../../app/_locales/en/messages.json');
const FixtureBuilder = require('../fixture-builder');
describe('Add account', function () {
const testSeedPhrase =
'forum vessel pink push lonely enact gentle tail admit parrot grunt dress';
const testPassword = 'correct horse battery staple';
const ganacheOptions = {
accounts: [
@ -21,8 +22,8 @@ describe('Add account', function () {
},
],
};
const firstAccount = '0x0Cc5261AB8cE458dc977078A3623E2BaDD27afD3';
const secondAccount = '0x3ED0eE22E0685Ebbf07b2360A8331693c413CC59';
const thirdAccount = '0xD38d853771Fb546bd8B18b2F3638491bC0B0E906';
it('should display correct new account name after create', async function () {
await withFixtures(
@ -50,7 +51,7 @@ describe('Add account', function () {
);
});
it('should add the same account addresses when a secret recovery phrase is imported, the account is locked, and the same secret recovery phrase is imported again', async function () {
it('should not affect public address when using secret recovery phrase to recover account with non-zero balance', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder({ onboarding: true }).build(),
@ -61,64 +62,40 @@ describe('Add account', function () {
async ({ driver }) => {
await driver.navigate();
// On boarding with 1st account
await completeImportSRPOnboardingFlow(
driver,
testSeedPhrase,
TEST_SEED_PHRASE,
testPassword,
);
await driver.clickElement('.account-menu__icon');
// Check address of 1st account
const firstAccountPublicAddress = await checkAccountDetails(driver);
assert.equal(firstAccountPublicAddress, firstAccount);
await driver.delay(regularDelayMs);
// Create a new account
await driver.findClickableElement('.account-menu__icon');
await driver.clickElement('[data-testid="account-menu-icon"]');
await driver.clickElement({ text: 'Create account', tag: 'div' });
await driver.fill('.new-account-create-form input', '2nd account');
await driver.clickElement({ text: 'Create', tag: 'button' });
await driver.clickElement(
'[data-testid="account-options-menu-button"]',
);
await driver.clickElement(
'[data-testid="account-options-menu__account-details"]',
// Check address of 2nd account
const secondAccountPublicAddress = await checkAccountDetails(driver);
assert.strictEqual(secondAccountPublicAddress, secondAccount);
await driver.delay(regularDelayMs);
// Give 2nd locally account some balance so it will not be removed after recovering SRP
const accountOneSelector = await findAnotherAccountFromAccountList(
driver,
1,
'Account 1',
);
await driver.clickElement(accountOneSelector);
await sendTransaction(driver, secondAccount, '2.8');
const detailsModal = await driver.findVisibleElement('span .modal');
// get the public address for the "second account"
await driver.waitForSelector('.qr-code__address');
const secondAccountAddress = await driver.findElement({
text: secondAccount,
tag: 'div',
});
const secondAccountPublicAddress = await secondAccountAddress.getText();
await driver.clickElement('.account-modal__close');
await detailsModal.waitForElementState('hidden');
// generate a third accound
await driver.clickElement('.account-menu__icon');
await driver.clickElement({ text: 'Create account', tag: 'div' });
await driver.fill('.new-account-create-form input', '3rd account');
await driver.clickElement({ text: 'Create', tag: 'button' });
await driver.clickElement(
'[data-testid="account-options-menu-button"]',
);
await driver.clickElement(
'[data-testid="account-options-menu__account-details"]',
);
// get the public address for the "third account"
const secondDetailsModal = await driver.findVisibleElement(
'span .modal',
);
await driver.waitForSelector('.qr-code__address');
const thirdAccountAddress = await driver.findElement({
text: thirdAccount,
tag: 'div',
});
const thirdAccountPublicAddress = await thirdAccountAddress.getText();
await driver.clickElement('.account-modal__close');
await secondDetailsModal.waitForElementState('hidden');
// lock account
// Lock the account
await driver.clickElement('.account-menu__icon');
await driver.delay(regularDelayMs);
@ -128,7 +105,7 @@ describe('Add account', function () {
await lockButton.click();
await driver.delay(regularDelayMs);
// restore same seed phrase
// Recover via SRP in "forget password" option
const restoreSeedLink = await driver.findClickableElement(
'.unlock-page__link',
);
@ -138,7 +115,7 @@ describe('Add account', function () {
await driver.pasteIntoField(
'[data-testid="import-srp__srp-word-0"]',
testSeedPhrase,
TEST_SEED_PHRASE,
);
await driver.fill('#password', 'correct horse battery staple');
@ -147,59 +124,27 @@ describe('Add account', function () {
text: enLocaleMessages.restore.message,
tag: 'button',
});
// Land in 1st account home page
await driver.findElement('.home__main-view');
// Check address of 1st account
const restoredFirstAccountPublicAddress = await checkAccountDetails(
driver,
);
assert.equal(restoredFirstAccountPublicAddress, firstAccount);
await driver.delay(regularDelayMs);
// recreate a "2nd account"
await driver.clickElement('.account-menu__icon');
await driver.clickElement({ text: 'Create account', tag: 'div' });
await driver.fill('.new-account-create-form input', '2nd account');
await driver.clickElement({ text: 'Create', tag: 'button' });
await driver.clickElement(
'[data-testid="account-options-menu-button"]',
// Check address of 2nd account
const accountTwoSelector = await findAnotherAccountFromAccountList(
driver,
2,
'Account 2',
);
await driver.clickElement(
'[data-testid="account-options-menu__account-details"]',
);
const thirdDetailsModal = await driver.findVisibleElement(
'span .modal',
);
// get the public address for the "second account"
await driver.waitForSelector('.qr-code__address');
const recreatedSecondAccountAddress = await driver.findElement({
text: secondAccount,
tag: 'div',
});
assert.equal(
await recreatedSecondAccountAddress.getText(),
secondAccountPublicAddress,
);
await driver.clickElement('.account-modal__close');
await thirdDetailsModal.waitForElementState('hidden');
// re-generate a third accound
await driver.clickElement('.account-menu__icon');
await driver.clickElement({ text: 'Create account', tag: 'div' });
await driver.fill('.new-account-create-form input', '3rd account');
await driver.clickElement({ text: 'Create', tag: 'button' });
await driver.clickElement(
'[data-testid="account-options-menu-button"]',
);
await driver.clickElement(
'[data-testid="account-options-menu__account-details"]',
);
// get the public address for the "third account"
const recreatedThirdAccountAddress = await driver.findElement({
text: thirdAccount,
tag: 'div',
});
assert.strictEqual(
await recreatedThirdAccountAddress.getText(),
thirdAccountPublicAddress,
await driver.clickElement(accountTwoSelector);
const restoredSecondAccountPublicAddress = await checkAccountDetails(
driver,
);
assert.equal(restoredSecondAccountPublicAddress, secondAccount);
},
);
});
@ -264,3 +209,19 @@ describe('Add account', function () {
);
});
});
async function checkAccountDetails(driver) {
await driver.clickElement('[data-testid="account-options-menu-button"]');
await driver.clickElement(
'[data-testid="account-options-menu__account-details"]',
);
await driver.findVisibleElement('.account-details-modal');
// get the public address for the "second account"
const accountDOM = await driver.findElement('.qr-code__address');
const accountAddress = await accountDOM.getText();
await driver.clickElement('.account-modal__close');
await driver.waitForElementNotPresent('.account-details-modal ');
return accountAddress;
}

View File

@ -1,5 +1,9 @@
const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures } = require('../helpers');
const {
convertToHexValue,
withFixtures,
assertAccountBalanceForDOM,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Address Book', function () {
@ -12,6 +16,7 @@ describe('Address Book', function () {
},
],
};
it('Sends to an address book entry', async function () {
await withFixtures(
{
@ -33,11 +38,12 @@ describe('Address Book', function () {
ganacheOptions,
title: this.test.title,
},
async ({ driver }) => {
async ({ driver, ganacheServer }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await assertAccountBalanceForDOM(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
const recipientRowTitle = await driver.findElement(
'.send__select-recipient-wrapper__group-item__title',

View File

@ -1,5 +1,9 @@
const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures, openDapp } = require('../helpers');
const {
convertToHexValue,
withFixtures,
openDapp,
locateAccountBalanceDOM,
} = require('../helpers');
const { SMART_CONTRACTS } = require('../seeder/smart-contracts');
const FixtureBuilder = require('../fixture-builder');
@ -89,12 +93,7 @@ describe('Deploy contract and call contract methods', function () {
// renders the correct ETH balance
await driver.switchToWindow(extension);
const balance = await ganacheServer.getBalance();
const balanceElement = await driver.waitForSelector({
css: '[data-testid="eth-overview__primary-currency"]',
text: balance,
});
assert.equal(`${balance}\nETH`, await balanceElement.getText());
await locateAccountBalanceDOM(driver, ganacheServer);
},
);
});

View File

@ -1,28 +1,29 @@
const { strict: assert } = require('assert');
const path = require('path');
const {
TEST_SEED_PHRASE,
convertToHexValue,
withFixtures,
regularDelayMs,
largeDelayMs,
completeImportSRPOnboardingFlow,
completeImportSRPOnboardingFlowWordByWord,
findAnotherAccountFromAccountList,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
const ganacheOptions = {
accounts: [
{
secretKey:
'0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9',
balance: convertToHexValue(25000000000000000000),
},
],
};
describe('MetaMask Import UI', function () {
it('Importing wallet using Secret Recovery Phrase', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9',
balance: convertToHexValue(25000000000000000000),
},
],
};
const testSeedPhrase =
'forum vessel pink push lonely enact gentle tail admit parrot grunt dress';
const testPassword = 'correct horse battery staple';
const testAddress = '0x0Cc5261AB8cE458dc977078A3623E2BaDD27afD3';
@ -38,7 +39,7 @@ describe('MetaMask Import UI', function () {
await completeImportSRPOnboardingFlow(
driver,
testSeedPhrase,
TEST_SEED_PHRASE,
testPassword,
);
@ -130,17 +131,6 @@ describe('MetaMask Import UI', function () {
});
it('Importing wallet using Secret Recovery Phrase with pasting word by word', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9',
balance: convertToHexValue(25000000000000000000),
},
],
};
const testSeedPhrase =
'forum vessel pink push lonely enact gentle tail admit parrot grunt dress';
const testPassword = 'correct horse battery staple';
const testAddress = '0x0Cc5261AB8cE458dc977078A3623E2BaDD27afD3';
@ -156,7 +146,7 @@ describe('MetaMask Import UI', function () {
await completeImportSRPOnboardingFlowWordByWord(
driver,
testSeedPhrase,
TEST_SEED_PHRASE,
testPassword,
);
@ -177,15 +167,6 @@ describe('MetaMask Import UI', function () {
});
it('Import Account using private key', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9',
balance: convertToHexValue(25000000000000000000),
},
],
};
const testPrivateKey1 =
'14abe6f4aab7f9f626fe981c864d0adeb5685f289ac9270c27b8fd790b4235d6';
const testPrivateKey2 =
@ -221,13 +202,12 @@ describe('MetaMask Import UI', function () {
assert.equal(await importedAccountName.getText(), 'Account 4');
// should show the imported label
await driver.clickElement('.account-menu__icon');
// confirm 4th account is account 4, as expected
const accountMenuItemSelector = '.account-menu__account:nth-child(4)';
const fourthAccountName = await driver.findElement(
`${accountMenuItemSelector} .account-menu__name`,
const accountMenuItemSelector = await findAnotherAccountFromAccountList(
driver,
4,
'Account 4',
);
assert.equal(await fourthAccountName.getText(), 'Account 4');
// confirm label is present on the same menu item
const importedLabel = await driver.findElement(
`${accountMenuItemSelector} .keyring-label`,
@ -284,16 +264,6 @@ describe('MetaMask Import UI', function () {
});
it('Import Account using json file', async function () {
const ganacheOptions = {
accounts: [
{
secretKey:
'0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9',
balance: convertToHexValue(25000000000000000000),
},
],
};
await withFixtures(
{
fixtures: new FixtureBuilder()
@ -336,13 +306,13 @@ describe('MetaMask Import UI', function () {
assert.equal(await importedAccountName.getText(), 'Account 4');
// should show the imported label
await driver.clickElement('.account-menu__icon');
// confirm 4th account is account 4, as expected
const accountMenuItemSelector = '.account-menu__account:nth-child(4)';
const fourthAccountName = await driver.findElement(
`${accountMenuItemSelector} .account-menu__name`,
const accountMenuItemSelector = await findAnotherAccountFromAccountList(
driver,
4,
'Account 4',
);
assert.equal(await fourthAccountName.getText(), 'Account 4');
// confirm label is present on the same menu item
const importedLabel = await driver.findElement(
`${accountMenuItemSelector} .keyring-label`,
@ -360,15 +330,6 @@ describe('MetaMask Import UI', function () {
it('Import Account using private key of an already active account should result in an error', async function () {
const testPrivateKey =
'0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9';
const ganacheOptions = {
accounts: [
{
secretKey: testPrivateKey,
balance: convertToHexValue(25000000000000000000),
},
],
};
await withFixtures(
{
fixtures: new FixtureBuilder()
@ -400,44 +361,36 @@ describe('MetaMask Import UI', function () {
);
});
it('Connects to a Hardware wallet', async function () {
const ganacheOptions = {
accounts: [
if (process.env.ENABLE_MV3) {
it('Connects to a Hardware wallet for trezor', async function () {
await withFixtures(
{
secretKey:
'0x53CB0AB5226EEBF4D872113D98332C1555DC304443BEE1CF759D15798D3C55A9',
balance: convertToHexValue(25000000000000000000),
fixtures: new FixtureBuilder().build(),
ganacheOptions,
title: this.test.title,
},
],
};
async ({ driver }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
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);
// choose Connect hardware wallet from the account menu
await driver.clickElement('.account-menu__icon');
await driver.clickElement({
text: 'Connect hardware wallet',
tag: 'div',
});
await driver.delay(regularDelayMs);
// choose Connect hardware wallet from the account menu
await driver.clickElement('.account-menu__icon');
await driver.clickElement({
text: 'Connect hardware wallet',
tag: 'div',
});
await driver.delay(regularDelayMs);
// should open the TREZOR Connect popup
await driver.clickElement('.hw-connect__btn:nth-of-type(2)');
await driver.delay(largeDelayMs * 2);
await driver.clickElement({ text: 'Continue', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
const allWindows = await driver.getAllWindowHandles();
assert.equal(allWindows.length, 2);
},
);
});
// should open the TREZOR Connect popup
await driver.clickElement('.hw-connect__btn:nth-of-type(2)');
await driver.delay(largeDelayMs * 2);
await driver.clickElement({ text: 'Continue', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
const allWindows = await driver.getAllWindowHandles();
assert.equal(allWindows.length, 2);
},
);
});
}
});

View File

@ -1,5 +1,10 @@
const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures } = require('../helpers');
const {
TEST_SEED_PHRASE_TWO,
convertToHexValue,
withFixtures,
assertAccountBalanceForDOM,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('MetaMask Responsive UI', function () {
@ -76,8 +81,6 @@ describe('MetaMask Responsive UI', function () {
it('Importing existing wallet from lock page', async function () {
const driverOptions = { openDevToolsForTabs: true };
const testSeedPhrase =
'phrase upgrade clock rough situate wedding elder clever doctor stamp excess tent';
await withFixtures(
{
@ -98,7 +101,7 @@ describe('MetaMask Responsive UI', function () {
await driver.pasteIntoField(
'[data-testid="import-srp__srp-word-0"]',
testSeedPhrase,
TEST_SEED_PHRASE_TWO,
);
await driver.fill('#password', 'correct horse battery staple');
@ -106,11 +109,7 @@ describe('MetaMask Responsive UI', function () {
await driver.press('#confirm-password', driver.Key.ENTER);
// balance renders
const balance = await ganacheServer.getBalance();
await driver.waitForSelector({
css: '[data-testid="eth-overview__primary-currency"]',
text: `${balance} ETH`,
});
await assertAccountBalanceForDOM(driver, ganacheServer);
},
);
});

View File

@ -1,5 +1,10 @@
const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures, openDapp } = require('../helpers');
const {
convertToHexValue,
withFixtures,
openDapp,
locateAccountBalanceDOM,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Navigate transactions', function () {
@ -227,11 +232,7 @@ describe('Navigate transactions', function () {
// reject transactions
await driver.clickElement({ text: 'Reject 4', tag: 'a' });
await driver.clickElement({ text: 'Reject all', tag: 'button' });
const balance = await ganacheServer.getBalance();
const balanceElement = await driver.findElement(
'[data-testid="eth-overview__primary-currency"]',
);
assert.equal(`${balance}\nETH`, await balanceElement.getText());
await locateAccountBalanceDOM(driver, ganacheServer);
},
);
});

View File

@ -1,5 +1,9 @@
const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures } = require('../helpers');
const {
convertToHexValue,
withFixtures,
regularDelayMs,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Gas API fallback', function () {
@ -79,6 +83,9 @@ describe('Gas API fallback', function () {
await driver.clickElement({ text: 'Next', tag: 'button' });
await driver.delay(regularDelayMs);
await driver.findElement('.transaction-alerts');
const error = await driver.isElementPresent({
text: 'Network is busy. Gas prices are high and estimates are less accurate.',
});

View File

@ -1,6 +1,7 @@
const { strict: assert } = require('assert');
const { By } = require('selenium-webdriver');
const {
TEST_SEED_PHRASE,
convertToHexValue,
withFixtures,
completeCreateNewWalletOnboardingFlow,
@ -8,12 +9,11 @@ const {
importSRPOnboardingFlow,
importWrongSRPOnboardingFlow,
testSRPDropdownIterations,
assertAccountBalanceForDOM,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('MetaMask onboarding', function () {
const testSeedPhrase =
'forum vessel pink push lonely enact gentle tail admit parrot grunt dress';
const testPassword = 'correct horse battery staple';
const wrongSeedPhrase =
'test test test test test test test test test test test test';
@ -63,7 +63,7 @@ describe('MetaMask onboarding', function () {
await completeImportSRPOnboardingFlow(
driver,
testSeedPhrase,
TEST_SEED_PHRASE,
testPassword,
);
@ -164,7 +164,7 @@ describe('MetaMask onboarding', function () {
// Check that the error message is displayed for the password fields
await driver.isElementPresent(
// eslint-disable-next-line prettier/prettier
{ text: 'Passwords don\'t match', tag: 'h6' },
{ text: "Passwords don't match", tag: 'h6' },
true,
);
@ -188,7 +188,7 @@ describe('MetaMask onboarding', function () {
async ({ driver }) => {
await driver.navigate();
await importSRPOnboardingFlow(driver, testSeedPhrase, testPassword);
await importSRPOnboardingFlow(driver, TEST_SEED_PHRASE, testPassword);
// Verify site
assert.equal(
await driver.isElementPresent({
@ -267,7 +267,7 @@ describe('MetaMask onboarding', function () {
async ({ driver, secondaryGanacheServer }) => {
await driver.navigate();
await importSRPOnboardingFlow(driver, testSeedPhrase, testPassword);
await importSRPOnboardingFlow(driver, TEST_SEED_PHRASE, testPassword);
// Add custome network localhost 8546 during onboarding
await driver.clickElement({ text: 'Advanced configuration', tag: 'a' });
@ -305,11 +305,7 @@ describe('MetaMask onboarding', function () {
);
assert.equal(await networkDisplay.getText(), networkName);
const balance = await secondaryGanacheServer.getBalance();
const balanceElement = await driver.findElement(
'[data-testid="eth-overview__primary-currency"]',
);
assert.equal(`${balance}\nETH`, await balanceElement.getText());
await assertAccountBalanceForDOM(driver, secondaryGanacheServer);
},
);
});

View File

@ -1,6 +1,11 @@
const { strict: assert } = require('assert');
const { SMART_CONTRACTS } = require('../seeder/smart-contracts');
const { convertToHexValue, withFixtures, openDapp } = require('../helpers');
const {
convertToHexValue,
withFixtures,
openDapp,
assertAccountBalanceForDOM,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Send ETH from inside MetaMask using default gas', function () {
@ -20,11 +25,13 @@ describe('Send ETH from inside MetaMask using default gas', function () {
ganacheOptions,
title: this.test.title,
},
async ({ driver }) => {
async ({ driver, ganacheServer }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await assertAccountBalanceForDOM(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(

View File

@ -1,5 +1,9 @@
const { strict: assert } = require('assert');
const { convertToHexValue, withFixtures } = require('../helpers');
const {
convertToHexValue,
withFixtures,
assertAccountBalanceForDOM,
} = require('../helpers');
const { SMART_CONTRACTS } = require('../seeder/smart-contracts');
const FixtureBuilder = require('../fixture-builder');
@ -133,11 +137,7 @@ describe('Send ERC20 to a 40 character hexadecimal address', function () {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
const balanceAfterDeployment = await ganacheServer.getBalance();
await driver.waitForSelector({
css: '[data-testid="eth-overview__primary-currency"]',
text: `${balanceAfterDeployment} ETH`,
});
await assertAccountBalanceForDOM(driver, ganacheServer);
// Send TST
await driver.clickElement('[data-testid="home__asset-tab"]');
@ -198,11 +198,8 @@ describe('Send ERC20 to a 40 character hexadecimal address', function () {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
const balanceAfterDeployment = await ganacheServer.getBalance();
await driver.waitForSelector({
css: '[data-testid="eth-overview__primary-currency"]',
text: `${balanceAfterDeployment} ETH`,
});
await assertAccountBalanceForDOM(driver, ganacheServer);
// Send TST
await driver.clickElement('[data-testid="home__asset-tab"]');

View File

@ -1,4 +1,9 @@
const { convertToHexValue, withFixtures } = require('../helpers');
const {
convertToHexValue,
withFixtures,
sendTransaction,
assertAccountBalanceForDOM,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Simple send', function () {
@ -18,20 +23,17 @@ describe('Simple send', function () {
ganacheOptions,
title: this.test.title,
},
async ({ driver }) => {
async ({ driver, ganacheServer }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'[data-testid="ens-input"]',
await assertAccountBalanceForDOM(driver, ganacheServer);
await sendTransaction(
driver,
'0x985c30949c92df7a0bd42e0f3e3d539ece98db24',
'1',
);
await driver.fill('.unit-input__input', '1');
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.clickElement('[data-testid="page-container-footer-next"]');
await driver.clickElement('[data-testid="home__activity-tab"]');
await driver.findElement('.transaction-list-item');
},
);
});

View File

@ -79,6 +79,7 @@ exports[`AccountListItem Component render should match snapshot 1`] = `
>
<div
class="account-mismatch-warning__tooltip-container-icon"
data-testid="account-mismatch-warning-tooltip"
>
<svg
class="info-icon info-icon--warning"

View File

@ -5,11 +5,6 @@ import { renderWithProvider } from '../../../../test/lib/render-helpers';
import mockState from '../../../../test/data/mock-state.json';
import AccountListItem from './account-list-item';
jest.mock('../../../../shared/modules/hexstring-utils', () => ({
...jest.requireActual('../../../../shared/modules/hexstring-utils'),
toChecksumHexAddress: jest.fn(() => 'mockCheckSumAddress'),
}));
describe('AccountListItem Component', () => {
const store = configureStore()(mockState);
@ -93,7 +88,7 @@ describe('AccountListItem Component', () => {
<AccountListItem {...props} />,
store,
);
expect(queryByText('mockCheckSumAddress')).not.toBeInTheDocument();
expect(queryByText('0xmockAddress')).not.toBeInTheDocument();
const displayAddressProps = {
...props,
@ -101,7 +96,23 @@ describe('AccountListItem Component', () => {
};
rerender(<AccountListItem {...displayAddressProps} />);
expect(queryByText('mockCheckSumAddress')).toBeInTheDocument();
expect(queryByText('0xmockAddress')).toBeInTheDocument();
});
it('render without <AccountMismatchWarning /> if hideDefaultMismatchWarning is true', () => {
const { getByTestId, rerender } = renderWithProvider(
<AccountListItem {...props} />,
store,
);
const infoIcon = getByTestId('account-mismatch-warning-tooltip');
expect(infoIcon).toBeInTheDocument();
rerender(<AccountListItem {...props} hideDefaultMismatchWarning />);
expect(infoIcon).not.toBeInTheDocument();
});
});
});

View File

@ -10,9 +10,18 @@ export default function AccountListItem({
displayAddress = false,
handleClick,
icon = null,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
hideDefaultMismatchWarning = false,
///: END:ONLY_INCLUDE_IN
}) {
const { name, address, balance } = account || {};
let showDefaultMismatchWarning = true;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
showDefaultMismatchWarning = !hideDefaultMismatchWarning;
///: END:ONLY_INCLUDE_IN
return (
<div
className={`account-list-item ${className}`}
@ -34,9 +43,10 @@ export default function AccountListItem({
{icon}
</div>
) : null}
<AccountMismatchWarning address={address} />
{showDefaultMismatchWarning && (
<AccountMismatchWarning address={address} />
)}
</div>
{displayAddress && name && (
<div className="account-list-item__account-address">
{toChecksumHexAddress(address)}
@ -71,4 +81,10 @@ AccountListItem.propTypes = {
* Pass icon component to be displayed. Currently not used
*/
icon: PropTypes.node,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
/**
* MMI Prop, will hide the default AccountMismatchWarning when needed
*/
hideDefaultMismatchWarning: PropTypes.bool,
///: END:ONLY_INCLUDE_IN
};

View File

@ -41,7 +41,6 @@
@import 'snaps/snap-ui-markdown/index';
@import 'snaps/snap-delineator/index';
@import 'snaps/snap-settings-card/index';
@import 'snaps/update-snap-permission-list/index';
@import 'snaps/copyable/index';
@import 'snaps/snap-version/index';
@import 'gas-details-item/index';

View File

@ -8,7 +8,7 @@ import {
AlignItems,
FLEX_DIRECTION,
JustifyContent,
TEXT_ALIGN,
TextAlign,
TextColor,
TextVariant,
} from '../../../../helpers/constants/design-system';
@ -53,7 +53,7 @@ export const SnapInsight = ({ transaction, origin, chainId, selectedSnap }) => {
marginBottom={hasNoData && 12}
alignItems={hasNoData && AlignItems.center}
justifyContent={hasNoData && JustifyContent.center}
textAlign={hasNoData && TEXT_ALIGN.CENTER}
textAlign={hasNoData && TextAlign.Center}
className="snap-insight"
>
{!loading && !error && (

View File

@ -13,7 +13,7 @@ import {
AlignItems,
DISPLAY,
FLEX_DIRECTION,
TEXT_ALIGN,
TextAlign,
TextVariant,
JustifyContent,
Size,
@ -273,7 +273,7 @@ export default function CustomSpendingCap({
marginLeft="auto"
paddingRight={4}
paddingBottom={2}
textAlign={TEXT_ALIGN.END}
textAlign={TextAlign.End}
className={classnames('custom-spending-cap__max', {
'custom-spending-cap__max--with-error-message': error,
})}

View File

@ -5,7 +5,7 @@ import { useI18nContext } from '../../../hooks/useI18nContext';
import { IMPORT_TOKEN_ROUTE } from '../../../helpers/constants/routes';
import Button from '../../ui/button';
import Box from '../../ui/box/box';
import { TEXT_ALIGN } from '../../../helpers/constants/design-system';
import { TextAlign } from '../../../helpers/constants/design-system';
import { detectNewTokens } from '../../../store/actions';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import {
@ -33,7 +33,7 @@ export default function ImportTokenLink() {
Boolean(process.env.IN_TEST);
return (
<Box className="import-token-link" textAlign={TEXT_ALIGN.CENTER}>
<Box className="import-token-link" textAlign={TextAlign.Center}>
{isTokenDetectionAvailable && (
<>
<Button

View File

@ -23,7 +23,7 @@ import { BannerAlert, ButtonLink, Text } from '../../component-library';
import { useI18nContext } from '../../../hooks/useI18nContext';
import {
SEVERITIES,
TEXT_ALIGN,
TextAlign,
TextColor,
} from '../../../helpers/constants/design-system';
import {
@ -150,7 +150,7 @@ export default function LedgerInstructionField({ showDataInstruction }) {
{renderInstructionStep(
<span>
<ButtonLink
textAlign={TEXT_ALIGN.LEFT}
textAlign={TextAlign.Left}
onClick={async () => {
if (environmentTypeIsFullScreen) {
window.location.reload();
@ -167,7 +167,7 @@ export default function LedgerInstructionField({ showDataInstruction }) {
{renderInstructionStep(
<span>
<ButtonLink
textAlign={TEXT_ALIGN.LEFT}
textAlign={TextAlign.Left}
onClick={async () => {
if (environmentTypeIsFullScreen) {
const connectedDevices =

View File

@ -79,6 +79,7 @@ export default class LoadingNetworkScreen extends PureComponent {
<Popover
onClose={() => {
window.clearTimeout(this.cancelCallTimeout);
this.setState({ showErrorScreen: false });
}}
centerTitle
title={
@ -100,6 +101,7 @@ export default class LoadingNetworkScreen extends PureComponent {
<ButtonSecondary
onClick={() => {
window.clearTimeout(this.cancelCallTimeout);
this.setState({ showErrorScreen: false });
showNetworkDropdown();
}}
variant={TextVariant.bodySm}

View File

@ -5,6 +5,9 @@ import { useDispatch, useSelector } from 'react-redux';
import { getAccountLink } from '@metamask/etherscan-link';
import { showModal } from '../../../store/actions';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { mmiActionsFactory } from '../../../store/institutional/institution-background';
///: END:ONLY_INCLUDE_IN
import {
CONNECTED_ROUTE,
NETWORKS_ROUTE,
@ -17,10 +20,16 @@ import {
getCurrentKeyring,
getRpcPrefsForCurrentProvider,
getSelectedIdentity,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getMetaMaskAccountsOrdered,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils';
///: END:ONLY_INCLUDE_IN
import { KeyringType } from '../../../../shared/constants/keyring';
import {
MetaMetricsEventCategory,
@ -46,6 +55,12 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
const trackEvent = useContext(MetaMetricsContext);
const blockExplorerLinkText = useSelector(getBlockExplorerLinkText);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const accounts = useSelector(getMetaMaskAccountsOrdered);
const isCustodial = /Custody/u.test(keyring.type);
const mmiActions = mmiActionsFactory();
///: END:ONLY_INCLUDE_IN
const isRemovable = keyring.type !== KeyringType.hdKeyTree;
const routeToAddBlockExplorerUrl = () => {
@ -165,6 +180,37 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
{t('removeAccount')}
</MenuItem>
) : null}
{
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
isCustodial ? (
<MenuItem
data-testid="account-options-menu__remove-jwt"
onClick={async () => {
const token = await dispatch(mmiActions.getCustodianToken());
const custodyAccountDetails = await dispatch(
mmiActions.getAllCustodianAccountsWithToken(
keyring.type.split(' - ')[1],
token,
),
);
dispatch(
showModal({
name: 'CONFIRM_REMOVE_JWT',
token,
custodyAccountDetails,
accounts,
selectedAddress: toChecksumHexAddress(address),
}),
);
onClose();
}}
iconClassName="fas fa-trash-alt"
>
{t('removeJWT')}
</MenuItem>
) : null
///: END:ONLY_INCLUDE_IN
}
</Menu>
);
}

View File

@ -0,0 +1,104 @@
import React from 'react';
import configureStore from 'redux-mock-store';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import mockState from '../../../../test/data/mock-state.json';
import AccountOptionsMenu from './account-options-menu';
const initState = {
...mockState,
metamask: {
...mockState.metamask,
providerConfig: {
type: 'test',
chainId: '1',
},
identities: {
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
name: 'Custody Account A',
address: '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275',
},
},
selectedAddress: '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275',
keyrings: [
{
type: 'Custody',
accounts: ['0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275'],
},
],
custodyStatusMaps: '123',
custodyAccountDetails: {
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
custodianName: 'saturn',
},
},
custodianSupportedChains: {
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
supportedChains: ['1', '2'],
custodianName: 'saturn',
},
},
mmiConfiguration: {
portfolio: {
enabled: true,
url: 'https://dashboard.metamask-institutional.io',
},
custodians: [
{
type: 'saturn',
name: 'saturn',
apiUrl: 'https://saturn-custody.dev.metamask-institutional.io',
iconUrl: 'images/saturn.svg',
displayName: 'Saturn Custody',
production: true,
refreshTokenUrl: null,
isNoteToTraderSupported: false,
version: 1,
},
],
},
},
};
const mockStore = configureStore();
const props = {
onClose: jest.fn(),
anchorElement: document.body,
};
const mockedGetCustodianToken = jest.fn().mockReturnValue({ type: 'TYPE' });
const mockedGetAllCustodianAccountsWithToken = jest
.fn()
.mockReturnValue({ type: 'TYPE' });
jest.mock('../../../store/institutional/institution-background', () => ({
mmiActionsFactory: () => ({
getCustodianToken: mockedGetCustodianToken,
getAllCustodianAccountsWithToken: mockedGetAllCustodianAccountsWithToken,
}),
}));
describe('AccountOptionsMenu', () => {
it('shows the remove account and remove jwt menu options', async () => {
const store = mockStore(initState);
renderWithProvider(<AccountOptionsMenu {...props} />, store);
await waitFor(() => {
expect(
screen.queryByTestId('account-options-menu__connected-sites'),
).toBeInTheDocument();
const removeAccount = screen.queryByTestId(
'account-options-menu__remove-account',
);
fireEvent.click(removeAccount);
expect(props.onClose).toHaveBeenCalled();
const removeJwt = screen.queryByTestId(
'account-options-menu__remove-jwt',
);
fireEvent.click(removeJwt);
expect(mockedGetCustodianToken).toHaveBeenCalled();
});
});
});

View File

@ -14,6 +14,19 @@
color: var(--color-text-default);
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
&__custody-logo {
@media screen and (max-width: $break-small) {
display: none;
}
&--icon {
height: 35px;
margin-left: 10px;
}
}
///: END:ONLY_INCLUDE_IN
.selected-account {
grid-column: 2 / span 1;
place-self: center stretch;

View File

@ -2,6 +2,11 @@ import React, { useState, useContext, useRef } from 'react';
import browser from 'webextension-polyfill';
import { useHistory } from 'react-router-dom';
import { useSelector } from 'react-redux';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { getCustodianIconForAddress } from '../../../selectors/institutional/selectors';
import Box from '../../ui/box';
import { DISPLAY, AlignItems } from '../../../helpers/constants/design-system';
///: END:ONLY_INCLUDE_IN
import SelectedAccount from '../selected-account';
import ConnectedStatusIndicator from '../connected-status-indicator';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
@ -12,7 +17,12 @@ import {
} from '../../../../shared/constants/metametrics';
import { CONNECTED_ACCOUNTS_ROUTE } from '../../../helpers/constants/routes';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getOriginOfCurrentTab } from '../../../selectors';
import {
getOriginOfCurrentTab,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getSelectedAddress,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { ButtonIcon, IconName } from '../../component-library';
import AccountOptionsMenu from './account-options-menu';
@ -23,6 +33,12 @@ export default function MenuBar() {
const history = useHistory();
const [accountOptionsMenuOpen, setAccountOptionsMenuOpen] = useState(false);
const origin = useSelector(getOriginOfCurrentTab);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const selectedAddress = useSelector(getSelectedAddress);
const custodianIcon = useSelector((state) =>
getCustodianIconForAddress(state, selectedAddress),
);
///: END:ONLY_INCLUDE_IN
const ref = useRef(false);
const showStatus =
@ -37,6 +53,24 @@ export default function MenuBar() {
onClick={() => history.push(CONNECTED_ACCOUNTS_ROUTE)}
/>
) : null}
{
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodianIcon && (
<Box
display={DISPLAY.FLEX}
alignItems={AlignItems.center}
className="menu-bar__custody-logo"
data-testid="custody-logo"
>
<img
src={custodianIcon}
className="menu-bar__custody-logo--icon"
alt=""
/>
</Box>
)
///: END:ONLY_INCLUDE_IN
}
<SelectedAccount />
<span style={{ display: 'inherit' }} ref={ref}>
<ButtonIcon

View File

@ -4,11 +4,14 @@ import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
import { CHAIN_IDS } from '../../../../shared/constants/network';
import { KeyringType } from '../../../../shared/constants/keyring';
import mockState from '../../../../test/data/mock-state.json';
import MenuBar from './menu-bar';
const initState = {
...mockState,
activeTab: {},
metamask: {
...mockState.metamask,
providerConfig: {
chainId: CHAIN_IDS.GOERLI,
},
@ -64,4 +67,76 @@ describe('MenuBar', () => {
expect(accountOptionsMenu).not.toBeInTheDocument();
});
it('shows a custodial account detail', async () => {
const customState = {
...mockState,
activeTab: {},
metamask: {
...mockState.metamask,
networkConfigurations: {},
providerConfig: {
type: 'test',
chainId: '1',
},
identities: {
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
name: 'Custody Account A',
address: '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275',
},
},
selectedAddress: '0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275',
waitForConfirmDeepLinkDialog: '123',
keyrings: [
{
type: 'Custody',
accounts: ['0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275'],
},
],
custodyStatusMaps: '123',
custodyAccountDetails: {
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
custodianName: 'saturn',
},
},
custodianSupportedChains: {
'0x5Ab19e7091dD208F352F8E727B6DCC6F8aBB6275': {
supportedChains: ['1', '2'],
custodianName: 'saturn',
},
},
mmiConfiguration: {
portfolio: {
enabled: true,
url: 'https://dashboard.metamask-institutional.io',
},
custodians: [
{
type: 'saturn',
name: 'saturn',
apiUrl: 'https://saturn-custody.dev.metamask-institutional.io',
iconUrl: 'images/saturn.svg',
displayName: 'Saturn Custody',
production: true,
refreshTokenUrl: null,
isNoteToTraderSupported: false,
version: 1,
},
],
},
},
};
const store = mockStore(customState);
renderWithProvider(<MenuBar />, store);
const accountOptions = screen.queryByTestId('account-options-menu-button');
fireEvent.click(accountOptions);
await waitFor(() => {
const custodyLogo = screen.queryByTestId('custody-logo');
expect(custodyLogo).toBeInTheDocument();
});
});
});

View File

@ -9,10 +9,10 @@ import NftsDetectionNotice from '../nfts-detection-notice';
import NftsItems from '../nfts-items';
import {
TypographyVariant,
TEXT_ALIGN,
TextAlign,
JustifyContent,
FLEX_DIRECTION,
FONT_WEIGHT,
FontWeight,
AlignItems,
TextColor,
} from '../../../helpers/constants/design-system';
@ -76,8 +76,8 @@ export default function NftsTab({ onAddNFT }) {
<Typography
color={TextColor.textMuted}
variant={TypographyVariant.H4}
align={TEXT_ALIGN.CENTER}
fontWeight={FONT_WEIGHT.BOLD}
align={TextAlign.Center}
fontWeight={FontWeight.Bold}
>
{t('noNFTs')}
</Typography>
@ -101,7 +101,7 @@ export default function NftsTab({ onAddNFT }) {
<Typography
color={TextColor.textMuted}
variant={TypographyVariant.H5}
align={TEXT_ALIGN.CENTER}
align={TextAlign.Center}
>
{t('missingNFT')}
</Typography>
@ -128,7 +128,7 @@ export default function NftsTab({ onAddNFT }) {
<Typography
color={TextColor.textMuted}
variant={TypographyVariant.H6}
align={TEXT_ALIGN.CENTER}
align={TextAlign.Center}
>
{t('or')}
</Typography>

View File

@ -13,6 +13,7 @@ import { PageContainerFooter } from '../../ui/page-container';
import PermissionsConnectFooter from '../permissions-connect-footer';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { RestrictedMethods } from '../../../../shared/constants/permissions';
import SnapPrivacyWarning from '../snaps/snap-privacy-warning';
///: END:ONLY_INCLUDE_IN
import { PermissionPageContainerContent } from '.';
@ -24,6 +25,8 @@ export default class PermissionPageContainer extends Component {
allIdentitiesSelected: PropTypes.bool,
///: BEGIN:ONLY_INCLUDE_IN(snaps)
currentPermissions: PropTypes.object,
snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired,
setSnapsInstallPrivacyWarningShownStatus: PropTypes.func,
///: END:ONLY_INCLUDE_IN
request: PropTypes.object,
requestMetadata: PropTypes.object,
@ -108,6 +111,12 @@ export default class PermissionPageContainer extends Component {
caveats: [{ type: SnapCaveatType.SnapIds, value: dedupedCaveats }],
};
}
showSnapsPrivacyWarning() {
this.setState({
isShowingSnapsPrivacyWarning: true,
});
}
///: END:ONLY_INCLUDE_IN
getRequestedMethodNames(props) {
@ -123,6 +132,14 @@ export default class PermissionPageContainer extends Component {
legacy_event: true,
},
});
///: BEGIN:ONLY_INCLUDE_IN(snaps)
if (this.props.request.permissions[WALLET_SNAP_PERMISSION_KEY]) {
if (this.props.snapsInstallPrivacyWarningShown === false) {
this.showSnapsPrivacyWarning();
}
}
///: END:ONLY_INCLUDE_IN
}
onCancel = () => {
@ -167,8 +184,33 @@ export default class PermissionPageContainer extends Component {
allIdentitiesSelected,
} = this.props;
///: BEGIN:ONLY_INCLUDE_IN(snaps)
const setIsShowingSnapsPrivacyWarning = (value) => {
this.setState({
isShowingSnapsPrivacyWarning: value,
});
};
const confirmSnapsPrivacyWarning = () => {
setIsShowingSnapsPrivacyWarning(false);
this.props.setSnapsInstallPrivacyWarningShownStatus(true);
};
///: END:ONLY_INCLUDE_IN
return (
<div className="page-container permission-approval-container">
{
///: BEGIN:ONLY_INCLUDE_IN(snaps)
<>
{this.state.isShowingSnapsPrivacyWarning && (
<SnapPrivacyWarning
onAccepted={() => confirmSnapsPrivacyWarning()}
onCanceled={() => this.onCancel()}
/>
)}
</>
///: END:ONLY_INCLUDE_IN
}
<PermissionPageContainerContent
requestMetadata={requestMetadata}
subjectMetadata={targetSubjectMetadata}

View File

@ -20,8 +20,8 @@ import Typography from '../../ui/typography/typography';
import { PageContainerFooter } from '../../ui/page-container';
import {
TypographyVariant,
FONT_WEIGHT,
TEXT_ALIGN,
FontWeight,
TextAlign,
TextColor,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
IconColor,
@ -38,7 +38,6 @@ import SecurityProviderBannerMessage from '../security-provider-banner-message/s
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
import { getValueFromWeiHex } from '../../../../shared/modules/conversion.utils';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { Icon, IconName, Text } from '../../component-library';
import Box from '../../ui/box/box';
@ -91,16 +90,6 @@ export default class SignatureRequestOriginal extends Component {
}
};
renderAccountInfo = () => {
return (
<div className="request-signature__account-info">
{this.renderAccount()}
{this.renderRequestIcon()}
{this.renderBalance()}
</div>
);
};
renderTypedData = (data) => {
const { t } = this.context;
const { domain, message } = JSON.parse(data);
@ -210,7 +199,7 @@ export default class SignatureRequestOriginal extends Component {
<Typography
className="request-signature__content__title"
variant={TypographyVariant.H3}
fontWeight={FONT_WEIGHT.BOLD}
fontWeight={FontWeight.Bold}
>
{this.context.t('sigRequest')}
</Typography>
@ -218,7 +207,7 @@ export default class SignatureRequestOriginal extends Component {
className="request-signature__content__subtitle"
variant={TypographyVariant.H7}
color={TextColor.textAlternative}
align={TEXT_ALIGN.CENTER}
align={TextAlign.Center}
margin={12}
marginTop={3}
>

View File

@ -48,6 +48,9 @@ export default function SignatureRequestSIWEHeader({
{fromAccount && (
<AccountListItem
account={fromAccount}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
hideDefaultMismatchWarning
///: END:ONLY_INCLUDE_IN
className="signature-request-siwe-header__account-list-item"
/>
)}

View File

@ -0,0 +1,136 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SignatureRequestHeader renders correctly with fromAccount 1`] = `
<div>
<div
class="signature-request-header"
>
<div
class="signature-request-header--account"
>
<div
class="account-list-item undefined"
data-testid="account-list-item"
>
<div
class="account-list-item__top-row"
>
<div
class=""
>
<div
class="identicon account-list-item__identicon"
style="height: 18px; width: 18px; border-radius: 9px;"
>
<div
style="border-radius: 50px; overflow: hidden; padding: 0px; margin: 0px; width: 18px; height: 18px; display: inline-block; background: rgb(24, 151, 242);"
>
<svg
height="18"
width="18"
x="0"
y="0"
>
<rect
fill="#2362E1"
height="18"
transform="translate(2.018998228945791 -3.0005497255688565) rotate(458.4 9 9)"
width="18"
x="0"
y="0"
/>
<rect
fill="#F94301"
height="18"
transform="translate(-8.641945850428243 4.495697794961231) rotate(268.8 9 9)"
width="18"
x="0"
y="0"
/>
<rect
fill="#FA7900"
height="18"
transform="translate(-5.10539291960705 16.582508932884398) rotate(117.3 9 9)"
width="18"
x="0"
y="0"
/>
</svg>
</div>
</div>
</div>
<div
class="account-list-item__account-name"
>
0x
</div>
</div>
</div>
</div>
<div
class="signature-request-header--network"
>
<div
class="network-display chip chip--with-left-icon chip--border-color-border-muted chip--background-color-undefined chip--max-content"
data-testid="network-display"
>
<div
class="chip__left-icon"
>
<div
class="color-indicator color-indicator--filled color-indicator--color-icon-muted color-indicator--size-lg"
data-testid="color-icon-icon-muted"
>
<i
class="color-indicator__icon fa fa-question"
/>
</div>
</div>
<span
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography chip__label typography--h7 typography--weight-normal typography--style-normal typography--color-text-alternative"
>
Private network
</span>
</div>
</div>
</div>
</div>
`;
exports[`SignatureRequestHeader renders correctly without fromAccount 1`] = `
<div>
<div
class="signature-request-header"
>
<div
class="signature-request-header--account"
/>
<div
class="signature-request-header--network"
>
<div
class="network-display chip chip--with-left-icon chip--border-color-border-muted chip--background-color-undefined chip--max-content"
data-testid="network-display"
>
<div
class="chip__left-icon"
>
<div
class="color-indicator color-indicator--filled color-indicator--color-icon-muted color-indicator--size-lg"
data-testid="color-icon-icon-muted"
>
<i
class="color-indicator__icon fa fa-question"
/>
</div>
</div>
<span
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography chip__label typography--h7 typography--weight-normal typography--style-normal typography--color-text-alternative"
>
Private network
</span>
</div>
</div>
</div>
</div>
`;

View File

@ -14,7 +14,14 @@ export default class SignatureRequestHeader extends PureComponent {
return (
<div className="signature-request-header">
<div className="signature-request-header--account">
{fromAccount ? <AccountListItem account={fromAccount} /> : null}
{fromAccount ? (
<AccountListItem
account={fromAccount}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
hideDefaultMismatchWarning
///: END:ONLY_INCLUDE_IN
/>
) : null}
</div>
<div className="signature-request-header--network">
<NetworkDisplay />

View File

@ -0,0 +1,23 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import mockState from '../../../../../test/data/mock-state.json';
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
import SignatureRequestHeader from './signature-request-header.component';
describe('SignatureRequestHeader', () => {
const store = configureMockStore()(mockState);
it('renders correctly with fromAccount', () => {
const fromAccount = { address: '0x' };
const { container } = renderWithProvider(
<SignatureRequestHeader fromAccount={fromAccount} />,
store,
);
expect(container).toMatchSnapshot();
});
it('renders correctly without fromAccount', () => {
const { container } = renderWithProvider(<SignatureRequestHeader />, store);
expect(container).toMatchSnapshot();
});
});

View File

@ -17,8 +17,8 @@ import Typography from '../../ui/typography/typography';
import ContractDetailsModal from '../modals/contract-details-modal/contract-details-modal';
import {
TypographyVariant,
FONT_WEIGHT,
TEXT_ALIGN,
FontWeight,
TextAlign,
TextColor,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
IconColor,
@ -94,6 +94,8 @@ export default class SignatureRequest extends PureComponent {
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
cancelAll: PropTypes.func.isRequired,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
showCustodianDeepLink: PropTypes.func,
isNotification: PropTypes.bool,
// Used to show a warning if the signing account is not the selected account
// Largely relevant for contract wallet custodians
selectedAccount: PropTypes.object,
@ -110,6 +112,25 @@ export default class SignatureRequest extends PureComponent {
showContractDetails: false,
};
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
componentDidMount() {
if (this.props.txData.custodyId) {
this.props.showCustodianDeepLink({
custodyId: this.props.txData.custodyId,
fromAddress: this.props.fromAccount.address,
closeNotification: this.props.isNotification,
onDeepLinkFetched: () => undefined,
onDeepLinkShown: () => {
this.context.trackEvent({
category: 'MMI',
event: 'Show deeplink for signature',
});
},
});
}
}
///: END:ONLY_INCLUDE_IN
setMessageRootRef(ref) {
this.messageRootRef = ref;
}
@ -306,7 +327,7 @@ export default class SignatureRequest extends PureComponent {
<Typography
className="signature-request__content__title"
variant={TypographyVariant.H3}
fontWeight={FONT_WEIGHT.BOLD}
fontWeight={FontWeight.Bold}
boxProps={{
marginTop: 4,
}}
@ -317,7 +338,7 @@ export default class SignatureRequest extends PureComponent {
className="request-signature__content__subtitle"
variant={TypographyVariant.H7}
color={TextColor.textAlternative}
align={TEXT_ALIGN.CENTER}
align={TextAlign.Center}
margin={12}
marginTop={3}
>
@ -358,6 +379,9 @@ export default class SignatureRequest extends PureComponent {
cancelAction={onCancel}
signAction={onSign}
disabled={
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
Boolean(this.props.txData?.custodyId) ||
///: END:ONLY_INCLUDE_IN
hardwareWalletRequiresConnection ||
(messageIsScrollable && !this.state.hasScrolledMessage)
}

View File

@ -11,6 +11,7 @@ import {
getPreferences,
conversionRateSelector,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getAccountType,
getSelectedAccount,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
@ -20,14 +21,39 @@ import {
getProviderConfig,
} from '../../../ducks/metamask/metamask';
import { getAccountByAddress, valuesFor } from '../../../helpers/utils/util';
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import { cancelMsgs, showModal } from '../../../store/actions';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
// eslint-disable-next-line import/order
import { showCustodianDeepLink } from '@metamask-institutional/extension';
import {
mmiActionsFactory,
setTypedMessageInProgress,
} from '../../../store/institutional/institution-background';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
///: END:ONLY_INCLUDE_IN
import {
MESSAGE_TYPE,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
ENVIRONMENT_TYPE_NOTIFICATION,
///: END:ONLY_INCLUDE_IN
} from '../../../../shared/constants/app';
import {
cancelMsgs,
showModal,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
goHome,
///: END:ONLY_INCLUDE_IN
} from '../../../store/actions';
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck';
import SignatureRequest from './signature-request.component';
function mapStateToProps(state, ownProps) {
const { txData } = ownProps;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const envType = getEnvironmentType();
///: END:ONLY_INCLUDE_IN
const {
msgParams: { from },
} = txData;
@ -60,12 +86,62 @@ function mapStateToProps(state, ownProps) {
// not forwarded to component
allAccounts: accountsWithSendEtherInfoSelector(state),
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
accountType: getAccountType(state),
isNotification: envType === ENVIRONMENT_TYPE_NOTIFICATION,
selectedAccount: getSelectedAccount(state),
///: END:ONLY_INCLUDE_IN
};
}
function mapDispatchToProps(dispatch) {
let mapDispatchToProps = null;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
function mmiMapDispatchToProps(dispatch) {
const mmiActions = mmiActionsFactory();
return {
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
setMsgInProgress: (msgId) => dispatch(setTypedMessageInProgress(msgId)),
showCustodianDeepLink: ({
custodyId,
fromAddress,
closeNotification,
onDeepLinkFetched,
onDeepLinkShown,
}) =>
showCustodianDeepLink({
dispatch,
mmiActions,
txId: undefined,
fromAddress,
custodyId,
isSignature: true,
closeNotification,
onDeepLinkFetched,
onDeepLinkShown,
}),
showTransactionsFailedModal: ({
errorMessage,
closeNotification,
operationFailed,
}) =>
dispatch(
showModal({
name: 'TRANSACTION_FAILED',
errorMessage,
closeNotification,
operationFailed,
}),
),
setWaitForConfirmDeepLinkDialog: (wait) =>
dispatch(mmiActions.setWaitForConfirmDeepLinkDialog(wait)),
goHome: () => dispatch(goHome()),
};
}
mapDispatchToProps = mmiMapDispatchToProps;
///: END:ONLY_INCLUDE_IN
mapDispatchToProps = function (dispatch) {
return {
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
showRejectTransactionsConfirmationModal: ({
@ -84,7 +160,7 @@ function mapDispatchToProps(dispatch) {
cancelAll: (unconfirmedMessagesList) =>
dispatch(cancelMsgs(unconfirmedMessagesList)),
};
}
};
function mergeProps(stateProps, dispatchProps, ownProps) {
const {
@ -101,6 +177,10 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
unconfirmedMessagesList,
unapprovedMessagesCount,
mostRecentOverviewPage,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
accountType,
isNotification,
///: END:ONLY_INCLUDE_IN
} = stateProps;
const {
signPersonalMessage,
@ -135,13 +215,55 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
sign = signMessage;
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const signFn = async (...opts) => {
if (accountType === 'custody') {
try {
let msgData = opts;
let id = opts.custodyId;
if (!opts.custodyId) {
msgData = await sign(opts);
id = msgData.custodyId;
}
dispatchProps.showCustodianDeepLink({
custodyId: id,
fromAddress: fromAccount.address,
closeNotification: isNotification,
onDeepLinkFetched: () => undefined,
onDeepLinkShown: () => undefined,
});
await dispatchProps.setMsgInProgress(msgData.metamaskId);
await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
await goHome();
return msgData;
} catch (err) {
await dispatchProps.setWaitForConfirmDeepLinkDialog(true);
await dispatchProps.showTransactionsFailedModal({
errorMessage: err.message,
closeNotification: true,
operationFailed: true,
});
return null;
}
}
return sign(opts);
};
///: END:ONLY_INCLUDE_IN
return {
...ownProps,
...dispatchProps,
fromAccount,
txData,
cancel,
///: BEGIN:ONLY_INCLUDE_IN(build-main,build-beta,build-flask)
sign,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
// eslint-disable-next-line no-dupe-keys
sign: signFn,
///: END:ONLY_INCLUDE_IN
isLedgerWallet,
hardwareWalletRequiresConnection,
chainId,

View File

@ -9,7 +9,7 @@ import {
BackgroundColor,
IconColor,
TextVariant,
TEXT_ALIGN,
TextAlign,
Size,
JustifyContent,
} from '../../../../helpers/constants/design-system';
@ -86,13 +86,13 @@ export default function SnapInstallWarning({ onCancel, onSubmit, warnings }) {
</Box>
<Text
paddingBottom={6}
textAlign={TEXT_ALIGN.CENTER}
textAlign={TextAlign.Center}
variant={TextVariant.headingSm}
as="h2"
>
{t('snapInstallWarningHeading')}
</Text>
<Text paddingBottom={6} textAlign={TEXT_ALIGN.CENTER}>
<Text paddingBottom={6} textAlign={TextAlign.Center}>
{warnings.length > 1
? t('snapInstallWarningCheckPlural')
: t('snapInstallWarningCheck')}

View File

@ -0,0 +1 @@
export { default } from './snap-privacy-warning';

View File

@ -0,0 +1,140 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import Box from '../../../ui/box/box';
import Popover from '../../../ui/popover';
import {
AvatarIcon,
Button,
BUTTON_LINK_SIZES,
BUTTON_PRIMARY_SIZES,
BUTTON_VARIANT,
ButtonLink,
IconName,
IconSize,
Text,
} from '../../../component-library';
import {
AlignItems,
BackgroundColor,
BLOCK_SIZES,
DISPLAY,
IconColor,
JustifyContent,
TextVariant,
} from '../../../../helpers/constants/design-system';
export default function SnapPrivacyWarning({ onAccepted, onCanceled }) {
const t = useI18nContext();
const [isDescriptionOpen, setIsDescriptionOpen] = useState(false);
const handleReadMoreClick = () => {
setIsDescriptionOpen(true);
};
return (
<Popover className="snap-privacy-warning">
<Box padding={4}>
<Box
className="snap-privacy-warning__info-icon"
display={DISPLAY.FLEX}
justifyContent={JustifyContent.center}
alignItems={AlignItems.center}
>
<AvatarIcon
iconName={IconName.Info}
color={IconColor.infoDefault}
backgroundColor={BackgroundColor.primaryMuted}
size={IconSize.Md}
/>
</Box>
<Box
className="snap-privacy-warning__title"
marginTop={4}
marginBottom={6}
display={DISPLAY.FLEX}
justifyContent={JustifyContent.center}
alignItems={AlignItems.center}
>
<Text variant={TextVariant.headingMd}>{t('thirdPartySoftware')}</Text>
</Box>
<Box className="snap-privacy-warning__message">
<Text variant={TextVariant.bodyMd}>
{t('snapsPrivacyWarningFirstMessage')}
</Text>
{!isDescriptionOpen && (
<>
<Text variant={TextVariant.bodyMd} paddingTop={6}>
{t('snapsPrivacyWarningSecondMessage')}
</Text>
<Text
variant={TextVariant.bodyMd}
className="snap-privacy-warning__more-details"
>
{t('click')}
<ButtonLink
size={BUTTON_LINK_SIZES.INHERIT}
onClick={handleReadMoreClick}
data-testid="snapsPrivacyPopup_readMoreButton"
>
&nbsp;{t('here')}&nbsp;
</ButtonLink>
{t('forMoreDetails')}
</Text>
</>
)}
{isDescriptionOpen && (
<>
<Text variant={TextVariant.bodyMd} paddingTop={6}>
{t('snapsThirdPartyNoticeReadMorePartOne')}
</Text>
<Text variant={TextVariant.bodyMd} paddingTop={6}>
{t('snapsThirdPartyNoticeReadMorePartTwo')}
</Text>
<Text variant={TextVariant.bodyMd} paddingTop={6}>
{t('snapsThirdPartyNoticeReadMorePartThree')}
</Text>
</>
)}
</Box>
<Box
className="snap-privacy-warning__ok-button"
marginTop={6}
display={DISPLAY.FLEX}
>
<Button
variant={BUTTON_VARIANT.SECONDARY}
size={BUTTON_PRIMARY_SIZES.LG}
width={BLOCK_SIZES.FULL}
className="snap-privacy-warning__cancel-button"
onClick={onCanceled}
marginRight={2}
>
{t('cancel')}
</Button>
<Button
variant={BUTTON_VARIANT.PRIMARY}
size={BUTTON_PRIMARY_SIZES.LG}
width={BLOCK_SIZES.FULL}
className="snap-privacy-warning__ok-button"
onClick={onAccepted}
marginLeft={2}
>
{t('accept')}
</Button>
</Box>
</Box>
</Popover>
);
}
SnapPrivacyWarning.propTypes = {
/**
* onAccepted handler
*/
onAccepted: PropTypes.func.isRequired,
/**
* onCanceled handler
*/
onCanceled: PropTypes.func.isRequired,
};

View File

@ -0,0 +1,22 @@
import React from 'react';
import SnapPrivacyWarning from '.';
export default {
title: 'Components/App/snaps/SnapPrivacyWarning',
component: SnapPrivacyWarning,
argTypes: {
onAccepted: {
action: 'onAccepted',
},
onCanceled: {
action: 'onCanceled',
},
},
};
export const DefaultStory = (args) => <SnapPrivacyWarning {...args} />;
DefaultStory.storyName = 'Default';
DefaultStory.args = {};

View File

@ -0,0 +1,77 @@
import React from 'react';
import { screen } from '@testing-library/react';
import { renderWithProvider } from '../../../../../test/jest';
import SnapPrivacyWarning from './snap-privacy-warning';
describe('Snap Privacy Warning Popover', () => {
it('renders snaps privacy warning popover and works with accept flow', () => {
const mockOnAcceptCallback = jest.fn();
const { getByTestId } = renderWithProvider(
<SnapPrivacyWarning
onAccepted={mockOnAcceptCallback}
onCanceled={jest.fn()}
/>,
);
expect(screen.getByText('Third party software')).toBeInTheDocument();
expect(
screen.getByText(
'Installing a snap retrieves data from third parties. They may collect your personal information.',
),
).toBeInTheDocument();
expect(
screen.getByText('MetaMask has no access to this information.'),
).toBeInTheDocument();
const clickHereToReadMoreButton = getByTestId(
'snapsPrivacyPopup_readMoreButton',
);
expect(clickHereToReadMoreButton).toBeDefined();
clickHereToReadMoreButton.click();
expect(
screen.getByText(
'Any information you share with third-party-developed snaps will be collected directly by those snaps in accordance with their privacy policies.',
),
).toBeInTheDocument();
expect(
screen.getByText(
'During the installation of a snap, npmjs (npmjs.com) and AWS (aws.amazon.com) may collect your IP address. Please refer to their privacy policies for more information.',
),
).toBeInTheDocument();
expect(
screen.getByRole('button', {
name: /Accept/iu,
}),
).toBeInTheDocument();
screen
.getByRole('button', {
name: /Accept/iu,
})
.click();
expect(mockOnAcceptCallback).toHaveBeenCalled();
});
it('renders snaps privacy warning popover and works with cancel flow', () => {
const mockOnAcceptCallback = jest.fn();
const mockOnCanceledCallback = jest.fn();
renderWithProvider(
<SnapPrivacyWarning
onAccepted={mockOnAcceptCallback}
onCanceled={mockOnCanceledCallback}
/>,
);
expect(screen.getByText('Third party software')).toBeInTheDocument();
expect(
screen.getByRole('button', {
name: /Cancel/iu,
}),
).toBeInTheDocument();
screen
.getByRole('button', {
name: /Cancel/iu,
})
.click();
expect(mockOnCanceledCallback).toHaveBeenCalled();
expect(mockOnAcceptCallback).not.toHaveBeenCalled();
});
});

View File

@ -1,62 +0,0 @@
.update-snap-permission-list {
width: 100%;
.approved-permission,
.new-permission,
.revoked-permission {
@include H6;
width: 100%;
padding-bottom: 16px;
border-bottom: 1px solid var(--color-border-default);
display: flex;
flex-direction: row;
align-items: center;
color: var(--color-text-default);
i {
display: block;
padding: 16px;
min-width: 16px;
min-height: 16px;
font-size: 1rem;
}
}
.approved-permission {
color: var(--color-success-default);
}
.revoked-permission {
color: var(--color-error-alternative);
}
.new-permission {
color: var(--color-info-default);
}
.permission-description {
width: 100%;
display: flex;
flex-direction: column;
}
.permission-description-subtext {
@include H7;
}
.permission {
&__tooltip-icon {
margin-left: auto !important;
padding-left: 16px;
i {
color: var(--color-icon-muted);
}
&__warning i {
color: var(--color-warning-default);
}
}
}
}

View File

@ -13,30 +13,26 @@
min-width: 0;
flex: 0 0 auto;
box-sizing: border-box;
opacity: 0.5;
transition: opacity 200ms ease-in-out;
background-color: unset;
text-align: start;
@include screen-sm-min {
&:hover {
opacity: 0.4;
}
&:active {
opacity: 0.6;
}
}
position: relative;
@include screen-sm-max {
@include H4;
border-bottom: 1px solid var(--color-border-muted);
opacity: 1;
}
&__selected-indicator {
width: 4px;
height: calc(100% - 8px);
position: absolute;
top: 4px;
left: 4px;
}
&__content {
padding: 12px 18px;
display: flex;
align-items: center;
position: relative;
@ -67,7 +63,6 @@
justify-content: center;
margin-inline-end: 16px;
flex: 0 0 18px;
color: var(--color-icon-alternative);
}
}
@ -87,6 +82,10 @@
&--active {
opacity: 1 !important;
@include screen-sm-min {
background-color: var(--color-primary-muted);
}
}
}

View File

@ -3,6 +3,12 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import { Icon, IconName, IconSize } from '../../component-library';
import Box from '../../ui/box';
import {
BorderRadius,
Color,
DISPLAY,
} from '../../../helpers/constants/design-system';
const TabBar = (props) => {
const { tabs = [], onSelect, isActive } = props;
@ -10,13 +16,26 @@ const TabBar = (props) => {
return (
<div className="tab-bar">
{tabs.map(({ key, content, icon }) => (
<button
<Box
as="button"
key={key}
paddingTop={5}
paddingBottom={5}
paddingLeft={4}
paddingRight={4}
className={classnames('tab-bar__tab pointer', {
'tab-bar__tab--active': isActive(key, content),
})}
onClick={() => onSelect(key)}
>
{isActive(key, content) && (
<Box
className="tab-bar__tab__selected-indicator"
borderRadius={BorderRadius.pill}
backgroundColor={Color.primaryDefault}
display={[DISPLAY.NONE, DISPLAY.BLOCK]}
/>
)}
<div className="tab-bar__tab__content">
<div className="tab-bar__tab__content__icon">{icon}</div>
<div className="tab-bar__tab__content__title">{content}</div>
@ -26,7 +45,7 @@ const TabBar = (props) => {
size={IconSize.Sm}
className="tab-bar__tab__caret"
/>
</button>
</Box>
))}
</div>
);

View File

@ -3,7 +3,7 @@
exports[`Token Cell should match snapshot 1`] = `
<div>
<div
class="list-item asset-list-item token-cell list-item--single-content-row"
class="list-item asset-list-item token-cell list-item--single-content-row list-item-border"
role="button"
tabindex="0"
>

View File

@ -5,12 +5,12 @@ import classnames from 'classnames';
import Typography from '../../ui/typography/typography';
import {
Color,
FONT_WEIGHT,
FontWeight,
TypographyVariant,
DISPLAY,
FLEX_WRAP,
AlignItems,
TEXT_ALIGN,
TextAlign,
} from '../../../helpers/constants/design-system';
export default function TransactionDetailItem({
@ -28,7 +28,7 @@ export default function TransactionDetailItem({
<div className="transaction-detail-item__row">
<Typography
color={detailTitleColor}
fontWeight={boldHeadings ? FONT_WEIGHT.BOLD : FONT_WEIGHT.NORMAL}
fontWeight={boldHeadings ? FontWeight.Bold : FontWeight.Normal}
variant={TypographyVariant.H6}
boxProps={{
display: DISPLAY.FLEX,
@ -54,12 +54,12 @@ export default function TransactionDetailItem({
)}
<Typography
color={Color.textDefault}
fontWeight={boldHeadings ? FONT_WEIGHT.BOLD : FONT_WEIGHT.NORMAL}
fontWeight={boldHeadings ? FontWeight.Bold : FontWeight.Normal}
variant={TypographyVariant.H6}
marginTop={1}
marginBottom={1}
marginLeft={1}
boxProps={{ textAlign: TEXT_ALIGN.RIGHT }}
boxProps={{ textAlign: TextAlign.Right }}
>
{detailTotal}
</Typography>

View File

@ -11,6 +11,11 @@ import Button from '../../ui/button';
import Tooltip from '../../ui/tooltip';
import CancelButton from '../cancel-button';
import Popover from '../../ui/popover';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import Box from '../../ui/box/box';
import { Icon, IconName, Text } from '../../component-library';
import { IconColor } from '../../../helpers/constants/design-system';
///: END:ONLY_INCLUDE_IN
import { SECOND } from '../../../../shared/constants/time';
import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics';
import { TransactionType } from '../../../../shared/constants/transaction';
@ -52,10 +57,18 @@ export default class TransactionListItemDetails extends PureComponent {
isCustomNetwork: PropTypes.bool,
history: PropTypes.object,
blockExplorerLinkText: PropTypes.object,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getCustodianTransactionDeepLink: PropTypes.func,
selectedIdentity: PropTypes.object,
transactionNote: PropTypes.string,
///: END:ONLY_INCLUDE_IN
};
state = {
justCopied: false,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodyTransactionDeepLink: null,
///: END:ONLY_INCLUDE_IN
};
handleBlockExplorerClick = () => {
@ -124,16 +137,57 @@ export default class TransactionListItemDetails extends PureComponent {
};
componentDidMount() {
const { recipientAddress, tryReverseResolveAddress } = this.props;
const {
recipientAddress,
tryReverseResolveAddress,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
selectedIdentity,
transactionGroup,
///: END:ONLY_INCLUDE_IN
} = this.props;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this._mounted = true;
const address = selectedIdentity?.address;
const custodyId = transactionGroup?.primaryTransaction?.custodyId;
if (this._mounted && address && custodyId) {
this.getCustodianTransactionDeepLink(address, custodyId);
}
///: END:ONLY_INCLUDE_IN
if (recipientAddress) {
tryReverseResolveAddress(recipientAddress);
}
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getCustodianTransactionDeepLink = async (address, custodyId) => {
const { getCustodianTransactionDeepLink } = this.props;
const custodyTransactionDeepLink = await getCustodianTransactionDeepLink(
address,
custodyId,
);
if (custodyTransactionDeepLink && this._mounted) {
this.setState({ custodyTransactionDeepLink });
}
};
componentWillUnmount() {
this._mounted = false;
}
///: END:ONLY_INCLUDE_IN
render() {
const { t } = this.context;
const { justCopied } = this.state;
const {
justCopied,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodyTransactionDeepLink,
///: END:ONLY_INCLUDE_IN
} = this.state;
const {
transactionGroup,
primaryCurrency,
@ -152,6 +206,9 @@ export default class TransactionListItemDetails extends PureComponent {
showCancel,
transactionStatus: TransactionStatus,
blockExplorerLinkText,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
transactionNote,
///: END:ONLY_INCLUDE_IN
} = this.props;
const {
primaryTransaction: transaction,
@ -229,6 +286,30 @@ export default class TransactionListItemDetails extends PureComponent {
</Button>
</Tooltip>
</div>
{
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodyTransactionDeepLink &&
custodyTransactionDeepLink.url && (
<Tooltip
wrapperClassName="transaction-list-item-details__header-button"
containerClassName="transaction-list-item-details__header-button-tooltip-container"
title={t('viewinCustodianApp')}
>
<Button
type="raised"
onClick={() => {
window.open(custodyTransactionDeepLink.url);
}}
>
<Icon
name={IconName.Custody}
color={IconColor.primaryDefault}
/>
</Button>
</Tooltip>
)
///: END:ONLY_INCLUDE_IN
}
</div>
</div>
<div className="transaction-list-item-details__body">
@ -281,6 +362,20 @@ export default class TransactionListItemDetails extends PureComponent {
primaryCurrency={primaryCurrency}
className="transaction-list-item-details__transaction-breakdown"
/>
{
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
transactionNote && transactionNote.length !== 0 && (
<Box className="transaction-list-item-details__transaction-breakdown">
<Text as="h4" className="transaction-breakdown__title">
{t('transactionNote')}
</Text>
<Text as="p" className="transaction-breakdown__description">
{transactionNote}
</Text>
</Box>
)
///: END:ONLY_INCLUDE_IN
}
{transactionGroup.initialTransaction.type !==
TransactionType.incoming && (
<Disclosure title={t('activityLog')} size="small">

View File

@ -1,6 +1,7 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { waitFor } from '@testing-library/react';
import { TransactionStatus } from '../../../../shared/constants/transaction';
import { GAS_LIMITS } from '../../../../shared/constants/gas';
import { renderWithProvider } from '../../../../test/lib/render-helpers';
@ -13,6 +14,14 @@ jest.mock('../../../store/actions.ts', () => ({
addPollingTokenToAppState: jest.fn(),
}));
let mockGetCustodianTransactionDeepLink = jest.fn();
jest.mock('../../../store/institutional/institution-background', () => ({
mmiActionsFactory: () => ({
getCustodianTransactionDeepLink: () => mockGetCustodianTransactionDeepLink,
}),
}));
describe('TransactionListItemDetails Component', () => {
const transaction = {
history: [],
@ -26,6 +35,10 @@ describe('TransactionListItemDetails Component', () => {
to: '0x2',
value: '0x2386f26fc10000',
},
metadata: {
note: 'some note',
},
custodyId: '1',
};
const transactionGroup = {
@ -58,7 +71,7 @@ describe('TransactionListItemDetails Component', () => {
rpcPrefs,
};
it('should render title with title prop', () => {
it('should render title with title prop', async () => {
const mockStore = configureMockStore([thunk])(mockState);
const { queryByText } = renderWithProvider(
@ -66,7 +79,9 @@ describe('TransactionListItemDetails Component', () => {
mockStore,
);
expect(queryByText(props.title)).toBeInTheDocument();
await waitFor(() => {
expect(queryByText(props.title)).toBeInTheDocument();
});
});
describe('Retry button', () => {
@ -122,4 +137,55 @@ describe('TransactionListItemDetails Component', () => {
expect(queryByTestId('speedup-button')).toBeInTheDocument();
});
});
describe('Institutional', () => {
it('should render correctly if custodyTransactionDeepLink has a url', async () => {
mockGetCustodianTransactionDeepLink = jest
.fn()
.mockReturnValue({ url: 'https://url.com' });
const mockStore = configureMockStore([thunk])(mockState);
renderWithProvider(<TransactionListItemDetails {...props} />, mockStore);
await waitFor(() => {
const custodianViewButton = document.querySelector(
'[data-original-title="View in custodian app"]',
);
// Assert that the custodian view button is rendered
expect(custodianViewButton).toBeInTheDocument();
});
});
it('should render correctly if transactionNote is provided', async () => {
const newTransaction = {
...transaction,
metadata: {
note: 'some note',
},
custodyId: '1',
};
const newTransactionGroup = {
...transactionGroup,
transactions: [newTransaction],
primaryTransaction: newTransaction,
initialTransaction: newTransaction,
};
const mockStore = configureMockStore([thunk])(mockState);
const { queryByText } = renderWithProvider(
<TransactionListItemDetails
{...props}
transactionGroup={newTransactionGroup}
/>,
mockStore,
);
await waitFor(() => {
expect(queryByText('some note')).toBeInTheDocument();
});
});
});
});

View File

@ -2,6 +2,9 @@ import { connect } from 'react-redux';
import { compose } from 'redux';
import { withRouter } from 'react-router-dom';
import { tryReverseResolveAddress } from '../../../store/actions';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { mmiActionsFactory } from '../../../store/institutional/institution-background';
///: END:ONLY_INCLUDE_IN
import {
getAddressBook,
getBlockExplorerLinkText,
@ -11,6 +14,10 @@ import {
getAccountName,
getMetadataContractName,
getMetaMaskIdentities,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getSelectedIdentity,
getKnownMethodData,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
import { toChecksumHexAddress } from '../../../../shared/modules/hexstring-utils';
import TransactionListItemDetails from './transaction-list-item-details.component';
@ -40,6 +47,13 @@ const mapStateToProps = (state, ownProps) => {
const isCustomNetwork = getIsCustomNetwork(state);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const data = ownProps.transactionGroup?.primaryTransaction?.txParams?.data;
const methodData = getKnownMethodData(state, data) || {};
const transactionNote =
ownProps.transactionGroup?.primaryTransaction?.metadata?.note;
///: END:ONLY_INCLUDE_IN
return {
rpcPrefs,
recipientEns,
@ -49,14 +63,29 @@ const mapStateToProps = (state, ownProps) => {
blockExplorerLinkText: getBlockExplorerLinkText(state),
recipientName,
recipientMetadataName,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
methodData,
transactionNote,
selectedIdentity: getSelectedIdentity(state),
///: END:ONLY_INCLUDE_IN
};
};
const mapDispatchToProps = (dispatch) => {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const mmiActions = mmiActionsFactory();
///: END:ONLY_INCLUDE_IN
return {
tryReverseResolveAddress: (address) => {
return dispatch(tryReverseResolveAddress(address));
},
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getCustodianTransactionDeepLink: (address, txId) => {
return dispatch(
mmiActions.getCustodianTransactionDeepLink(address, txId),
);
},
///: END:ONLY_INCLUDE_IN
};
};

View File

@ -67,4 +67,13 @@
text-overflow: ellipsis;
white-space: nowrap;
}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
&__icon-badge {
position: absolute;
top: 18px;
left: 18px;
transform: scale(0.8);
}
///: END:ONLY_INCLUDE_IN
}

View File

@ -13,6 +13,10 @@ import { CONFIRM_TRANSACTION_ROUTE } from '../../../helpers/constants/routes';
import { useShouldShowSpeedUp } from '../../../hooks/useShouldShowSpeedUp';
import TransactionStatusLabel from '../transaction-status-label/transaction-status-label';
import TransactionIcon from '../transaction-icon';
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { IconColor } from '../../../helpers/constants/design-system';
import { Icon, IconName, IconSize } from '../../component-library';
///: END:ONLY_INCLUDE_IN
import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics';
import {
TransactionGroupCategory,
@ -125,6 +129,9 @@ function TransactionListItemInner({
const isApproval = category === TransactionGroupCategory.approval;
const isUnapproved = status === TransactionStatus.unapproved;
const isSwap = category === TransactionGroupCategory.swap;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const isCustodian = Boolean(transactionGroup.primaryTransaction.custodyId);
///: END:ONLY_INCLUDE_IN
const className = classnames('transaction-list-item', {
'transaction-list-item--unconfirmed':
@ -144,10 +151,29 @@ function TransactionListItemInner({
setShowDetails((prev) => !prev);
}, [isUnapproved, history, id]);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const debugTransactionMeta = {
'data-hash': transactionGroup.primaryTransaction.hash,
...(isCustodian
? {
'data-custodiantransactionid':
transactionGroup.primaryTransaction.custodyId,
}
: {}),
};
///: END:ONLY_INCLUDE_IN
const speedUpButton = useMemo(() => {
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
if (isCustodian) {
return null;
}
///: END:ONLY_INCLUDE_IN
if (!shouldShowSpeedUp || !isPending || isUnapproved) {
return null;
}
return (
<Button
type="primary"
@ -165,9 +191,32 @@ function TransactionListItemInner({
hasCancelled,
retryTransaction,
cancelTransaction,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
isCustodian,
///: END:ONLY_INCLUDE_IN
]);
const showCancelButton = !hasCancelled && isPending && !isUnapproved;
const showBorder = process.env.MULTICHAIN;
let showCancelButton = !hasCancelled && isPending && !isUnapproved;
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
showCancelButton = showCancelButton && !isCustodian;
const PENDING_COLOR = IconColor.iconAlternative;
const OK_COLOR = IconColor.primaryDefault;
const FAIL_COLOR = IconColor.errorDefault;
const getTransactionColor = (tsStatus) => {
switch (tsStatus) {
case TransactionStatus.signed:
return PENDING_COLOR;
case TransactionStatus.rejected:
case TransactionStatus.failed:
case TransactionStatus.dropped:
return FAIL_COLOR;
default:
return OK_COLOR;
}
};
///: END:ONLY_INCLUDE_IN
return (
<>
@ -176,8 +225,28 @@ function TransactionListItemInner({
className={className}
title={title}
icon={
<TransactionIcon category={category} status={displayedStatusKey} />
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
isCustodian ? (
<div style={{ position: 'relative' }} data-testid="custody-icon">
<TransactionIcon
category={category}
status={displayedStatusKey}
/>
<Icon
name={IconName.Custody}
className="transaction-list-item__icon-badge"
color={getTransactionColor(status)}
size={IconSize.Xs}
/>
</div>
) : (
///: END:ONLY_INCLUDE_IN
<TransactionIcon category={category} status={displayedStatusKey} />
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
)
///: END:ONLY_INCLUDE_IN
}
showBorder={showBorder}
subtitle={
<h3>
<TransactionStatusLabel
@ -186,6 +255,12 @@ function TransactionListItemInner({
error={err}
date={date}
status={displayedStatusKey}
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodyStatus={transactionGroup.primaryTransaction.custodyStatus}
custodyStatusDisplayText={
transactionGroup.primaryTransaction.custodyStatusDisplayText
}
///: END:ONLY_INCLUDE_IN
/>
{subtitleContainsOrigin ? (
<SiteOrigin siteOrigin={subtitle} />
@ -222,6 +297,11 @@ function TransactionListItemInner({
/>
)}
</div>
{
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
<a {...debugTransactionMeta} className="test-transaction-meta" />
///: END:ONLY_INCLUDE_IN
}
</ListItem>
{showDetails && (
<TransactionListItemDetails
@ -232,11 +312,28 @@ function TransactionListItemInner({
senderAddress={senderAddress}
recipientAddress={recipientAddress}
onRetry={retryTransaction}
showRetry={status === TransactionStatus.failed && !isSwap}
showSpeedUp={shouldShowSpeedUp}
showRetry={
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
!isCustodian &&
///: END:ONLY_INCLUDE_IN
status === TransactionStatus.failed &&
!isSwap
}
showSpeedUp={
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
!isCustodian &&
///: END:ONLY_INCLUDE_IN
shouldShowSpeedUp
}
isEarliestNonce={isEarliestNonce}
onCancel={cancelTransaction}
showCancel={isPending && !hasCancelled}
showCancel={
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
!isCustodian &&
///: END:ONLY_INCLUDE_IN
isPending &&
!hasCancelled
}
transactionStatus={() => (
<TransactionStatusLabel
isPending={isPending}

View File

@ -1,6 +1,8 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { fireEvent } from '@testing-library/react';
import { fireEvent, screen } from '@testing-library/react';
import configureStore from 'redux-mock-store';
import mockState from '../../../../test/data/mock-state.json';
import transactionGroup from '../../../../test/data/mock-pending-transaction-data.json';
import {
getConversionRate,
@ -72,6 +74,20 @@ jest.mock('react', () => {
};
});
jest.mock('../../../store/actions.ts', () => ({
tryReverseResolveAddress: jest.fn().mockReturnValue({ type: 'TYPE' }),
}));
jest.mock('../../../store/institutional/institution-background', () => ({
mmiActionsFactory: () => ({
getCustodianTransactionDeepLink: jest
.fn()
.mockReturnValue({ type: 'TYPE' }),
}),
}));
const mockStore = configureStore();
const generateUseSelectorRouter = (opts) => (selector) => {
if (selector === getConversionRate) {
return 1;
@ -146,5 +162,49 @@ describe('TransactionListItem', () => {
fireEvent.click(cancelButton);
expect(getByText('Cancel transaction')).toBeInTheDocument();
});
it('should have a custodian Tx and show the custody icon', () => {
useSelector.mockImplementation(
generateUseSelectorRouter({
balance: '2AA1EFB94E0000',
}),
);
const newTransactionGroup = {
...transactionGroup,
...(transactionGroup.primaryTransaction.custodyId = '1'),
};
const { queryByTestId } = renderWithProvider(
<TransactionListItem transactionGroup={newTransactionGroup} />,
);
expect(queryByTestId('custody-icon')).toBeInTheDocument();
});
it('should click the custody list item and view the send screen', () => {
const store = mockStore(mockState);
useSelector.mockImplementation(
generateUseSelectorRouter({
balance: '2AA1EFB94E0000',
}),
);
const newTransactionGroup = {
...transactionGroup,
...(transactionGroup.primaryTransaction.custodyId = '1'),
};
const { queryByTestId } = renderWithProvider(
<TransactionListItem transactionGroup={newTransactionGroup} />,
store,
);
const custodyListItem = queryByTestId('custody-icon');
fireEvent.click(custodyListItem);
const sendTextExists = screen.queryAllByText('Send');
expect(sendTextExists).toBeTruthy();
});
});
});

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