mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Merge remote-tracking branch 'upstream/develop' into minimal
This commit is contained in:
commit
ea91836181
@ -143,11 +143,6 @@ workflows:
|
||||
requires:
|
||||
- prep-deps
|
||||
- prep-build
|
||||
- test-mozilla-lint-beta:
|
||||
<<: *rc_branch_only
|
||||
requires:
|
||||
- prep-deps
|
||||
- trigger-beta-build
|
||||
- test-mozilla-lint-desktop:
|
||||
filters:
|
||||
branches:
|
||||
@ -176,7 +171,6 @@ workflows:
|
||||
- validate-source-maps-desktop
|
||||
- validate-source-maps-flask
|
||||
- test-mozilla-lint
|
||||
- test-mozilla-lint-beta
|
||||
- test-mozilla-lint-desktop
|
||||
- test-mozilla-lint-flask
|
||||
- test-e2e-chrome
|
||||
@ -1144,17 +1138,6 @@ jobs:
|
||||
name: test:mozilla-lint
|
||||
command: NODE_OPTIONS=--max_old_space_size=3072 yarn mozilla-lint
|
||||
|
||||
test-mozilla-lint-beta:
|
||||
executor: node-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Lint beta for firefox
|
||||
command: |
|
||||
.circleci/scripts/mozilla-lint-beta.sh
|
||||
|
||||
test-mozilla-lint-desktop:
|
||||
executor: node-browsers
|
||||
steps:
|
||||
|
@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
current_commit_msg=$(git show -s --format='%s' HEAD)
|
||||
|
||||
if [[ $current_commit_msg =~ Version[[:space:]](v[[:digit:]]+.[[:digit:]]+.[[:digit:]]+[-]beta.[[:digit:]]) ]]
|
||||
then
|
||||
# filter the commit message like Version v10.24.1-beta.1
|
||||
printf '%s\n' "Linting beta builds for firefox"
|
||||
# Move beta build to dist
|
||||
mv ./dist-beta ./dist
|
||||
# Move beta zips to builds
|
||||
mv ./builds-beta ./builds
|
||||
# test:mozilla-lint
|
||||
export NODE_OPTIONS='--max_old_space_size=3072'
|
||||
yarn mozilla-lint
|
||||
else
|
||||
printf '%s\n' 'Commit message does not match commit message for beta pattern; skipping linting for firefox'
|
||||
mkdir dist
|
||||
mkdir builds
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exit 0
|
@ -12,8 +12,9 @@ if [[ $current_commit_msg =~ Version[[:space:]](v[[:digit:]]+.[[:digit:]]+.[[:di
|
||||
then
|
||||
# filter the commit message like Version v10.24.1-beta.1
|
||||
printf '%s\n' "Create a build for $version with beta version $current_commit_msg"
|
||||
yarn build --build-type beta dist
|
||||
yarn build --build-type beta prod
|
||||
export ENABLE_MV3=true
|
||||
yarn build --build-type beta --platform='chrome' dist
|
||||
yarn build --build-type beta --platform='chrome' prod
|
||||
else
|
||||
printf '%s\n' 'Commit message does not match commit message for beta pattern; skipping beta automation build'
|
||||
mkdir dist
|
||||
|
11
.github/workflows/fitness-functions.yml
vendored
11
.github/workflows/fitness-functions.yml
vendored
@ -17,12 +17,13 @@ jobs:
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Run fitness functions
|
||||
env:
|
||||
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||
run: |
|
||||
# git fetch origin $HEAD_REF
|
||||
# git fetch origin $BASE_REF
|
||||
# git diff origin/$BASE_REF origin/$HEAD_REF -- . ':(exclude)development/fitness-functions/*' > diff
|
||||
# npm run fitness-functions -- "ci" "./diff"
|
||||
git fetch origin $BASE_REF
|
||||
git diff origin/$BASE_REF HEAD -- . > diff
|
||||
npm run fitness-functions -- "ci" "./diff"
|
@ -1,11 +1,15 @@
|
||||
; Extra environment variables
|
||||
PASSWORD=METAMASK PASSWORD
|
||||
; Defaults are set in builds.yml
|
||||
|
||||
; This variable is required
|
||||
INFURA_PROJECT_ID=00000000000
|
||||
SEGMENT_WRITE_KEY=
|
||||
SWAPS_USE_DEV_APIS=
|
||||
PORTFOLIO_URL=
|
||||
TRANSACTION_SECURITY_PROVIDER=
|
||||
MULTICHAIN=
|
||||
|
||||
;PASSWORD=METAMASK PASSWORD
|
||||
,SEGMENT_WRITE_KEY=
|
||||
;SWAPS_USE_DEV_APIS=
|
||||
;PORTFOLIO_URL=
|
||||
;TRANSACTION_SECURITY_PROVIDER=
|
||||
;MULTICHAIN=
|
||||
|
||||
; Set this to test changes to the phishing warning page.
|
||||
PHISHING_WARNING_PAGE_URL=
|
||||
;PHISHING_WARNING_PAGE_URL=
|
||||
|
34
app/_locales/de/messages.json
generated
34
app/_locales/de/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 ist mit keiner Site verbunden.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1-Snap ist mit diesen Sites verbunden. Sie haben Zugriff auf die oben aufgeführten Berechtigungen.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Verbinden..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "Dateiimport fehlgeschlagen? Bitte hier klicken!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Details ansehen",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Hinzugefügt am",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "von",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "Sie sollten diese Erweiterung deinstallieren",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Diese Gasgebühr wurde von $1 vorgeschlagen. Dies kann ein Problem mit Ihrer Transaktion verursachen. Bei Fragen wenden Sie sich bitte an $1.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Gasgebühr"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Gaslimit"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Keine passenden Ergebnisse gefunden."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Zeigen"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Intelligente Transaktionen"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1-Snap hat Zugriff auf:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Am $1 von $2 hinzugefügt",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Diese Inhalte stammen von $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Verwalten Sie Ihre Snaps"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Snap-Status ist von der Aktivität abhängig."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Ein Snap wird nur ausgeführt, wenn er aktiviert ist"
|
||||
},
|
||||
|
34
app/_locales/el/messages.json
generated
34
app/_locales/el/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 δεν είναι συνδεδεμένο με καμία τοποθεσία.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "Το snap $1 συνδέεται με αυτούς τους ιστότοπους. Έχουν πρόσβαση στις παραπάνω άδειες.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Σύνδεση..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "Η εισαγωγή αρχείων δεν λειτουργεί; Κάντε κλικ εδώ!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Προβολή λεπτομερειών",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Προστέθηκε στις",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "από",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "θα πρέπει να καταργήσετε την εγκατάσταση αυτής της επέκτασης",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Αυτό το τέλος συναλλαγής έχει προταθεί από το $1. Η παράκαμψη μπορεί να προκαλέσει πρόβλημα με τη συναλλαγή σας. Εάν έχετε απορίες, επικοινωνήστε με $1.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Τέλη Συναλλαγής"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Όριο τέλους συναλλαγής"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Δε βρέθηκαν αποτελέσματα που να ταιριάζουν."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Εμφάνιση"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Έξυπνη Συναλλαγή"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "Το snap $1 έχει πρόσβαση σε:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Προστέθηκε στις $1 από $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Αυτό το περιεχόμενο προέρχεται από το $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Διαχειριστείτε τα Snaps σας"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Η κατάσταση του Snap εξαρτάται από τη δραστηριότητα."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Ένα snap θα εκτελεστεί μόνο εάν είναι ενεργοποιημένο"
|
||||
},
|
||||
|
115
app/_locales/en/messages.json
generated
115
app/_locales/en/messages.json
generated
@ -333,6 +333,12 @@
|
||||
"alerts": {
|
||||
"message": "Alerts"
|
||||
},
|
||||
"allCustodianAccountsConnectedSubtitle": {
|
||||
"message": "You have either already connected all your custodian accounts or don’t have any account to connect to MetaMask Institutional."
|
||||
},
|
||||
"allCustodianAccountsConnectedTitle": {
|
||||
"message": "No accounts available to connect"
|
||||
},
|
||||
"allOfYour": {
|
||||
"message": "All of your $1",
|
||||
"description": "$1 is the symbol or name of the token that the user is approving spending"
|
||||
@ -466,6 +472,9 @@
|
||||
"average": {
|
||||
"message": "Average"
|
||||
},
|
||||
"awaitingApproval": {
|
||||
"message": "Awaiting approval..."
|
||||
},
|
||||
"back": {
|
||||
"message": "Back"
|
||||
},
|
||||
@ -721,6 +730,12 @@
|
||||
"connectAccountOrCreate": {
|
||||
"message": "Connect account or create new"
|
||||
},
|
||||
"connectCustodialAccountMsg": {
|
||||
"message": "Please choose the custodian you want to connect in order to add or refresh a token."
|
||||
},
|
||||
"connectCustodialAccountTitle": {
|
||||
"message": "Custodial Accounts"
|
||||
},
|
||||
"connectHardwareWallet": {
|
||||
"message": "Connect hardware wallet"
|
||||
},
|
||||
@ -771,10 +786,6 @@
|
||||
"message": "$1 is not connected to any sites.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1 snap is connected to these sites. They have access to the permissions listed above.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connecting..."
|
||||
},
|
||||
@ -936,6 +947,27 @@
|
||||
"custodianAccount": {
|
||||
"message": "Custodian account"
|
||||
},
|
||||
"custodianReplaceRefreshTokenChangedFailed": {
|
||||
"message": "Please go to $1 and click the 'Connect to MMI' button within their user interface to connect your accounts to MMI again."
|
||||
},
|
||||
"custodianReplaceRefreshTokenChangedSubtitle": {
|
||||
"message": "You can now use your custodian accounts in MetaMask Institutional."
|
||||
},
|
||||
"custodianReplaceRefreshTokenChangedTitle": {
|
||||
"message": "Your custodian token has been refreshed"
|
||||
},
|
||||
"custodianReplaceRefreshTokenSubtitle": {
|
||||
"message": "This is will replace the custodian token for the following address:"
|
||||
},
|
||||
"custodianReplaceRefreshTokenTitle": {
|
||||
"message": "Replace custodian token"
|
||||
},
|
||||
"custodyApiUrl": {
|
||||
"message": "$1 API URL"
|
||||
},
|
||||
"custodyDeeplinkDescription": {
|
||||
"message": "Approve the transaction in the $1 app. Once all required custody approvals have been performed the transaction will complete. Check your $1 app for status."
|
||||
},
|
||||
"custodyRefreshTokenModalDescription": {
|
||||
"message": "Please go to $1 and click the 'Connect to MMI' button within their user interface to connect your accounts to MMI again."
|
||||
},
|
||||
@ -1064,6 +1096,10 @@
|
||||
"description": {
|
||||
"message": "Description"
|
||||
},
|
||||
"descriptionFromSnap": {
|
||||
"message": "Description from $1",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"desktopConnectionCriticalErrorDescription": {
|
||||
"message": "This error could be intermittent, so try restarting the extension or disable MetaMask Desktop."
|
||||
},
|
||||
@ -1328,6 +1364,9 @@
|
||||
"enableSmartTransactions": {
|
||||
"message": "Enable smart transactions"
|
||||
},
|
||||
"enableSnap": {
|
||||
"message": "Enable"
|
||||
},
|
||||
"enableToken": {
|
||||
"message": "enable $1",
|
||||
"description": "$1 is a token symbol, e.g. ETH"
|
||||
@ -1364,6 +1403,9 @@
|
||||
"enterANumber": {
|
||||
"message": "Enter a number"
|
||||
},
|
||||
"enterCustodianToken": {
|
||||
"message": "Enter your $1 token or add a new token"
|
||||
},
|
||||
"enterMaxSpendLimit": {
|
||||
"message": "Enter max spend limit"
|
||||
},
|
||||
@ -1478,18 +1520,6 @@
|
||||
"fileTooBig": {
|
||||
"message": "The dropped file is too big."
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "See details",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Added on",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "from",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "you should uninstall this extension",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1555,9 +1585,6 @@
|
||||
"message": "This gas fee has been suggested by $1. Overriding this may cause a problem with your transaction. Please reach out to $1 if you have questions.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Gas fee"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Gas limit"
|
||||
},
|
||||
@ -1855,6 +1882,13 @@
|
||||
"install": {
|
||||
"message": "Install"
|
||||
},
|
||||
"installOrigin": {
|
||||
"message": "Install origin"
|
||||
},
|
||||
"installedOn": {
|
||||
"message": "Installed on $1",
|
||||
"description": "$1 is the date when the snap has been installed"
|
||||
},
|
||||
"institutionalFeatures": {
|
||||
"message": "Institutional Features"
|
||||
},
|
||||
@ -1969,6 +2003,9 @@
|
||||
"lastSold": {
|
||||
"message": "Last sold"
|
||||
},
|
||||
"layer1Fees": {
|
||||
"message": "Layer 1 fees"
|
||||
},
|
||||
"learnCancelSpeeedup": {
|
||||
"message": "Learn how to $1",
|
||||
"description": "$1 is link to cancel or speed up transactions"
|
||||
@ -1980,6 +2017,9 @@
|
||||
"message": "Want to $1 about gas?",
|
||||
"description": "$1 will be replaced by the learnMore translation key"
|
||||
},
|
||||
"learnMoreKeystone": {
|
||||
"message": "Learn More"
|
||||
},
|
||||
"learnMoreUpperCase": {
|
||||
"message": "Learn more"
|
||||
},
|
||||
@ -2152,6 +2192,9 @@
|
||||
"metrics": {
|
||||
"message": "Metrics"
|
||||
},
|
||||
"mismatchAccount": {
|
||||
"message": "Your selected account ($1) is different than the account trying to sign ($2)"
|
||||
},
|
||||
"mismatchedChainLinkText": {
|
||||
"message": "verify the network details",
|
||||
"description": "Serves as link text for the 'mismatchedChain' key. This text will be embedded inside the translation for that key."
|
||||
@ -2190,6 +2233,9 @@
|
||||
"mmiAuthenticate": {
|
||||
"message": "The page at $1 would like to authorise the following project’s compliance settings in MetaMask Institutional"
|
||||
},
|
||||
"more": {
|
||||
"message": "more"
|
||||
},
|
||||
"moreComingSoon": {
|
||||
"message": "More coming soon..."
|
||||
},
|
||||
@ -2881,6 +2927,9 @@
|
||||
"passwordsDontMatch": {
|
||||
"message": "Passwords don't match"
|
||||
},
|
||||
"pasteJWTToken": {
|
||||
"message": "Paste or drop your token here:"
|
||||
},
|
||||
"pastePrivateKey": {
|
||||
"message": "Enter your private key string here:",
|
||||
"description": "For importing an account from a private key"
|
||||
@ -3491,18 +3540,27 @@
|
||||
"seedPhraseWriteDownHeader": {
|
||||
"message": "Write down your Secret Recovery Phrase"
|
||||
},
|
||||
"select": {
|
||||
"message": "Select"
|
||||
},
|
||||
"selectAccounts": {
|
||||
"message": "Select the account(s) to use on this site"
|
||||
},
|
||||
"selectAll": {
|
||||
"message": "Select all"
|
||||
},
|
||||
"selectAllAccounts": {
|
||||
"message": "Select all accounts"
|
||||
},
|
||||
"selectAnAccount": {
|
||||
"message": "Select an account"
|
||||
},
|
||||
"selectAnAccountAlreadyConnected": {
|
||||
"message": "This account has already been connected to MetaMask"
|
||||
},
|
||||
"selectAnAccountHelp": {
|
||||
"message": "Select the custodian accounts to use in MetaMask Institutional."
|
||||
},
|
||||
"selectHdPath": {
|
||||
"message": "Select HD path"
|
||||
},
|
||||
@ -3574,9 +3632,9 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "No matching results found."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"shortVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
"description": "$1 is the version number to show"
|
||||
},
|
||||
"show": {
|
||||
"message": "Show"
|
||||
@ -3654,14 +3712,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Smart transaction"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1 snap has access to:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Added on $1 from $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "This content is coming from $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3733,9 +3783,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Manage your Snaps"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Snap status is dependent on activity."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "A snap will only run if it is enabled"
|
||||
},
|
||||
@ -4523,7 +4570,6 @@
|
||||
"transactionFailed": {
|
||||
"message": "Transaction Failed"
|
||||
},
|
||||
|
||||
"transactionFee": {
|
||||
"message": "Transaction fee"
|
||||
},
|
||||
@ -4750,6 +4796,9 @@
|
||||
"message": "Verify this token on $1 and make sure this is the token you want to trade.",
|
||||
"description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\""
|
||||
},
|
||||
"version": {
|
||||
"message": "Version"
|
||||
},
|
||||
"view": {
|
||||
"message": "View"
|
||||
},
|
||||
|
34
app/_locales/es/messages.json
generated
34
app/_locales/es/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 no está conectado a ningún sitio.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "El complemento de $1 está conectado a estos sitios. Tienen acceso a los permisos enumerados anteriormente.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Estableciendo conexión…"
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "¿No funciona la importación del archivo? Haga clic aquí.",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Ver detalles",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Añadido el",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "de",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "le recomendamos que desinstale esta extensión",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Esta tarifa de gas ha sido sugerida por $1. Anularla puede causar un problema con su transacción. Comuníquese con $1 si tiene preguntas.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Cuota de gas"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Límite de gas"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "No se encontraron resultados coincidentes."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Mostrar"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Transacción inteligente"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "El complemento de $1 tiene acceso a:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Se agregó en $1 de $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Este contenido proviene de $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Administre sus complementos"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "El estado del complemento depende de la actividad."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Un complemento solo se ejecutará si está habilitado"
|
||||
},
|
||||
|
12
app/_locales/es_419/messages.json
generated
12
app/_locales/es_419/messages.json
generated
@ -882,18 +882,6 @@
|
||||
"message": "¿No funciona la importación del archivo? ¡Haga clic aquí!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Ver detalles",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Añadido el",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "de",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "le recomendamos que desinstale esta extensión",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
|
34
app/_locales/fr/messages.json
generated
34
app/_locales/fr/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 n’est connecté à aucun site.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "Le snap $1 est connecté à ces sites. Ils ont accès aux autorisations énumérées ci-dessus.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connexion…"
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "L’importation de fichier ne fonctionne pas ? Cliquez ici !",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Voir les détails",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Ajouté le",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "de",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "vous devriez désinstaller cette extension",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Ce prix de carburant a été suggéré par $1. Si vous n’en tenez pas compte, vous risquez de rencontrer des difficultés lors de votre transaction. Veuillez contacter $1 pour toute question.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Frais de transaction"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Montant maximal des frais de transaction"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Aucun résultat correspondant trouvé."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Afficher"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Transaction intelligente"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "Le snap $1 peut accéder à :",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Ajouté le $1 à partir de $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Ce contenu provient de $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Gérez vos Snaps"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "L’état du Snap dépend de l’activité."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Un snap ne s’exécute que s’il est activé"
|
||||
},
|
||||
|
34
app/_locales/hi/messages.json
generated
34
app/_locales/hi/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 किसी भी साइट से कनेक्ट नहीं है।",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1 स्नैप इन साइटों से जुड़ा है। वे ऊपर सूचीबद्ध अनुमतियों को एक्सेस कर सकती हैं।",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "कनेक्ट किया जा रहा है..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "फाइल आयात काम नहीं कर रहा है? यहां क्लिक करें!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "विवरण देखें",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "जोड़ा गया",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "से",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "आपको इस एक्सटेन्शन को अनइंस्टाल करना चाहिए",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "यह गैस शुल्क $1 द्वारा सुझाया गया है। इसे ओवरराइड करने से आपके लेन-देन में समस्या हो सकती है। यदि आपके पास कोई सवाल हैं तो कृपया $1 तक पहुंचें।",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "गैस शुल्क"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "गैस की सीमा"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "कोई मेल खाने वाला परिणाम नहीं मिला।"
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "दिखाएं"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "स्मार्ट लेनदेन"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1 स्नैप को एक्सेस है:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "$2 से $1 जोड़ा गया",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "यह सामग्री $1 से आ रही है",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "अपने स्नैप्स प्रबंधित करें"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "स्नैप स्टेटस एक्टिविटी पर निर्भर करता है।"
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "कोई स्नैप तभी चलेगा जब उसे सक्षम किया गया हो"
|
||||
},
|
||||
|
34
app/_locales/id/messages.json
generated
34
app/_locales/id/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 tidak terhubung ke situs mana pun.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "Snap $1 terhubung ke situs-situs ini. Token ini memiliki akses ke izin yang tercantum di atas. \t",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Menghubungkan..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "Impor file tidak bekerja? Klik di sini!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Lihat detailnya",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Ditambahkan di",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "dari",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "Anda harus menghapus ekstensi ini",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Biaya gas ini telah disarankan oleh $1. Pengabaian dapat menyebabkan masalah pada transaksi Anda. Hubungi $1 jika ada pertanyaan.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Biaya gas"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Batas gas"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Tidak menemukan hasil yang cocok."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Tampil"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Transaksi pintar"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "Snap $1 memiliki akses ke:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Ditambahkan pada $1 dari $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Konten ini berasal dari $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Kelola Snap Anda"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Status snap tergantung pada aktivitas."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Snap hanya akan beroperasi jika diaktifkan"
|
||||
},
|
||||
|
4
app/_locales/it/messages.json
generated
4
app/_locales/it/messages.json
generated
@ -538,10 +538,6 @@
|
||||
"message": "$1 non è connesso ad alcun sito.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1 snap è collegato a questi siti. Hanno accesso alle autorizzazioni sopra elencate.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connessione..."
|
||||
},
|
||||
|
34
app/_locales/ja/messages.json
generated
34
app/_locales/ja/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1はどのサイトとも接続されていません。",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1 スナップはこれらのサイトに接続されており、上記のパーミッションにアクセスできます。",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "接続中..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "ファイルのインポートが機能していない場合ここをクリックしてください!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "詳細を表示",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "追加日",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "元",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "この拡張機能はアンインストールしてください",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "このガス代は$1により提案されています。これを上書きすると、トランザクションに問題が発生する可能性があります。ご質問がございましたら、$1までお問い合わせください。",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "ガス代"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "ガスリミット"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "一致する結果が見つかりませんでした。"
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "表示"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "スマートトランザクション"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1 スナップは次にアクセス可能です:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "$1 に $2 から追加",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "このコンテンツは $1 からのものです",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "スナップの管理"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "スナップのステータスはアクティビティによります。"
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "スナップは有効になっている場合にのみ実行されます"
|
||||
},
|
||||
|
82
app/_locales/ko/messages.json
generated
82
app/_locales/ko/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 계정은 어떤 사이트에도 연결되어 있지 않습니다.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1 스냅이 이 사이트에 연결되어 있습니다. 이 사이트는 위에 나열된 접근 권한이 있습니다.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "연결 중..."
|
||||
},
|
||||
@ -918,10 +914,10 @@
|
||||
"message": "소수점 이하 자릿수는 0 이상, 36 이하여야 합니다."
|
||||
},
|
||||
"decrypt": {
|
||||
"message": "암호 해독"
|
||||
"message": "복호화"
|
||||
},
|
||||
"decryptCopy": {
|
||||
"message": "암호 해독된 메시지 복사"
|
||||
"message": "복호화된 메시지 복사"
|
||||
},
|
||||
"decryptInlineError": {
|
||||
"message": "다음 오류 때문에 이 메시지를 해독할 수 없습니다: $1",
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "파일 가져오기가 작동하지 않나요? 여기를 클릭하세요.",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "세부 정보 보기",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "추가하기",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "발신",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "이 확장 프로그램을 삭제해야 합니다",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "$1에서 이 가스 요금을 제안했습니다. 이를 무시하면 거래에 문제가 발생할 수 있습니다. 질문이 있는 경우 $1에 문의하세요.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "가스 수수료"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "가스 한도"
|
||||
},
|
||||
@ -2176,7 +2157,7 @@
|
||||
"message": "다음"
|
||||
},
|
||||
"nextNonceWarning": {
|
||||
"message": "임시값이 권장 임시값인 $1보다 큽니다.",
|
||||
"message": "논스값이 권장 논스값인 $1보다 큽니다.",
|
||||
"description": "The next nonce according to MetaMask's internal logic"
|
||||
},
|
||||
"nftAddFailedMessage": {
|
||||
@ -2243,13 +2224,13 @@
|
||||
"message": "웹캠을 찾을 수 없음"
|
||||
},
|
||||
"nonce": {
|
||||
"message": "임시값"
|
||||
"message": "논스"
|
||||
},
|
||||
"nonceField": {
|
||||
"message": "거래 임시값 맞춤화"
|
||||
"message": "거래 논스 맞춤화"
|
||||
},
|
||||
"nonceFieldDescription": {
|
||||
"message": "이 기능을 켜면 확인 화면에서 임시값(거래 번호)을 변경할 수 있습니다. 이는 고급 기능으로, 주의해서 사용해야 합니다."
|
||||
"message": "이 기능을 켜면 확인 화면에서 논스(거래 번호)를 변경할 수 있습니다. 이는 고급 기능으로, 주의해서 사용해야 합니다."
|
||||
},
|
||||
"nonceFieldHeading": {
|
||||
"message": "커스텀 논스"
|
||||
@ -2349,7 +2330,7 @@
|
||||
"description": "The 'call to action' on the button, or link, of the 'Swap on Binance Smart Chain!' notification. Upon clicking, users will be taken to a page where then can swap tokens on Binance Smart Chain."
|
||||
},
|
||||
"notifications4Description": {
|
||||
"message": "토큰 스왑 최고가를 지갑에서 바로 이용하세요. MetaMask는 이제 바이낸스 스마트 체인의 여러 분산형 교환 애그리게이터 및 투자전문기관과 연결됩니다.",
|
||||
"message": "토큰 스왑 최고가를 지갑에서 바로 이용하세요. MetaMask는 이제 바이낸스 스마트 체인의 여러 탈중앙화 거래소 애그리게이터 및 투자전문기관과 연결됩니다.",
|
||||
"description": "Description of a notification in the 'See What's New' popup."
|
||||
},
|
||||
"notifications4Title": {
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "검색 결과가 없습니다."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "보기"
|
||||
},
|
||||
@ -3287,7 +3264,7 @@
|
||||
"message": "본 메시지에 서명하는 행위는 위험의 가능성을 내포하고 있습니다. 본 서명을 통해 메시지 발신 당사자에게 귀하의 계정 및 모든 자산에 대해 완전한 권한을 부여할 수 있기 때문입니다. 이를 통해 계정의 모든 잔액을 인출할 수 있기도 하다는 뜻입니다. 주의하여 진행하세요. $1"
|
||||
},
|
||||
"signed": {
|
||||
"message": "서명완료"
|
||||
"message": "서명 완료"
|
||||
},
|
||||
"signin": {
|
||||
"message": "로그인"
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "스마트 트랜잭션"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1 스냅이 접근할 수 있는 대상:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "$1에 $2에서 추가됨",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "콘텐츠 출처: $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "스냅 관리"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "스냅 상태는 활동에 따라 달라집니다."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "스냅은 활성화된 상태에서만 작동합니다."
|
||||
},
|
||||
@ -3609,7 +3575,7 @@
|
||||
"message": "보장 금액"
|
||||
},
|
||||
"swapAmountReceivedInfo": {
|
||||
"message": "수신하는 최소 금액입니다. 슬리패지에 따라 추가 금액을 받을 수도 있습니다."
|
||||
"message": "수신하는 최소 금액입니다. 슬리피지에 따라 추가 금액을 받을 수도 있습니다."
|
||||
},
|
||||
"swapApproval": {
|
||||
"message": "스왑을 위해 $1 승인",
|
||||
@ -3640,7 +3606,7 @@
|
||||
"message": "맞춤형"
|
||||
},
|
||||
"swapDecentralizedExchange": {
|
||||
"message": "분산형 교환"
|
||||
"message": "탈중앙화 거래소"
|
||||
},
|
||||
"swapDirectContract": {
|
||||
"message": "직접 계약"
|
||||
@ -3703,17 +3669,17 @@
|
||||
"description": "$1 is the selected network, e.g. Ethereum or BSC"
|
||||
},
|
||||
"swapHighSlippageWarning": {
|
||||
"message": "슬리패지 금액이 아주 큽니다."
|
||||
"message": "슬리피지 금액이 아주 큽니다."
|
||||
},
|
||||
"swapIncludesMMFee": {
|
||||
"message": "$1%의 MetaMask 요금이 포함됩니다.",
|
||||
"description": "Provides information about the fee that metamask takes for swaps. $1 is a decimal number."
|
||||
},
|
||||
"swapLowSlippageError": {
|
||||
"message": "거래가 실패할 수도 있습니다. 최대 슬리패지가 너무 낮습니다."
|
||||
"message": "거래가 실패할 수도 있습니다. 최대 슬리피지가 너무 낮습니다."
|
||||
},
|
||||
"swapMaxSlippage": {
|
||||
"message": "최대 슬리패지"
|
||||
"message": "최대 슬리피지"
|
||||
},
|
||||
"swapMetaMaskFee": {
|
||||
"message": "MetaMask 수수료"
|
||||
@ -3749,7 +3715,7 @@
|
||||
"message": "시장 가격 데이터가 부족하여 가격 영향을 파악할 수 없습니다. 스왑하기 전에 받게 될 토큰 수가 만족스러운지 확인하시기 바랍니다."
|
||||
},
|
||||
"swapPriceUnavailableTitle": {
|
||||
"message": "진행하기 전에 요율 확인"
|
||||
"message": "진행하기 전에 비율 확인"
|
||||
},
|
||||
"swapProcessing": {
|
||||
"message": "처리 중"
|
||||
@ -3761,25 +3727,25 @@
|
||||
"message": "견적 소스"
|
||||
},
|
||||
"swapQuotesExpiredErrorDescription": {
|
||||
"message": "새 견적을 요청해 최신 요율을 확인하세요."
|
||||
"message": "새 견적을 요청해 최신 비율을 확인하세요."
|
||||
},
|
||||
"swapQuotesExpiredErrorTitle": {
|
||||
"message": "견적 시간 초과"
|
||||
},
|
||||
"swapQuotesNotAvailableErrorDescription": {
|
||||
"message": "금액 또는 슬리패지 설정을 조정한 후 다시 시도해 보세요."
|
||||
"message": "금액 또는 슬리피지 설정을 조정한 후 다시 시도해 보세요."
|
||||
},
|
||||
"swapQuotesNotAvailableErrorTitle": {
|
||||
"message": "사용 가능한 견적 없음"
|
||||
},
|
||||
"swapRate": {
|
||||
"message": "요율"
|
||||
"message": "비율"
|
||||
},
|
||||
"swapReceiving": {
|
||||
"message": "수신 중"
|
||||
},
|
||||
"swapReceivingInfoTooltip": {
|
||||
"message": "이것은 예상치입니다. 정확한 금액은 슬리패지에 따라 달라집니다."
|
||||
"message": "이것은 예상치입니다. 정확한 금액은 슬리피지에 따라 달라집니다."
|
||||
},
|
||||
"swapRequestForQuotation": {
|
||||
"message": "견적 요청"
|
||||
@ -3803,20 +3769,20 @@
|
||||
"message": "다음은 여러 유동성 소스에서 수집한 전체 견적입니다."
|
||||
},
|
||||
"swapSlippageNegative": {
|
||||
"message": "슬리패지는 0보다 크거나 같아야 합니다."
|
||||
"message": "슬리피지는 0보다 크거나 같아야 합니다."
|
||||
},
|
||||
"swapSlippagePercent": {
|
||||
"message": "$1%",
|
||||
"description": "$1 is the amount of % for slippage"
|
||||
},
|
||||
"swapSlippageTooltip": {
|
||||
"message": "주문 시점과 확인 시점 사이에 가격이 변동되는 현상을 \"슬리패지\"라고 합니다. 슬리패지가 \"최대 슬리패지\" 설정을 초과하면 스왑이 자동으로 취소됩니다."
|
||||
"message": "주문 시점과 확인 시점 사이에 가격이 변동되는 현상을 \"슬리피지\"라고 합니다. 슬리피지가 \"최대 슬리피지\" 설정을 초과하면 스왑이 자동으로 취소됩니다."
|
||||
},
|
||||
"swapSource": {
|
||||
"message": "유동성 소스"
|
||||
},
|
||||
"swapSourceInfo": {
|
||||
"message": "저희는 여러 유동성 소스(교환, 애그리게이터, 투자전문기관)를 검색하여 최상의 요율과 최저 네트워크 수수료를 찾아드립니다."
|
||||
"message": "저희는 여러 유동성 소스(교환, 애그리게이터, 투자전문기관)를 검색하여 최상의 비율과 최저 네트워크 수수료를 찾아드립니다."
|
||||
},
|
||||
"swapSuggested": {
|
||||
"message": "제안 스왑"
|
||||
@ -3884,13 +3850,13 @@
|
||||
"description": "Tells the user how much of a token they have in their balance. $1 is a decimal number amount of tokens, and $2 is a token symbol"
|
||||
},
|
||||
"swapZeroSlippage": {
|
||||
"message": "0% 슬리패지"
|
||||
"message": "0% 슬리피지"
|
||||
},
|
||||
"swapsAdvancedOptions": {
|
||||
"message": "고급 옵션"
|
||||
},
|
||||
"swapsExcessiveSlippageWarning": {
|
||||
"message": "슬리패지 금액이 너무 커서 전환율이 좋지 않습니다. 슬리패지 허용치를 15% 값 이하로 줄이세요."
|
||||
"message": "슬리피지 금액이 너무 커서 전환율이 좋지 않습니다. 슬리피지 허용치를 15% 값 이하로 줄이세요."
|
||||
},
|
||||
"swapsMaxSlippage": {
|
||||
"message": "슬리피지 허용치"
|
||||
@ -4297,7 +4263,7 @@
|
||||
"message": "연락처 정보 인증"
|
||||
},
|
||||
"verifyThisTokenDecimalOn": {
|
||||
"message": "토큰 십진수는 $1에서 찾을 수 있습니다.",
|
||||
"message": "토큰 소수점은 $1에서 찾을 수 있습니다.",
|
||||
"description": "Points the user to etherscan as a place they can verify information about a token. $1 is replaced with the translation for \"etherscan\""
|
||||
},
|
||||
"verifyThisTokenOn": {
|
||||
|
34
app/_locales/pt/messages.json
generated
34
app/_locales/pt/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 não está conectada a nenhum site.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "O snap $1 está conectado a estes sites. Eles têm acesso às permissões listadas acima.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Conectando..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "A importação de ficheiro não está a funcionar? Carregue aqui!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Ver detalhes",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Adicionado em",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "de",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "você deve desinstalar essa extensão",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Essa taxa de gás foi sugerida por $1. Sua substituição pode causar um problema com a sua transação. Entre em contato com $1 se tiver perguntas.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Taxa de gás"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Limite de gás"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Nenhum resultado correspondente encontrado."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Exibir"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Transação inteligente"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "Snap $1 tem acesso a:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Adicionado em $1 a partir de $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Esse conteúdo vem de $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Gerencie seus snaps"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "O status do snap depende da atividade."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "O snap só será executado se estiver ativado"
|
||||
},
|
||||
|
12
app/_locales/pt_BR/messages.json
generated
12
app/_locales/pt_BR/messages.json
generated
@ -882,18 +882,6 @@
|
||||
"message": "A importação de arquivo não está funcionando? Clique aqui!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Ver detalhes",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Adicionado em",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "de",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "você deve desinstalar essa extensão",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
|
34
app/_locales/ru/messages.json
generated
34
app/_locales/ru/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 не подключен ни к каким сайтам.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "Снап $1 подключен к этим сайтам. У них есть доступ к перечисленным выше разрешениям.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Подключение..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "Импорт файлов не работает? Нажмите здесь!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "См. подробности",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Добавлена",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "от",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "вам нужно должны удалить это расширение",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Эта плата за газ была предложена $1. Ее переопредление может вызвать проблемы с вашей транзакцией. При наличии вопросов обратитесь к $1.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Плата за газ"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Лимит газа"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Совпадений не найдено."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Показать"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Смарт-транзакция"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "У снапа $1 есть доступ к:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Добавлено на $1 из $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Этот контент поступает от $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Управление вашим снапами"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Статус снапа зависит от активности."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Снап будет работать только в том случае, если он включен"
|
||||
},
|
||||
|
34
app/_locales/tl/messages.json
generated
34
app/_locales/tl/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "Ang $1 ay hindi nakakonekta sa anumang site.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "Ang $1 snap ay konektado sa mga site na ito. May access sila sa mga pahintulot na nakalista sa itaas.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Kumokonekta..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "Hindi gumagana ang pag-import ng file? Mag-click dito!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Tingnan ang mga detalye",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Dinagdag sa",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "mula sa",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "dapat mong i-uninstall ang extension na ito",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Ang gas fee na ito ay iminungkahi ng $1. Ang pag-override dito ay maaaring magdulot ng problema sa iyong transaksyon. Mangyaring makipag-ugnayan sa $1 kung mayroon kang mga tanong.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Bayarin sa Gas"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Limitasyon sa Gas"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Walang nakitang katugmang resulta."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Ipakita"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Smart Transaction"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "Ang $1 snap ay may access sa:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Idinagdag noong $1 mula sa $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Ang nilalamang ito ay nagmumula sa $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Pamahalaan ang iyong mga Snap"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Ang lagay ng Snap ay nakadepende sa aktibidad."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Tatakbo lamang ang snap kapag pinagana ito"
|
||||
},
|
||||
|
34
app/_locales/tr/messages.json
generated
34
app/_locales/tr/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 herhangi bir siteye bağlanmamış.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1 snap bu sitelere bağlı. Yukarıda listelenen izinlere erişimleri vardır.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Bağlanıyor..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "Dosya içe aktarma çalışmıyor mu? Buraya tıklayın!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Ayrıntıları gör",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Şu tarihte eklendi:",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "şurada:",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "bu uzantıyı kaldırmalısın",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Bu gaz ücreti $1 tarafından önerilmiştir. Bu değerin başka bir değerle değiştirilmesi işleminizle ilgili bir soruna neden olabilir. Sorularınız olursa lütfen $1 ile iletişime geçin.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Gaz ücreti"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Gaz limiti"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Eşleşen sonuç bulunamadı."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "s$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Göster"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Akıllı işlem"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1 snap'in şunlara erişimi vardır:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "$1 tarihinde $2 alanından eklendi",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Bu içerik $1 kaynaklıdır",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Snap'lerini yönet"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Snap durumu etkinliğe bağlıdır."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Bir snap yalnızca etkinleştirilmişse çalışır"
|
||||
},
|
||||
|
34
app/_locales/vi/messages.json
generated
34
app/_locales/vi/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 chưa được kết nối với bất kỳ trang web nào.",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "Snap $1 được kết nối với các trang web này. Các trang web này được phép sử dụng những quyền được liệt kê ở trên.",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Đang kết nối..."
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "Tính năng nhập tập tin không hoạt động? Nhấn vào đây!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "Xem chi tiết",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "Đã thêm vào",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "từ",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "bạn nên gỡ cài đặt tiện ích mở rộng này",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "Phí gas này đã được gợi ý bởi $1. Việc sửa đổi có thể khiến giao dịch của bạn gặp sự cố. Vui lòng liên hệ với $1 nếu bạn có câu hỏi.",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "Phí gas"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "Giới hạn gas"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "Không tìm thấy kết quả trùng khớp."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "Hiển thị"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "Giao dịch thông minh"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "Snap $1 có quyền truy cập vào:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "Đã thêm vào $1 từ $2",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "Nội dung này đến từ $1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "Quản lý Snap"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Trạng thái Snap tùy thuộc vào hoạt động."
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Snap chỉ hoạt động khi đã bật"
|
||||
},
|
||||
|
34
app/_locales/zh_CN/messages.json
generated
34
app/_locales/zh_CN/messages.json
generated
@ -695,10 +695,6 @@
|
||||
"message": "$1 还没连接到任何网站。",
|
||||
"description": "$1 is the account name"
|
||||
},
|
||||
"connectedSnapSites": {
|
||||
"message": "$1的snap已连接到这些站点。它们有上述的访问权限。",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "连接中……"
|
||||
},
|
||||
@ -1355,18 +1351,6 @@
|
||||
"message": "文件导入失败?点击这里!",
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"flaskSnapSettingsCardButtonCta": {
|
||||
"message": "查看详细信息",
|
||||
"description": "Call to action a user can take to see more information about the snap that is installed"
|
||||
},
|
||||
"flaskSnapSettingsCardDateAddedOn": {
|
||||
"message": "添加于",
|
||||
"description": "Start of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskSnapSettingsCardFrom": {
|
||||
"message": "自",
|
||||
"description": "Part of the sentence describing when and where snap was added"
|
||||
},
|
||||
"flaskWelcomeUninstall": {
|
||||
"message": "您应该卸载此扩展程序",
|
||||
"description": "This request is shown on the Flask Welcome screen. It is intended for non-developers, and will be bolded."
|
||||
@ -1432,9 +1416,6 @@
|
||||
"message": "这笔燃料费是由 $1 建议的。忽略它可能会导致您的交易出现问题。如果您有疑问,请联系 $1。",
|
||||
"description": "$1 represents the Dapp's origin"
|
||||
},
|
||||
"gasFee": {
|
||||
"message": "燃料费"
|
||||
},
|
||||
"gasLimit": {
|
||||
"message": "燃料上限"
|
||||
},
|
||||
@ -3230,10 +3211,6 @@
|
||||
"settingsSearchMatchingNotFound": {
|
||||
"message": "没有找到匹配的结果."
|
||||
},
|
||||
"shorthandVersion": {
|
||||
"message": "v$1",
|
||||
"description": "$1 is replaced by a version string (e.g. 1.2.3)"
|
||||
},
|
||||
"show": {
|
||||
"message": "显示"
|
||||
},
|
||||
@ -3307,14 +3284,6 @@
|
||||
"smartTransaction": {
|
||||
"message": "智能交易"
|
||||
},
|
||||
"snapAccess": {
|
||||
"message": "$1的snap可以访问:",
|
||||
"description": "$1 represents the name of the snap"
|
||||
},
|
||||
"snapAdded": {
|
||||
"message": "从 $2 添加到 $1",
|
||||
"description": "$1 represents the date the snap was installed, $2 represents which origin installed the snap."
|
||||
},
|
||||
"snapContent": {
|
||||
"message": "此内容来自$1",
|
||||
"description": "This is shown when a snap shows transaction insight information in the confirmation UI. $1 is a link to the snap's settings page with the link text being the name of the snap."
|
||||
@ -3351,9 +3320,6 @@
|
||||
"snapsSettingsDescription": {
|
||||
"message": "管理您的Snap"
|
||||
},
|
||||
"snapsStatus": {
|
||||
"message": "Snap状态取决于活动。"
|
||||
},
|
||||
"snapsToggle": {
|
||||
"message": "Snap仅在启用后才会运行"
|
||||
},
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
EXTENSION_MESSAGES,
|
||||
PLATFORM_FIREFOX,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
MESSAGE_TYPE,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../shared/constants/app';
|
||||
@ -55,7 +55,7 @@ import { deferredPromise, getPlatform } from './lib/util';
|
||||
/* eslint-enable import/first */
|
||||
|
||||
/* eslint-disable import/order */
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
import {
|
||||
CONNECTION_TYPE_EXTERNAL,
|
||||
CONNECTION_TYPE_INTERNAL,
|
||||
@ -105,7 +105,7 @@ const PHISHING_WARNING_PAGE_TIMEOUT = ONE_SECOND_IN_MILLISECONDS;
|
||||
const ACK_KEEP_ALIVE_MESSAGE = 'ACK_KEEP_ALIVE_MESSAGE';
|
||||
const WORKER_KEEP_ALIVE_MESSAGE = 'WORKER_KEEP_ALIVE_MESSAGE';
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
const OVERRIDE_ORIGIN = {
|
||||
EXTENSION: 'EXTENSION',
|
||||
DESKTOP: 'DESKTOP_APP',
|
||||
@ -263,7 +263,7 @@ async function initialize() {
|
||||
const initState = await loadStateFromPersistence();
|
||||
const initLangCode = await getFirstPreferredLangCode();
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
await DesktopManager.init(platform.getVersion());
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
@ -525,7 +525,7 @@ export function setupController(
|
||||
* @param {Port} remotePort - The port provided by a new context.
|
||||
*/
|
||||
connectRemote = async (remotePort) => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
if (
|
||||
DesktopManager.isDesktopEnabled() &&
|
||||
OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()
|
||||
@ -651,7 +651,7 @@ export function setupController(
|
||||
|
||||
// communication with page or other extension
|
||||
connectExternal = (remotePort) => {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
if (
|
||||
DesktopManager.isDesktopEnabled() &&
|
||||
OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()
|
||||
@ -682,7 +682,7 @@ export function setupController(
|
||||
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
||||
updateBadge,
|
||||
);
|
||||
controller.decryptMessageManager.on(
|
||||
controller.decryptMessageController.hub.on(
|
||||
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
||||
updateBadge,
|
||||
);
|
||||
@ -725,14 +725,11 @@ export function setupController(
|
||||
}
|
||||
|
||||
function getUnapprovedTransactionCount() {
|
||||
const { unapprovedDecryptMsgCount } = controller.decryptMessageManager;
|
||||
const pendingApprovalCount =
|
||||
controller.approvalController.getTotalApprovalCount();
|
||||
const waitingForUnlockCount =
|
||||
controller.appStateController.waitingForUnlock.length;
|
||||
return (
|
||||
unapprovedDecryptMsgCount + pendingApprovalCount + waitingForUnlockCount
|
||||
);
|
||||
return pendingApprovalCount + waitingForUnlockCount;
|
||||
}
|
||||
|
||||
notificationManager.on(
|
||||
@ -753,14 +750,9 @@ export function setupController(
|
||||
controller.txController.txStateManager.setTxStatusRejected(txId),
|
||||
);
|
||||
controller.signController.rejectUnapproved(REJECT_NOTIFICATION_CLOSE_SIG);
|
||||
controller.decryptMessageManager.messages
|
||||
.filter((msg) => msg.status === 'unapproved')
|
||||
.forEach((tx) =>
|
||||
controller.decryptMessageManager.rejectMsg(
|
||||
tx.id,
|
||||
REJECT_NOTIFICATION_CLOSE,
|
||||
),
|
||||
);
|
||||
controller.decryptMessageController.rejectUnapproved(
|
||||
REJECT_NOTIFICATION_CLOSE,
|
||||
);
|
||||
controller.encryptionPublicKeyController.rejectUnapproved(
|
||||
REJECT_NOTIFICATION_CLOSE,
|
||||
);
|
||||
@ -769,7 +761,7 @@ export function setupController(
|
||||
Object.values(controller.approvalController.state.pendingApprovals).forEach(
|
||||
({ id, type }) => {
|
||||
switch (type) {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
case MESSAGE_TYPE.SNAP_DIALOG_ALERT:
|
||||
case MESSAGE_TYPE.SNAP_DIALOG_PROMPT:
|
||||
controller.approvalController.accept(id, null);
|
||||
@ -791,7 +783,7 @@ export function setupController(
|
||||
updateBadge();
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
if (OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()) {
|
||||
controller.store.subscribe((state) => {
|
||||
DesktopManager.setState(state);
|
||||
|
222
app/scripts/controllers/decrypt-message.test.ts
Normal file
222
app/scripts/controllers/decrypt-message.test.ts
Normal file
@ -0,0 +1,222 @@
|
||||
import { DecryptMessageManager } from '@metamask/message-manager';
|
||||
import { AbstractMessage } from '@metamask/message-manager/dist/AbstractMessageManager';
|
||||
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
||||
import DecryptMessageController, {
|
||||
DecryptMessageControllerMessenger,
|
||||
DecryptMessageControllerOptions,
|
||||
getDefaultState,
|
||||
} from './decrypt-message';
|
||||
|
||||
const messageIdMock = '12345';
|
||||
const messageMock = {
|
||||
metamaskId: messageIdMock,
|
||||
time: 123,
|
||||
status: 'unapproved',
|
||||
type: 'testType',
|
||||
rawSig: undefined,
|
||||
} as any as AbstractMessage;
|
||||
|
||||
const mockExtState = {};
|
||||
|
||||
jest.mock('@metamask/message-manager', () => ({
|
||||
DecryptMessageManager: jest.fn(),
|
||||
}));
|
||||
|
||||
const createKeyringControllerMock = () => ({
|
||||
decryptMessage: jest.fn(),
|
||||
});
|
||||
|
||||
const createMessengerMock = () =>
|
||||
({
|
||||
registerActionHandler: jest.fn(),
|
||||
publish: jest.fn(),
|
||||
call: jest.fn(),
|
||||
} as any as jest.Mocked<DecryptMessageControllerMessenger>);
|
||||
|
||||
const createDecryptMessageManagerMock = <T>() =>
|
||||
({
|
||||
getUnapprovedMessages: jest.fn(),
|
||||
getUnapprovedMessagesCount: jest.fn(),
|
||||
getMessage: jest.fn(),
|
||||
addUnapprovedMessageAsync: jest.fn(),
|
||||
approveMessage: jest.fn(),
|
||||
setMessageStatusAndResult: jest.fn(),
|
||||
rejectMessage: jest.fn(),
|
||||
update: jest.fn(),
|
||||
subscribe: jest.fn(),
|
||||
updateMessage: jest.fn(),
|
||||
updateMessageErrorInline: jest.fn(),
|
||||
setResult: jest.fn(),
|
||||
hub: {
|
||||
on: jest.fn(),
|
||||
},
|
||||
} as any as jest.Mocked<T>);
|
||||
|
||||
describe('EncryptionPublicKeyController', () => {
|
||||
let decryptMessageController: DecryptMessageController;
|
||||
|
||||
const decryptMessageManagerConstructorMock =
|
||||
DecryptMessageManager as jest.MockedClass<typeof DecryptMessageManager>;
|
||||
const getStateMock = jest.fn();
|
||||
const keyringControllerMock = createKeyringControllerMock();
|
||||
const messengerMock = createMessengerMock();
|
||||
const metricsEventMock = jest.fn();
|
||||
|
||||
const decryptMessageManagerMock =
|
||||
createDecryptMessageManagerMock<DecryptMessageManager>();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
||||
decryptMessageManagerConstructorMock.mockReturnValue(
|
||||
decryptMessageManagerMock,
|
||||
);
|
||||
|
||||
decryptMessageController = new DecryptMessageController({
|
||||
getState: getStateMock as any,
|
||||
keyringController: keyringControllerMock as any,
|
||||
messenger: messengerMock as any,
|
||||
metricsEvent: metricsEventMock as any,
|
||||
} as DecryptMessageControllerOptions);
|
||||
});
|
||||
|
||||
it('should return unapprovedMsgCount', () => {
|
||||
decryptMessageManagerMock.getUnapprovedMessagesCount.mockReturnValue(5);
|
||||
expect(decryptMessageController.unapprovedDecryptMsgCount).toBe(5);
|
||||
});
|
||||
|
||||
it('should reset state', () => {
|
||||
decryptMessageController.update(() => ({
|
||||
unapprovedDecryptMsgs: {
|
||||
[messageIdMock]: messageMock,
|
||||
} as any,
|
||||
unapprovedDecryptMsgCount: 1,
|
||||
}));
|
||||
decryptMessageController.resetState();
|
||||
expect(decryptMessageController.state).toStrictEqual(getDefaultState());
|
||||
});
|
||||
|
||||
it('should clear unapproved messages', () => {
|
||||
decryptMessageController.clearUnapproved();
|
||||
expect(decryptMessageController.state).toStrictEqual(getDefaultState());
|
||||
expect(decryptMessageManagerMock.update).toBeCalledTimes(1);
|
||||
});
|
||||
it('should add unapproved messages', async () => {
|
||||
await decryptMessageController.newRequestDecryptMessage(messageMock);
|
||||
|
||||
expect(decryptMessageManagerMock.addUnapprovedMessageAsync).toBeCalledTimes(
|
||||
1,
|
||||
);
|
||||
expect(decryptMessageManagerMock.addUnapprovedMessageAsync).toBeCalledWith(
|
||||
messageMock,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should decrypt message', async () => {
|
||||
const messageToDecrypt = {
|
||||
...messageMock,
|
||||
data: '0x7b22666f6f223a22626172227d',
|
||||
};
|
||||
decryptMessageManagerMock.approveMessage.mockResolvedValue(
|
||||
messageToDecrypt,
|
||||
);
|
||||
keyringControllerMock.decryptMessage.mockResolvedValue('decryptedMessage');
|
||||
getStateMock.mockReturnValue(mockExtState);
|
||||
|
||||
const result = await decryptMessageController.decryptMessage(
|
||||
messageToDecrypt,
|
||||
);
|
||||
|
||||
expect(decryptMessageManagerMock.approveMessage).toBeCalledTimes(1);
|
||||
expect(decryptMessageManagerMock.approveMessage).toBeCalledWith(
|
||||
messageToDecrypt,
|
||||
);
|
||||
expect(keyringControllerMock.decryptMessage).toBeCalledTimes(1);
|
||||
expect(keyringControllerMock.decryptMessage).toBeCalledWith(
|
||||
messageToDecrypt,
|
||||
);
|
||||
expect(decryptMessageManagerMock.setMessageStatusAndResult).toBeCalledTimes(
|
||||
1,
|
||||
);
|
||||
expect(decryptMessageManagerMock.setMessageStatusAndResult).toBeCalledWith(
|
||||
messageIdMock,
|
||||
'decryptedMessage',
|
||||
'decrypted',
|
||||
);
|
||||
expect(result).toBe(mockExtState);
|
||||
});
|
||||
|
||||
it('should cancel decrypt request', async () => {
|
||||
const messageToDecrypt = {
|
||||
...messageMock,
|
||||
data: '0x7b22666f6f223a22626172227d',
|
||||
};
|
||||
decryptMessageManagerMock.approveMessage.mockResolvedValue(
|
||||
messageToDecrypt,
|
||||
);
|
||||
keyringControllerMock.decryptMessage.mockRejectedValue(new Error('error'));
|
||||
getStateMock.mockReturnValue(mockExtState);
|
||||
|
||||
return expect(
|
||||
decryptMessageController.decryptMessage(messageToDecrypt),
|
||||
).rejects.toThrow('error');
|
||||
});
|
||||
|
||||
it('should decrypt message inline', async () => {
|
||||
const messageToDecrypt = {
|
||||
...messageMock,
|
||||
data: '0x7b22666f6f223a22626172227d',
|
||||
};
|
||||
decryptMessageManagerMock.getMessage.mockReturnValue(messageToDecrypt);
|
||||
keyringControllerMock.decryptMessage.mockResolvedValue('decryptedMessage');
|
||||
getStateMock.mockReturnValue(mockExtState);
|
||||
|
||||
const result = await decryptMessageController.decryptMessageInline(
|
||||
messageToDecrypt,
|
||||
);
|
||||
|
||||
expect(decryptMessageManagerMock.setResult).toBeCalledTimes(1);
|
||||
expect(decryptMessageManagerMock.setResult).toBeCalledWith(
|
||||
messageMock.metamaskId,
|
||||
'decryptedMessage',
|
||||
);
|
||||
expect(result).toBe(mockExtState);
|
||||
});
|
||||
|
||||
it('should be able to cancel decrypt message', async () => {
|
||||
decryptMessageManagerMock.rejectMessage.mockResolvedValue(messageMock);
|
||||
getStateMock.mockReturnValue(mockExtState);
|
||||
|
||||
const result = await decryptMessageController.cancelDecryptMessage(
|
||||
messageIdMock,
|
||||
);
|
||||
|
||||
expect(decryptMessageManagerMock.rejectMessage).toBeCalledTimes(1);
|
||||
expect(decryptMessageManagerMock.rejectMessage).toBeCalledWith(
|
||||
messageIdMock,
|
||||
);
|
||||
expect(result).toBe(mockExtState);
|
||||
});
|
||||
|
||||
it('should be able to reject all unapproved messages', async () => {
|
||||
decryptMessageManagerMock.getUnapprovedMessages.mockReturnValue({
|
||||
[messageIdMock]: messageMock,
|
||||
});
|
||||
|
||||
await decryptMessageController.rejectUnapproved('reason to cancel');
|
||||
|
||||
expect(decryptMessageManagerMock.rejectMessage).toBeCalledTimes(1);
|
||||
expect(decryptMessageManagerMock.rejectMessage).toBeCalledWith(
|
||||
messageIdMock,
|
||||
);
|
||||
expect(metricsEventMock).toBeCalledTimes(1);
|
||||
expect(metricsEventMock).toBeCalledWith({
|
||||
event: 'reason to cancel',
|
||||
category: MetaMetricsEventCategory.Messages,
|
||||
properties: {
|
||||
action: 'Decrypt Message Request',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
394
app/scripts/controllers/decrypt-message.ts
Normal file
394
app/scripts/controllers/decrypt-message.ts
Normal file
@ -0,0 +1,394 @@
|
||||
import EventEmitter from 'events';
|
||||
import log from 'loglevel';
|
||||
import {
|
||||
DecryptMessageManager,
|
||||
DecryptMessageParams,
|
||||
DecryptMessageParamsMetamask,
|
||||
} from '@metamask/message-manager';
|
||||
import { KeyringController } from '@metamask/eth-keyring-controller';
|
||||
import {
|
||||
AbstractMessage,
|
||||
AbstractMessageManager,
|
||||
AbstractMessageParams,
|
||||
AbstractMessageParamsMetamask,
|
||||
MessageManagerState,
|
||||
OriginalRequest,
|
||||
} from '@metamask/message-manager/dist/AbstractMessageManager';
|
||||
import {
|
||||
BaseControllerV2,
|
||||
RestrictedControllerMessenger,
|
||||
} from '@metamask/base-controller';
|
||||
import {
|
||||
AcceptRequest,
|
||||
AddApprovalRequest,
|
||||
RejectRequest,
|
||||
} from '@metamask/approval-controller';
|
||||
import { ApprovalType, ORIGIN_METAMASK } from '@metamask/controller-utils';
|
||||
import { Patch } from 'immer';
|
||||
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
||||
import { stripHexPrefix } from '../../../shared/modules/hexstring-utils';
|
||||
|
||||
const controllerName = 'DecryptMessageController';
|
||||
|
||||
const stateMetadata = {
|
||||
unapprovedDecryptMsgs: { persist: false, anonymous: false },
|
||||
unapprovedDecryptMsgCount: { persist: false, anonymous: false },
|
||||
};
|
||||
|
||||
export const getDefaultState = () => ({
|
||||
unapprovedDecryptMsgs: {},
|
||||
unapprovedDecryptMsgCount: 0,
|
||||
});
|
||||
|
||||
export type CoreMessage = AbstractMessage & {
|
||||
messageParams: AbstractMessageParams;
|
||||
};
|
||||
|
||||
export type StateMessage = Required<
|
||||
Omit<AbstractMessage, 'securityProviderResponse'>
|
||||
>;
|
||||
|
||||
export type DecryptMessageControllerState = {
|
||||
unapprovedDecryptMsgs: Record<string, StateMessage>;
|
||||
unapprovedDecryptMsgCount: number;
|
||||
};
|
||||
|
||||
export type GetDecryptMessageState = {
|
||||
type: `${typeof controllerName}:getState`;
|
||||
handler: () => DecryptMessageControllerState;
|
||||
};
|
||||
|
||||
export type DecryptMessageStateChange = {
|
||||
type: `${typeof controllerName}:stateChange`;
|
||||
payload: [DecryptMessageControllerState, Patch[]];
|
||||
};
|
||||
|
||||
export type DecryptMessageControllerActions = GetDecryptMessageState;
|
||||
|
||||
export type DecryptMessageControllerEvents = DecryptMessageStateChange;
|
||||
|
||||
type AllowedActions = AddApprovalRequest | AcceptRequest | RejectRequest;
|
||||
|
||||
export type DecryptMessageControllerMessenger = RestrictedControllerMessenger<
|
||||
typeof controllerName,
|
||||
DecryptMessageControllerActions | AllowedActions,
|
||||
DecryptMessageControllerEvents,
|
||||
AllowedActions['type'],
|
||||
never
|
||||
>;
|
||||
|
||||
export type DecryptMessageControllerOptions = {
|
||||
getState: () => any;
|
||||
keyringController: KeyringController;
|
||||
messenger: DecryptMessageControllerMessenger;
|
||||
metricsEvent: (payload: any, options?: any) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Controller for decrypt signing requests requiring user approval.
|
||||
*/
|
||||
export default class DecryptMessageController extends BaseControllerV2<
|
||||
typeof controllerName,
|
||||
DecryptMessageControllerState,
|
||||
DecryptMessageControllerMessenger
|
||||
> {
|
||||
hub: EventEmitter;
|
||||
|
||||
private _getState: () => any;
|
||||
|
||||
private _keyringController: KeyringController;
|
||||
|
||||
private _metricsEvent: (payload: any, options?: any) => void;
|
||||
|
||||
private _decryptMessageManager: DecryptMessageManager;
|
||||
|
||||
/**
|
||||
* Construct a DecryptMessage controller.
|
||||
*
|
||||
* @param options - The controller options.
|
||||
* @param options.getState - Callback to retrieve all user state.
|
||||
* @param options.keyringController - An instance of a keyring controller used to decrypt message
|
||||
* @param options.messenger - A reference to the messaging system.
|
||||
* @param options.metricsEvent - A function for emitting a metric event.
|
||||
*/
|
||||
constructor({
|
||||
getState,
|
||||
keyringController,
|
||||
metricsEvent,
|
||||
messenger,
|
||||
}: DecryptMessageControllerOptions) {
|
||||
super({
|
||||
metadata: stateMetadata,
|
||||
messenger,
|
||||
name: controllerName,
|
||||
state: getDefaultState(),
|
||||
});
|
||||
this._getState = getState;
|
||||
this._keyringController = keyringController;
|
||||
this._metricsEvent = metricsEvent;
|
||||
|
||||
this.hub = new EventEmitter();
|
||||
|
||||
this._decryptMessageManager = new DecryptMessageManager(
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
['decrypted'],
|
||||
);
|
||||
|
||||
this._decryptMessageManager.hub.on('updateBadge', () => {
|
||||
this.hub.emit('updateBadge');
|
||||
});
|
||||
|
||||
this._decryptMessageManager.hub.on(
|
||||
'unapprovedMessage',
|
||||
(messageParams: AbstractMessageParamsMetamask) => {
|
||||
this._requestApproval(messageParams);
|
||||
},
|
||||
);
|
||||
|
||||
this._subscribeToMessageState(
|
||||
this._decryptMessageManager,
|
||||
(state, newMessages, messageCount) => {
|
||||
state.unapprovedDecryptMsgs = newMessages;
|
||||
state.unapprovedDecryptMsgCount = messageCount;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the number of 'unapproved' Messages in the DecryptMessageManager.
|
||||
*
|
||||
* @returns The number of 'unapproved' Messages in the DecryptMessageManager.
|
||||
*/
|
||||
get unapprovedDecryptMsgCount(): number {
|
||||
return this._decryptMessageManager.getUnapprovedMessagesCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the controller state to the initial state.
|
||||
*/
|
||||
resetState() {
|
||||
this.update(() => getDefaultState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all unapproved messages from memory.
|
||||
*/
|
||||
clearUnapproved() {
|
||||
this._decryptMessageManager.update({
|
||||
unapprovedMessages: {},
|
||||
unapprovedMessagesCount: 0,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a dapp uses the eth_decrypt method
|
||||
*
|
||||
* @param messageParams - The params passed to eth_decrypt.
|
||||
* @param req - The original request, containing the origin.
|
||||
* @returns Promise resolving to the raw data of the signature request.
|
||||
*/
|
||||
async newRequestDecryptMessage(
|
||||
messageParams: DecryptMessageParams,
|
||||
req: OriginalRequest,
|
||||
): Promise<string> {
|
||||
return this._decryptMessageManager.addUnapprovedMessageAsync(
|
||||
messageParams,
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signifies a user's approval to decrypt a message in queue.
|
||||
* Triggers decrypt, and the callback function from newUnsignedDecryptMessage.
|
||||
*
|
||||
* @param messageParams - The params of the message to decrypt & return to the Dapp.
|
||||
* @returns A full state update.
|
||||
*/
|
||||
async decryptMessage(messageParams: DecryptMessageParamsMetamask) {
|
||||
const messageId = messageParams.metamaskId as string;
|
||||
try {
|
||||
const cleanMessageParams =
|
||||
await this._decryptMessageManager.approveMessage(messageParams);
|
||||
|
||||
cleanMessageParams.data = this._parseMessageData(cleanMessageParams.data);
|
||||
const rawMessage = await this._keyringController.decryptMessage(
|
||||
cleanMessageParams,
|
||||
);
|
||||
|
||||
this._decryptMessageManager.setMessageStatusAndResult(
|
||||
messageId,
|
||||
rawMessage,
|
||||
'decrypted',
|
||||
);
|
||||
this._acceptApproval(messageId);
|
||||
} catch (error) {
|
||||
log.info('MetaMaskController - eth_decrypt failed.', error);
|
||||
this._cancelAbstractMessage(this._decryptMessageManager, messageId);
|
||||
throw error;
|
||||
}
|
||||
return this._getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only decrypt message and don't touch transaction state
|
||||
*
|
||||
* @param messageParams - The params of the message to decrypt.
|
||||
* @returns A full state update.
|
||||
*/
|
||||
async decryptMessageInline(messageParams: DecryptMessageParamsMetamask) {
|
||||
const messageId = messageParams.metamaskId as string;
|
||||
messageParams.data = this._parseMessageData(messageParams.data);
|
||||
const rawMessage = await this._keyringController.decryptMessage(
|
||||
messageParams,
|
||||
);
|
||||
|
||||
this._decryptMessageManager.setResult(messageId, rawMessage);
|
||||
|
||||
return this._getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to cancel a eth_decrypt type message.
|
||||
*
|
||||
* @param messageId - The ID of the message to cancel.
|
||||
* @returns A full state update.
|
||||
*/
|
||||
cancelDecryptMessage(messageId: string) {
|
||||
this._decryptMessageManager.rejectMessage(messageId);
|
||||
this._rejectApproval(messageId);
|
||||
return this._getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject all unapproved messages of any type.
|
||||
*
|
||||
* @param reason - A message to indicate why.
|
||||
*/
|
||||
rejectUnapproved(reason?: string) {
|
||||
Object.keys(this._decryptMessageManager.getUnapprovedMessages()).forEach(
|
||||
(messageId) => {
|
||||
this._cancelAbstractMessage(
|
||||
this._decryptMessageManager,
|
||||
messageId,
|
||||
reason,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private _acceptApproval(messageId: string) {
|
||||
this.messagingSystem.call('ApprovalController:acceptRequest', messageId);
|
||||
}
|
||||
|
||||
private _cancelAbstractMessage(
|
||||
messageManager: AbstractMessageManager<
|
||||
AbstractMessage,
|
||||
AbstractMessageParams,
|
||||
AbstractMessageParamsMetamask
|
||||
>,
|
||||
messageId: string,
|
||||
reason?: string,
|
||||
) {
|
||||
if (reason) {
|
||||
this._metricsEvent({
|
||||
event: reason,
|
||||
category: MetaMetricsEventCategory.Messages,
|
||||
properties: {
|
||||
action: 'Decrypt Message Request',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
messageManager.rejectMessage(messageId);
|
||||
this._rejectApproval(messageId);
|
||||
|
||||
return this._getState();
|
||||
}
|
||||
|
||||
private _subscribeToMessageState(
|
||||
messageManager: AbstractMessageManager<
|
||||
AbstractMessage,
|
||||
AbstractMessageParams,
|
||||
AbstractMessageParamsMetamask
|
||||
>,
|
||||
updateState: (
|
||||
state: DecryptMessageControllerState,
|
||||
newMessages: Record<string, StateMessage>,
|
||||
messageCount: number,
|
||||
) => void,
|
||||
) {
|
||||
messageManager.subscribe((state: MessageManagerState<AbstractMessage>) => {
|
||||
const newMessages = this._migrateMessages(
|
||||
state.unapprovedMessages as any,
|
||||
);
|
||||
this.update((draftState) => {
|
||||
updateState(draftState, newMessages, state.unapprovedMessagesCount);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _migrateMessages(
|
||||
coreMessages: Record<string, CoreMessage>,
|
||||
): Record<string, StateMessage> {
|
||||
const stateMessages: Record<string, StateMessage> = {};
|
||||
|
||||
for (const messageId of Object.keys(coreMessages)) {
|
||||
const coreMessage = coreMessages[messageId];
|
||||
const stateMessage = this._migrateMessage(coreMessage);
|
||||
stateMessages[messageId] = stateMessage;
|
||||
}
|
||||
|
||||
return stateMessages;
|
||||
}
|
||||
|
||||
private _migrateMessage(coreMessage: CoreMessage): StateMessage {
|
||||
const { messageParams, ...coreMessageData } = coreMessage;
|
||||
|
||||
const stateMessage = {
|
||||
...coreMessageData,
|
||||
rawSig: coreMessage.rawSig as string,
|
||||
msgParams: messageParams,
|
||||
origin: messageParams.origin,
|
||||
};
|
||||
|
||||
return stateMessage;
|
||||
}
|
||||
|
||||
private _requestApproval(messageParams: AbstractMessageParamsMetamask) {
|
||||
const id = messageParams.metamaskId as string;
|
||||
const origin = messageParams.origin || ORIGIN_METAMASK;
|
||||
try {
|
||||
this.messagingSystem.call(
|
||||
'ApprovalController:addRequest',
|
||||
{
|
||||
id,
|
||||
origin,
|
||||
type: ApprovalType.EthDecrypt,
|
||||
},
|
||||
true,
|
||||
);
|
||||
} catch (error) {
|
||||
log.info('Error adding request to approval controller', error);
|
||||
}
|
||||
}
|
||||
|
||||
private _parseMessageData(data: string) {
|
||||
const stripped = stripHexPrefix(data);
|
||||
const buff = Buffer.from(stripped, 'hex');
|
||||
return JSON.parse(buff.toString('utf8'));
|
||||
}
|
||||
|
||||
private _rejectApproval(messageId: string) {
|
||||
try {
|
||||
this.messagingSystem.call(
|
||||
'ApprovalController:rejectRequest',
|
||||
messageId,
|
||||
'Cancel',
|
||||
);
|
||||
} catch (error) {
|
||||
log.info('Error rejecting request to approval controller', error);
|
||||
}
|
||||
}
|
||||
}
|
@ -352,6 +352,15 @@ describe('EncryptionPublicKeyController', () => {
|
||||
'Cancel',
|
||||
);
|
||||
});
|
||||
|
||||
it('returns current state', async () => {
|
||||
getStateMock.mockReturnValueOnce(stateMock);
|
||||
expect(
|
||||
await encryptionPublicKeyController.cancelEncryptionPublicKey(
|
||||
messageIdMock,
|
||||
),
|
||||
).toEqual(stateMock);
|
||||
});
|
||||
});
|
||||
|
||||
describe('message manager events', () => {
|
||||
|
@ -275,7 +275,7 @@ export default class EncryptionPublicKeyController extends BaseControllerV2<
|
||||
* @param msgId - The id of the message to cancel.
|
||||
*/
|
||||
cancelEncryptionPublicKey(msgId: string) {
|
||||
this._cancelAbstractMessage(this._encryptionPublicKeyManager, msgId);
|
||||
return this._cancelAbstractMessage(this._encryptionPublicKeyManager, msgId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -342,36 +342,31 @@ export default class EncryptionPublicKeyController extends BaseControllerV2<
|
||||
messageCount: number,
|
||||
) => void,
|
||||
) {
|
||||
messageManager.subscribe(
|
||||
async (state: MessageManagerState<AbstractMessage>) => {
|
||||
const newMessages = await this._migrateMessages(
|
||||
state.unapprovedMessages as any,
|
||||
);
|
||||
this.update((draftState) => {
|
||||
updateState(draftState, newMessages, state.unapprovedMessagesCount);
|
||||
});
|
||||
},
|
||||
);
|
||||
messageManager.subscribe((state: MessageManagerState<AbstractMessage>) => {
|
||||
const newMessages = this._migrateMessages(
|
||||
state.unapprovedMessages as any,
|
||||
);
|
||||
this.update((draftState) => {
|
||||
updateState(draftState, newMessages, state.unapprovedMessagesCount);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async _migrateMessages(
|
||||
private _migrateMessages(
|
||||
coreMessages: Record<string, CoreMessage>,
|
||||
): Promise<Record<string, StateMessage>> {
|
||||
): Record<string, StateMessage> {
|
||||
const stateMessages: Record<string, StateMessage> = {};
|
||||
|
||||
for (const messageId of Object.keys(coreMessages)) {
|
||||
const coreMessage = coreMessages[messageId];
|
||||
const stateMessage = await this._migrateMessage(coreMessage);
|
||||
|
||||
const stateMessage = this._migrateMessage(coreMessage);
|
||||
stateMessages[messageId] = stateMessage;
|
||||
}
|
||||
|
||||
return stateMessages;
|
||||
}
|
||||
|
||||
private async _migrateMessage(
|
||||
coreMessage: CoreMessage,
|
||||
): Promise<StateMessage> {
|
||||
private _migrateMessage(coreMessage: CoreMessage): StateMessage {
|
||||
const { messageParams, ...coreMessageData } = coreMessage;
|
||||
|
||||
// Core message managers use messageParams but frontend uses msgParams with lots of references
|
||||
|
@ -724,7 +724,7 @@ export default class MetaMetricsController {
|
||||
[MetaMetricsUserTrait.Theme]: metamaskState.theme || 'default',
|
||||
[MetaMetricsUserTrait.TokenDetectionEnabled]:
|
||||
metamaskState.useTokenDetection,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
[MetaMetricsUserTrait.DesktopEnabled]:
|
||||
metamaskState.desktopEnabled || false,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
@ -327,7 +327,7 @@ function buildDefaultProviderConfigState(): ProviderConfiguration {
|
||||
};
|
||||
} else if (
|
||||
process.env.METAMASK_DEBUG ||
|
||||
process.env.METAMASK_ENV === 'test'
|
||||
process.env.METAMASK_ENVIRONMENT === 'test'
|
||||
) {
|
||||
return {
|
||||
type: NETWORK_TYPES.GOERLI,
|
||||
|
@ -4,6 +4,6 @@ export * from './enums';
|
||||
export * from './permission-log';
|
||||
export * from './specifications';
|
||||
export * from './selectors';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
export * from './flask/snap-permissions';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
export * from './snaps/snap-permissions';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
@ -2,7 +2,7 @@ import {
|
||||
constructPermission,
|
||||
PermissionType,
|
||||
} from '@metamask/permission-controller';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
import { endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications } from '@metamask/snaps-controllers';
|
||||
import { caveatSpecifications as snapsCaveatsSpecifications } from '@metamask/rpc-methods';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
@ -71,7 +71,7 @@ export const getCaveatSpecifications = ({ getIdentities }) => {
|
||||
validateCaveatAccounts(caveat.value, getIdentities),
|
||||
},
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
...snapsCaveatsSpecifications,
|
||||
...snapsEndowmentCaveatSpecifications,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
@ -1,351 +0,0 @@
|
||||
import EventEmitter from 'events';
|
||||
import { ObservableStore } from '@metamask/obs-store';
|
||||
import { bufferToHex } from 'ethereumjs-util';
|
||||
import { ethErrors } from 'eth-rpc-errors';
|
||||
import log from 'loglevel';
|
||||
import { MESSAGE_TYPE } from '../../../shared/constants/app';
|
||||
import { MetaMetricsEventCategory } from '../../../shared/constants/metametrics';
|
||||
import { METAMASK_CONTROLLER_EVENTS } from '../metamask-controller';
|
||||
import createId from '../../../shared/modules/random-id';
|
||||
import { stripHexPrefix } from '../../../shared/modules/hexstring-utils';
|
||||
import { addHexPrefix } from './util';
|
||||
|
||||
const hexRe = /^[0-9A-Fa-f]+$/gu;
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_decrypt' type decryption request. These are created when a
|
||||
* decryption for an eth_decrypt call is requested.
|
||||
*
|
||||
* @typedef {object} DecryptMessage
|
||||
* @property {number} id An id to track and identify the message object
|
||||
* @property {object} msgParams The parameters to pass to the decryptMessage method once the decryption request is
|
||||
* approved.
|
||||
* @property {object} msgParams.metamaskId Added to msgParams for tracking and identification within MetaMask.
|
||||
* @property {string} msgParams.data A hex string conversion of the raw buffer data of the decryption request
|
||||
* @property {number} time The epoch time at which the this message was created
|
||||
* @property {string} status Indicates whether the decryption request is 'unapproved', 'approved', 'decrypted' or 'rejected'
|
||||
* @property {string} type The json-prc decryption method for which a decryption request has been made. A 'Message' will
|
||||
* always have a 'eth_decrypt' type.
|
||||
*/
|
||||
|
||||
export default class DecryptMessageManager extends EventEmitter {
|
||||
/**
|
||||
* Controller in charge of managing - storing, adding, removing, updating - DecryptMessage.
|
||||
*
|
||||
* @param {object} opts - Controller options
|
||||
* @param {Function} opts.metricEvent - A function for emitting a metric event.
|
||||
*/
|
||||
constructor(opts) {
|
||||
super();
|
||||
this.memStore = new ObservableStore({
|
||||
unapprovedDecryptMsgs: {},
|
||||
unapprovedDecryptMsgCount: 0,
|
||||
});
|
||||
|
||||
this.resetState = () => {
|
||||
this.memStore.updateState({
|
||||
unapprovedDecryptMsgs: {},
|
||||
unapprovedDecryptMsgCount: 0,
|
||||
});
|
||||
};
|
||||
|
||||
this.messages = [];
|
||||
this.metricsEvent = opts.metricsEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the number of 'unapproved' DecryptMessages in this.messages
|
||||
*
|
||||
* @returns {number} The number of 'unapproved' DecryptMessages in this.messages
|
||||
*/
|
||||
get unapprovedDecryptMsgCount() {
|
||||
return Object.keys(this.getUnapprovedMsgs()).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* A getter for the 'unapproved' DecryptMessages in this.messages
|
||||
*
|
||||
* @returns {object} An index of DecryptMessage ids to DecryptMessages, for all 'unapproved' DecryptMessages in
|
||||
* this.messages
|
||||
*/
|
||||
getUnapprovedMsgs() {
|
||||
return this.messages
|
||||
.filter((msg) => msg.status === 'unapproved')
|
||||
.reduce((result, msg) => {
|
||||
result[msg.id] = msg;
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DecryptMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
||||
* the new DecryptMessage to this.messages, and to save the unapproved DecryptMessages from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {object} msgParams - The params for the eth_decrypt call to be made after the message is approved.
|
||||
* @param {object} [req] - The original request object possibly containing the origin
|
||||
* @returns {Promise<Buffer>} The raw decrypted message contents
|
||||
*/
|
||||
addUnapprovedMessageAsync(msgParams, req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!msgParams.from) {
|
||||
reject(new Error('MetaMask Decryption: from field is required.'));
|
||||
return;
|
||||
}
|
||||
const msgId = this.addUnapprovedMessage(msgParams, req);
|
||||
this.once(`${msgId}:finished`, (data) => {
|
||||
switch (data.status) {
|
||||
case 'decrypted':
|
||||
resolve(data.rawData);
|
||||
return;
|
||||
case 'rejected':
|
||||
reject(
|
||||
ethErrors.provider.userRejectedRequest(
|
||||
'MetaMask Decryption: User denied message decryption.',
|
||||
),
|
||||
);
|
||||
return;
|
||||
case 'errored':
|
||||
reject(new Error('This message cannot be decrypted'));
|
||||
return;
|
||||
default:
|
||||
reject(
|
||||
new Error(
|
||||
`MetaMask Decryption: Unknown problem: ${JSON.stringify(
|
||||
msgParams,
|
||||
)}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DecryptMessage with an 'unapproved' status using the passed msgParams. this.addMsg is called to add
|
||||
* the new DecryptMessage to this.messages, and to save the unapproved DecryptMessages from that list to
|
||||
* this.memStore.
|
||||
*
|
||||
* @param {object} msgParams - The params for the eth_decryptMsg call to be made after the message is approved.
|
||||
* @param {object} [req] - The original request object possibly containing the origin
|
||||
* @returns {number} The id of the newly created DecryptMessage.
|
||||
*/
|
||||
addUnapprovedMessage(msgParams, req) {
|
||||
log.debug(
|
||||
`DecryptMessageManager addUnapprovedMessage: ${JSON.stringify(
|
||||
msgParams,
|
||||
)}`,
|
||||
);
|
||||
// add origin from request
|
||||
if (req) {
|
||||
msgParams.origin = req.origin;
|
||||
}
|
||||
msgParams.data = this.normalizeMsgData(msgParams.data);
|
||||
// create txData obj with parameters and meta data
|
||||
const time = new Date().getTime();
|
||||
const msgId = createId();
|
||||
const msgData = {
|
||||
id: msgId,
|
||||
msgParams,
|
||||
time,
|
||||
status: 'unapproved',
|
||||
type: MESSAGE_TYPE.ETH_DECRYPT,
|
||||
};
|
||||
this.addMsg(msgData);
|
||||
|
||||
// signal update
|
||||
this.emit('update');
|
||||
return msgId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a passed DecryptMessage to this.messages, and calls this._saveMsgList() to save the unapproved DecryptMessages from that
|
||||
* list to this.memStore.
|
||||
*
|
||||
* @param {Message} msg - The DecryptMessage to add to this.messages
|
||||
*/
|
||||
addMsg(msg) {
|
||||
this.messages.push(msg);
|
||||
this._saveMsgList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specified DecryptMessage.
|
||||
*
|
||||
* @param {number} msgId - The id of the DecryptMessage to get
|
||||
* @returns {DecryptMessage|undefined} The DecryptMessage with the id that matches the passed msgId, or undefined
|
||||
* if no DecryptMessage has that id.
|
||||
*/
|
||||
getMsg(msgId) {
|
||||
return this.messages.find((msg) => msg.id === msgId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approves a DecryptMessage. Sets the message status via a call to this.setMsgStatusApproved, and returns a promise
|
||||
* with the message params modified for proper decryption.
|
||||
*
|
||||
* @param {object} msgParams - The msgParams to be used when eth_decryptMsg is called, plus data added by MetaMask.
|
||||
* @param {object} msgParams.metamaskId - Added to msgParams for tracking and identification within MetaMask.
|
||||
* @returns {Promise<object>} Promises the msgParams object with metamaskId removed.
|
||||
*/
|
||||
approveMessage(msgParams) {
|
||||
this.setMsgStatusApproved(msgParams.metamaskId);
|
||||
return this.prepMsgForDecryption(msgParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage status to 'approved' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId - The id of the DecryptMessage to approve.
|
||||
*/
|
||||
setMsgStatusApproved(msgId) {
|
||||
this._setMsgStatus(msgId, 'approved');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage status to 'decrypted' via a call to this._setMsgStatus and updates that DecryptMessage in
|
||||
* this.messages by adding the raw decryption data of the decryption request to the DecryptMessage
|
||||
*
|
||||
* @param {number} msgId - The id of the DecryptMessage to decrypt.
|
||||
* @param {buffer} rawData - The raw data of the message request
|
||||
*/
|
||||
setMsgStatusDecrypted(msgId, rawData) {
|
||||
const msg = this.getMsg(msgId);
|
||||
msg.rawData = rawData;
|
||||
this._updateMsg(msg);
|
||||
this._setMsgStatus(msgId, 'decrypted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the metamaskId property from passed msgParams and returns a promise which resolves the updated msgParams
|
||||
*
|
||||
* @param {object} msgParams - The msgParams to modify
|
||||
* @returns {Promise<object>} Promises the msgParams with the metamaskId property removed
|
||||
*/
|
||||
async prepMsgForDecryption(msgParams) {
|
||||
delete msgParams.metamaskId;
|
||||
return msgParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage status to 'rejected' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId - The id of the DecryptMessage to reject.
|
||||
* @param reason
|
||||
*/
|
||||
rejectMsg(msgId, reason = undefined) {
|
||||
if (reason) {
|
||||
this.metricsEvent({
|
||||
event: reason,
|
||||
category: MetaMetricsEventCategory.Messages,
|
||||
properties: {
|
||||
action: 'Decrypt Message Request',
|
||||
},
|
||||
});
|
||||
}
|
||||
this._setMsgStatus(msgId, 'rejected');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus.
|
||||
*
|
||||
* @param {number} msgId - The id of the TypedMessage to error
|
||||
* @param error
|
||||
*/
|
||||
errorMessage(msgId, error) {
|
||||
const msg = this.getMsg(msgId);
|
||||
msg.error = error;
|
||||
this._updateMsg(msg);
|
||||
this._setMsgStatus(msgId, 'errored');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all unapproved messages from memory.
|
||||
*/
|
||||
clearUnapproved() {
|
||||
this.messages = this.messages.filter((msg) => msg.status !== 'unapproved');
|
||||
this._saveMsgList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the status of a DecryptMessage in this.messages via a call to this._updateMsg
|
||||
*
|
||||
* @private
|
||||
* @param {number} msgId - The id of the DecryptMessage to update.
|
||||
* @param {string} status - The new status of the DecryptMessage.
|
||||
* @throws A 'DecryptMessageManager - DecryptMessage not found for id: "${msgId}".' if there is no DecryptMessage
|
||||
* in this.messages with an id equal to the passed msgId
|
||||
* @fires An event with a name equal to `${msgId}:${status}`. The DecryptMessage is also fired.
|
||||
* @fires If status is 'rejected' or 'decrypted', an event with a name equal to `${msgId}:finished` is fired along
|
||||
* with the DecryptMessage
|
||||
*/
|
||||
_setMsgStatus(msgId, status) {
|
||||
const msg = this.getMsg(msgId);
|
||||
if (!msg) {
|
||||
throw new Error(
|
||||
`DecryptMessageManager - Message not found for id: "${msgId}".`,
|
||||
);
|
||||
}
|
||||
msg.status = status;
|
||||
this._updateMsg(msg);
|
||||
this.emit(`${msgId}:${status}`, msg);
|
||||
if (
|
||||
status === 'rejected' ||
|
||||
status === 'decrypted' ||
|
||||
status === 'errored'
|
||||
) {
|
||||
this.emit(`${msgId}:finished`, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a DecryptMessage in this.messages to the passed DecryptMessage if the ids are equal. Then saves the
|
||||
* unapprovedDecryptMsgs index to storage via this._saveMsgList
|
||||
*
|
||||
* @private
|
||||
* @param {DecryptMessage} msg - A DecryptMessage that will replace an existing DecryptMessage (with the same
|
||||
* id) in this.messages
|
||||
*/
|
||||
_updateMsg(msg) {
|
||||
const index = this.messages.findIndex((message) => message.id === msg.id);
|
||||
if (index !== -1) {
|
||||
this.messages[index] = msg;
|
||||
}
|
||||
this._saveMsgList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the unapproved DecryptMessages, and their count, to this.memStore
|
||||
*
|
||||
* @private
|
||||
* @fires 'updateBadge'
|
||||
*/
|
||||
_saveMsgList() {
|
||||
const unapprovedDecryptMsgs = this.getUnapprovedMsgs();
|
||||
const unapprovedDecryptMsgCount = Object.keys(unapprovedDecryptMsgs).length;
|
||||
this.memStore.updateState({
|
||||
unapprovedDecryptMsgs,
|
||||
unapprovedDecryptMsgCount,
|
||||
});
|
||||
this.emit(METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function that converts raw buffer data to a hex, or just returns the data if it is already formatted as a hex.
|
||||
*
|
||||
* @param {any} data - The buffer data to convert to a hex
|
||||
* @returns {string} A hex string conversion of the buffer data
|
||||
*/
|
||||
normalizeMsgData(data) {
|
||||
try {
|
||||
const stripped = stripHexPrefix(data);
|
||||
if (stripped.match(hexRe)) {
|
||||
return addHexPrefix(stripped);
|
||||
}
|
||||
} catch (e) {
|
||||
log.debug(`Message was not hex encoded, interpreting as utf8.`);
|
||||
}
|
||||
|
||||
return bufferToHex(Buffer.from(data, 'utf8'));
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
import { handlers as permittedSnapMethods } from '@metamask/rpc-methods/dist/permitted';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import { permissionRpcMethods } from '@metamask/permission-controller';
|
||||
@ -72,7 +72,7 @@ export function createMethodMiddleware(hooks) {
|
||||
};
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
const snapHandlerMap = permittedSnapMethods.reduce((map, handler) => {
|
||||
for (const methodName of handler.methodNames) {
|
||||
map.set(methodName, handler);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { Dedupe, ExtraErrorData } from '@sentry/integrations';
|
||||
|
||||
import { BuildType } from '../../../shared/constants/app';
|
||||
import { FilterEvents } from './sentry-filter-events';
|
||||
import extractEthjsErrorMessage from './extractEthjsErrorMessage';
|
||||
|
||||
@ -90,7 +89,7 @@ export default function setupSentry({ release, getState }) {
|
||||
}
|
||||
|
||||
const environment =
|
||||
METAMASK_BUILD_TYPE === BuildType.main
|
||||
METAMASK_BUILD_TYPE === 'main'
|
||||
? METAMASK_ENVIRONMENT
|
||||
: `${METAMASK_ENVIRONMENT}-${METAMASK_BUILD_TYPE}`;
|
||||
|
||||
|
@ -48,12 +48,12 @@ import {
|
||||
SubjectMetadataController,
|
||||
SubjectType,
|
||||
} from '@metamask/subject-metadata-controller';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
import { RateLimitController } from '@metamask/rate-limit-controller';
|
||||
import { NotificationController } from '@metamask/notification-controller';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import SmartTransactionsController from '@metamask/smart-transactions-controller';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
import {
|
||||
CronjobController,
|
||||
JsonSnapsRegistry,
|
||||
@ -83,19 +83,18 @@ import { KeyringType } from '../../shared/constants/keyring';
|
||||
import {
|
||||
CaveatTypes,
|
||||
RestrictedMethods,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
EndowmentPermissions,
|
||||
ExcludedSnapPermissions,
|
||||
ExcludedSnapEndowments,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../shared/constants/permissions';
|
||||
import { UI_NOTIFICATIONS } from '../../shared/notifications';
|
||||
import { stripHexPrefix } from '../../shared/modules/hexstring-utils';
|
||||
import { MILLISECOND, SECOND } from '../../shared/constants/time';
|
||||
import {
|
||||
ORIGIN_METAMASK,
|
||||
MESSAGE_TYPE,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
SNAP_DIALOG_TYPES,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
||||
@ -115,8 +114,7 @@ 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(flask)
|
||||
import { isMain, isFlask } from '../../shared/constants/environment';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
// eslint-disable-next-line import/order
|
||||
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
@ -131,7 +129,7 @@ import createDupeReqFilterMiddleware from './lib/createDupeReqFilterMiddleware';
|
||||
import createLoggerMiddleware from './lib/createLoggerMiddleware';
|
||||
import {
|
||||
createMethodMiddleware,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
createSnapMethodMiddleware,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from './lib/rpc-method-middleware';
|
||||
@ -151,7 +149,7 @@ import AlertController from './controllers/alert';
|
||||
import OnboardingController from './controllers/onboarding';
|
||||
import BackupController from './controllers/backup';
|
||||
import IncomingTransactionsController from './controllers/incoming-transactions';
|
||||
import DecryptMessageManager from './lib/decrypt-message-manager';
|
||||
import DecryptMessageController from './controllers/decrypt-message';
|
||||
import TransactionController from './controllers/transactions';
|
||||
import DetectTokensController from './controllers/detect-tokens';
|
||||
import SwapsController from './controllers/swaps';
|
||||
@ -175,7 +173,7 @@ import {
|
||||
NOTIFICATION_NAMES,
|
||||
PermissionLogController,
|
||||
unrestrictedMethods,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
buildSnapEndowmentSpecifications,
|
||||
buildSnapRestrictedMethodSpecifications,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
@ -786,7 +784,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
);
|
||||
},
|
||||
}),
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
...this.getSnapPermissionSpecifications(),
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
},
|
||||
@ -807,7 +805,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
subjectCacheLimit: 100,
|
||||
});
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
const snapExecutionServiceArgs = {
|
||||
iframeUrl: new URL('https://execution.metamask.io/0.15.1/index.html'),
|
||||
messenger: this.controllerMessenger.getRestricted({
|
||||
@ -852,6 +850,9 @@ export default class MetamaskController extends EventEmitter {
|
||||
],
|
||||
});
|
||||
|
||||
const allowLocalSnaps = process.env.ALLOW_LOCAL_SNAPS;
|
||||
const requireAllowlist = process.env.REQUIRE_SNAPS_ALLOWLIST;
|
||||
|
||||
this.snapController = new SnapController({
|
||||
environmentEndowmentPermissions: Object.values(EndowmentPermissions),
|
||||
excludedPermissions: {
|
||||
@ -863,8 +864,8 @@ export default class MetamaskController extends EventEmitter {
|
||||
messenger: snapControllerMessenger,
|
||||
featureFlags: {
|
||||
dappsCanUpdateSnaps: true,
|
||||
allowLocalSnaps: isFlask,
|
||||
requireAllowlist: isMain,
|
||||
allowLocalSnaps,
|
||||
requireAllowlist,
|
||||
},
|
||||
});
|
||||
|
||||
@ -933,8 +934,8 @@ export default class MetamaskController extends EventEmitter {
|
||||
this.snapsRegistry = new JsonSnapsRegistry({
|
||||
state: initState.SnapsRegistry,
|
||||
messenger: snapsRegistryMessenger,
|
||||
refetchOnAllowlistMiss: isMain,
|
||||
failOnUnavailableRegistry: isMain,
|
||||
refetchOnAllowlistMiss: requireAllowlist,
|
||||
failOnUnavailableRegistry: requireAllowlist,
|
||||
url: {
|
||||
registry: 'https://acl.execution.metamask.io/latest/registry.json',
|
||||
signature: 'https://acl.execution.metamask.io/latest/signature.json',
|
||||
@ -943,6 +944,9 @@ export default class MetamaskController extends EventEmitter {
|
||||
'0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a048b2fd6a86fa0a6',
|
||||
});
|
||||
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
this.desktopController = new DesktopController({
|
||||
initState: initState.DesktopController,
|
||||
});
|
||||
@ -1151,7 +1155,17 @@ export default class MetamaskController extends EventEmitter {
|
||||
);
|
||||
|
||||
this.networkController.lookupNetwork();
|
||||
this.decryptMessageManager = new DecryptMessageManager({
|
||||
this.decryptMessageController = new DecryptMessageController({
|
||||
getState: this.getState.bind(this),
|
||||
keyringController: this.keyringController,
|
||||
messenger: this.controllerMessenger.getRestricted({
|
||||
name: 'DecryptMessageController',
|
||||
allowedActions: [
|
||||
`${this.approvalController.name}:addRequest`,
|
||||
`${this.approvalController.name}:acceptRequest`,
|
||||
`${this.approvalController.name}:rejectRequest`,
|
||||
],
|
||||
}),
|
||||
metricsEvent: this.metaMetricsController.trackEvent.bind(
|
||||
this.metaMetricsController,
|
||||
),
|
||||
@ -1251,7 +1265,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
() => {
|
||||
this.txController.txStateManager.clearUnapprovedTxs();
|
||||
this.encryptionPublicKeyController.clearUnapproved();
|
||||
this.decryptMessageManager.clearUnapproved();
|
||||
this.decryptMessageController.clearUnapproved();
|
||||
this.signController.clearUnapproved();
|
||||
},
|
||||
);
|
||||
@ -1316,11 +1330,14 @@ export default class MetamaskController extends EventEmitter {
|
||||
this.signController.newUnsignedPersonalMessage.bind(
|
||||
this.signController,
|
||||
),
|
||||
processDecryptMessage: this.newRequestDecryptMessage.bind(this),
|
||||
processEncryptionPublicKey:
|
||||
this.encryptionPublicKeyController.newRequestEncryptionPublicKey.bind(
|
||||
this.encryptionPublicKeyController,
|
||||
),
|
||||
processDecryptMessage:
|
||||
this.decryptMessageController.newRequestDecryptMessage.bind(
|
||||
this.decryptMessageController,
|
||||
),
|
||||
getPendingNonce: this.getPendingNonce.bind(this),
|
||||
getPendingTransactionByHash: (hash) =>
|
||||
this.txController.getTransactions({
|
||||
@ -1342,7 +1359,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
AccountTracker: this.accountTracker.store,
|
||||
TxController: this.txController.memStore,
|
||||
TokenRatesController: this.tokenRatesController,
|
||||
DecryptMessageManager: this.decryptMessageManager.memStore,
|
||||
DecryptMessageController: this.decryptMessageController,
|
||||
EncryptionPublicKeyController: this.encryptionPublicKeyController,
|
||||
SignController: this.signController,
|
||||
SwapsController: this.swapsController.store,
|
||||
@ -1374,11 +1391,13 @@ export default class MetamaskController extends EventEmitter {
|
||||
SmartTransactionsController: this.smartTransactionsController,
|
||||
NftController: this.nftController,
|
||||
PhishingController: this.phishingController,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
SnapController: this.snapController,
|
||||
CronjobController: this.cronjobController,
|
||||
SnapsRegistry: this.snapsRegistry,
|
||||
NotificationController: this.notificationController,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
DesktopController: this.desktopController.store,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
...resetOnRestartStore,
|
||||
@ -1408,11 +1427,13 @@ export default class MetamaskController extends EventEmitter {
|
||||
TokensController: this.tokensController,
|
||||
SmartTransactionsController: this.smartTransactionsController,
|
||||
NftController: this.nftController,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
SnapController: this.snapController,
|
||||
CronjobController: this.cronjobController,
|
||||
SnapsRegistry: this.snapsRegistry,
|
||||
NotificationController: this.notificationController,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
DesktopController: this.desktopController.store,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
...resetOnRestartStore,
|
||||
@ -1424,7 +1445,9 @@ export default class MetamaskController extends EventEmitter {
|
||||
const resetMethods = [
|
||||
this.accountTracker.resetState,
|
||||
this.txController.resetState,
|
||||
this.decryptMessageManager.resetState,
|
||||
this.decryptMessageController.resetState.bind(
|
||||
this.decryptMessageController,
|
||||
),
|
||||
this.encryptionPublicKeyController.resetState.bind(
|
||||
this.encryptionPublicKeyController,
|
||||
),
|
||||
@ -1504,7 +1527,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
}
|
||||
|
||||
canUseHardwareWallets() {
|
||||
return !isManifestV3 || process.env.CONF?.HARDWARE_WALLETS_MV3;
|
||||
return !isManifestV3 || process.env.HARDWARE_WALLETS_MV3;
|
||||
}
|
||||
|
||||
resetStates(resetMethods) {
|
||||
@ -1517,7 +1540,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
/**
|
||||
* Constructor helper for getting Snap permission specifications.
|
||||
*/
|
||||
@ -1659,7 +1682,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
getPermittedAccountsByOrigin,
|
||||
);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
// Record Snap metadata whenever a Snap is added to state.
|
||||
this.controllerMessenger.subscribe(
|
||||
`${this.snapController.name}:snapAdded`,
|
||||
@ -1960,6 +1983,10 @@ export default class MetamaskController extends EventEmitter {
|
||||
this.networkController.upsertNetworkConfiguration.bind(
|
||||
this.networkController,
|
||||
),
|
||||
getCurrentNetworkEIP1559Compatibility:
|
||||
this.networkController.getEIP1559Compatibility.bind(
|
||||
this.networkController,
|
||||
),
|
||||
// PreferencesController
|
||||
setSelectedAddress: preferencesController.setSelectedAddress.bind(
|
||||
preferencesController,
|
||||
@ -2128,10 +2155,18 @@ export default class MetamaskController extends EventEmitter {
|
||||
this.signController,
|
||||
),
|
||||
|
||||
// decryptMessageManager
|
||||
decryptMessage: this.decryptMessage.bind(this),
|
||||
decryptMessageInline: this.decryptMessageInline.bind(this),
|
||||
cancelDecryptMessage: this.cancelDecryptMessage.bind(this),
|
||||
// decryptMessageController
|
||||
decryptMessage: this.decryptMessageController.decryptMessage.bind(
|
||||
this.decryptMessageController,
|
||||
),
|
||||
decryptMessageInline:
|
||||
this.decryptMessageController.decryptMessageInline.bind(
|
||||
this.decryptMessageController,
|
||||
),
|
||||
cancelDecryptMessage:
|
||||
this.decryptMessageController.cancelDecryptMessage.bind(
|
||||
this.decryptMessageController,
|
||||
),
|
||||
|
||||
// EncryptionPublicKeyController
|
||||
encryptionPublicKey:
|
||||
@ -2165,7 +2200,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
rejectPermissionsRequest: this.rejectPermissionsRequest,
|
||||
...getPermissionBackgroundApiMethods(permissionController),
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
// snaps
|
||||
removeSnapError: this.controllerMessenger.call.bind(
|
||||
this.controllerMessenger,
|
||||
@ -2189,6 +2224,8 @@ export default class MetamaskController extends EventEmitter {
|
||||
),
|
||||
dismissNotifications: this.dismissNotifications.bind(this),
|
||||
markNotificationsAsRead: this.markNotificationsAsRead.bind(this),
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
// Desktop
|
||||
getDesktopEnabled: this.desktopController.getDesktopEnabled.bind(
|
||||
this.desktopController,
|
||||
@ -2488,7 +2525,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
// clear permissions
|
||||
this.permissionController.clearState();
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
// Clear snap state
|
||||
this.snapController.clearState();
|
||||
// Clear notification state
|
||||
@ -2614,7 +2651,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
async _loginUser() {
|
||||
try {
|
||||
// Automatic login via config password
|
||||
const password = process.env.CONF?.PASSWORD;
|
||||
const password = process.env.PASSWORD;
|
||||
if (password && !process.env.IN_TEST) {
|
||||
await this.submitPassword(password);
|
||||
}
|
||||
@ -3147,95 +3184,6 @@ export default class MetamaskController extends EventEmitter {
|
||||
return await this.txController.newUnapprovedTransaction(txParams, req);
|
||||
}
|
||||
|
||||
// eth_decrypt methods
|
||||
|
||||
/**
|
||||
* Called when a dapp uses the eth_decrypt method.
|
||||
*
|
||||
* @param {object} msgParams - The params of the message to sign & return to the Dapp.
|
||||
* @param {object} req - (optional) the original request, containing the origin
|
||||
* Passed back to the requesting Dapp.
|
||||
*/
|
||||
async newRequestDecryptMessage(msgParams, req) {
|
||||
const promise = this.decryptMessageManager.addUnapprovedMessageAsync(
|
||||
msgParams,
|
||||
req,
|
||||
);
|
||||
this.sendUpdate();
|
||||
this.opts.showUserConfirmation();
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only decrypt message and don't touch transaction state
|
||||
*
|
||||
* @param {object} msgParams - The params of the message to decrypt.
|
||||
* @returns {Promise<object>} A full state update.
|
||||
*/
|
||||
async decryptMessageInline(msgParams) {
|
||||
log.info('MetaMaskController - decryptMessageInline');
|
||||
// decrypt the message inline
|
||||
const msgId = msgParams.metamaskId;
|
||||
const msg = this.decryptMessageManager.getMsg(msgId);
|
||||
try {
|
||||
const stripped = stripHexPrefix(msgParams.data);
|
||||
const buff = Buffer.from(stripped, 'hex');
|
||||
msgParams.data = JSON.parse(buff.toString('utf8'));
|
||||
|
||||
msg.rawData = await this.keyringController.decryptMessage(msgParams);
|
||||
} catch (e) {
|
||||
msg.error = e.message;
|
||||
}
|
||||
this.decryptMessageManager._updateMsg(msg);
|
||||
|
||||
return this.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Signifies a user's approval to decrypt a message in queue.
|
||||
* Triggers decrypt, and the callback function from newUnsignedDecryptMessage.
|
||||
*
|
||||
* @param {object} msgParams - The params of the message to decrypt & return to the Dapp.
|
||||
* @returns {Promise<object>} A full state update.
|
||||
*/
|
||||
async decryptMessage(msgParams) {
|
||||
log.info('MetaMaskController - decryptMessage');
|
||||
const msgId = msgParams.metamaskId;
|
||||
// sets the status op the message to 'approved'
|
||||
// and removes the metamaskId for decryption
|
||||
try {
|
||||
const cleanMsgParams = await this.decryptMessageManager.approveMessage(
|
||||
msgParams,
|
||||
);
|
||||
|
||||
const stripped = stripHexPrefix(cleanMsgParams.data);
|
||||
const buff = Buffer.from(stripped, 'hex');
|
||||
cleanMsgParams.data = JSON.parse(buff.toString('utf8'));
|
||||
|
||||
// decrypt the message
|
||||
const rawMess = await this.keyringController.decryptMessage(
|
||||
cleanMsgParams,
|
||||
);
|
||||
// tells the listener that the message has been decrypted and can be returned to the dapp
|
||||
this.decryptMessageManager.setMsgStatusDecrypted(msgId, rawMess);
|
||||
} catch (error) {
|
||||
log.info('MetaMaskController - eth_decrypt failed.', error);
|
||||
this.decryptMessageManager.errorMessage(msgId, error);
|
||||
}
|
||||
return this.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to cancel a eth_decrypt type message.
|
||||
*
|
||||
* @param {string} msgId - The ID of the message to cancel.
|
||||
*/
|
||||
cancelDecryptMessage(msgId) {
|
||||
const messageManager = this.decryptMessageManager;
|
||||
messageManager.rejectMsg(msgId);
|
||||
return this.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean} true if the keyring type supports EIP-1559
|
||||
*/
|
||||
@ -3543,7 +3491,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
if (subjectType === SubjectType.Internal) {
|
||||
origin = ORIGIN_METAMASK;
|
||||
}
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
else if (subjectType === SubjectType.Snap) {
|
||||
origin = sender.snapId;
|
||||
}
|
||||
@ -3591,7 +3539,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
/**
|
||||
* For snaps running in workers.
|
||||
*
|
||||
@ -3749,7 +3697,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
}),
|
||||
);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
engine.push(
|
||||
createSnapMethodMiddleware(subjectType === SubjectType.Snap, {
|
||||
getUnlockPromise: this.appStateController.getUnlockPromise.bind(
|
||||
@ -4247,7 +4195,7 @@ export default class MetamaskController extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
updateCaveat = (origin, target, caveatType, caveatValue) => {
|
||||
try {
|
||||
this.controllerMessenger.call(
|
||||
|
@ -194,7 +194,7 @@ export default class ExtensionPlatform {
|
||||
let message = `Transaction ${nonce} failed! ${
|
||||
errorMessage || txMeta.err.message
|
||||
}`;
|
||||
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
if (isNaN(nonce)) {
|
||||
message = `Transaction failed! ${errorMessage || txMeta.err.message}`;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import { checkForLastErrorAndLog } from '../../shared/modules/browser-runtime.ut
|
||||
import { SUPPORT_LINK } from '../../shared/lib/ui-utils';
|
||||
import {
|
||||
getErrorHtml,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
registerDesktopErrorActions,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../../shared/lib/error-utils';
|
||||
@ -264,7 +264,7 @@ async function start() {
|
||||
(
|
||||
err,
|
||||
store,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
backgroundConnection,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
) => {
|
||||
@ -274,7 +274,7 @@ async function start() {
|
||||
'troubleStarting',
|
||||
err,
|
||||
store,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
backgroundConnection,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
);
|
||||
@ -302,7 +302,7 @@ async function start() {
|
||||
displayCriticalError(
|
||||
'troubleStarting',
|
||||
err,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
undefined,
|
||||
backgroundConnection,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
@ -345,7 +345,7 @@ function initializeUi(activeTab, connectionStream, cb) {
|
||||
cb(
|
||||
err,
|
||||
null,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
backgroundConnection,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
);
|
||||
@ -367,7 +367,7 @@ async function displayCriticalError(
|
||||
errorKey,
|
||||
err,
|
||||
metamaskState,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
backgroundConnection,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
) {
|
||||
@ -375,14 +375,14 @@ async function displayCriticalError(
|
||||
errorKey,
|
||||
SUPPORT_LINK,
|
||||
metamaskState,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
err,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
);
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
registerDesktopErrorActions(backgroundConnection, browser);
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
|
214
builds.yml
Normal file
214
builds.yml
Normal file
@ -0,0 +1,214 @@
|
||||
# TODO(ritave): Add support for environments (<root>/development/build/constants.js:@ENVIRONMENT)
|
||||
# TODO(ritave): Add support for build targets (<root>/development/build/constants.js:@BUILD_TARGETS)
|
||||
# TODO(ritave): Warn if not all of declared variables have been defined / used
|
||||
|
||||
# The priority order of variable definitions (most important to least important):
|
||||
# <hardcoded build code>; <environmental variables>; .metamaskprodrc; .metamaskrc; builds.yml:.buildTypes.<type>.env; builds.yml:.features.<feature>.env; builds.yml:.env
|
||||
|
||||
# The build type to use when no build type provided in the cli
|
||||
default: &default main
|
||||
|
||||
# Declaration of build types
|
||||
# Each build type is composed of features, env variables and assets.
|
||||
# Also known as productFlavors in Android lingo
|
||||
buildTypes:
|
||||
main:
|
||||
features:
|
||||
- build-main
|
||||
# Additional env variables that are specific to this build
|
||||
env:
|
||||
- INFURA_PROD_PROJECT_ID
|
||||
- SEGMENT_PROD_WRITE_KEY
|
||||
- INFURA_ENV_KEY_REF: INFURA_PROD_PROJECT_ID
|
||||
- SEGMENT_WRITE_KEY_REF: SEGMENT_PROD_WRITE_KEY
|
||||
|
||||
beta:
|
||||
features:
|
||||
- build-beta
|
||||
env:
|
||||
- INFURA_BETA_PROJECT_ID
|
||||
- SEGMENT_BETA_WRITE_KEY
|
||||
- INFURA_ENV_KEY_REF: INFURA_BETA_PROJECT_ID
|
||||
- SEGMENT_WRITE_KEY_REF: SEGMENT_BETA_WRITE_KEY
|
||||
# Modifies how the version is displayed.
|
||||
# eg. instead of 10.25.0 -> 10.25.0-beta.2
|
||||
isPrerelease: true
|
||||
# Folder which contains overrides to browser manifests
|
||||
manifestOverrides: ./app/build-types/mmi/manifest/
|
||||
|
||||
flask:
|
||||
# Code surrounded using code fences for that feature
|
||||
# will not be removed
|
||||
features:
|
||||
- snaps
|
||||
- desktop
|
||||
- build-flask
|
||||
env:
|
||||
- INFURA_FLASK_PROJECT_ID
|
||||
- SEGMENT_FLASK_WRITE_KEY
|
||||
- ALLOW_LOCAL_SNAPS: true
|
||||
- REQUIRE_SNAPS_ALLOWLIST: false
|
||||
- SUPPORT_LINK: https://metamask-flask.zendesk.com/hc
|
||||
- SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new
|
||||
- INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID
|
||||
- SEGMENT_WRITE_KEY_REF: SEGMENT_FLASK_WRITE_KEY
|
||||
isPrerelease: true
|
||||
|
||||
desktop:
|
||||
features:
|
||||
- snaps
|
||||
- desktop
|
||||
- build-flask
|
||||
env:
|
||||
- INFURA_FLASK_PROJECT_ID
|
||||
- SEGMENT_FLASK_WRITE_KEY
|
||||
- ALLOW_LOCAL_SNAPS: true
|
||||
- REQUIRE_SNAPS_ALLOWLIST: false
|
||||
- SUPPORT_LINK: https://metamask-flask.zendesk.com/hc
|
||||
- SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new
|
||||
- INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID
|
||||
- SEGMENT_WRITE_KEY_REF: SEGMENT_FLASK_WRITE_KEY
|
||||
isPrerelease: true
|
||||
|
||||
mmi:
|
||||
features:
|
||||
- build-mmi
|
||||
env:
|
||||
- INFURA_MMI_PROJECT_ID
|
||||
- SEGMENT_MMI_WRITE_KEY
|
||||
- INFURA_ENV_KEY_REF: INFURA_MMI_PROJECT_ID
|
||||
- SEGMENT_WRITE_KEY_REF: SEGMENT_MMI_WRITE_KEY
|
||||
- SUPPORT_LINK: https://mmi-support.zendesk.com/hc/en-us
|
||||
- SUPPORT_REQUEST_LINK: https://mmi-support.zendesk.com/hc/en-us/requests/new
|
||||
# For some reason, MMI uses this type of versioning
|
||||
# Leaving it on for backwards compatibility
|
||||
isPrerelease: true
|
||||
|
||||
# Build types are composed of a set of features.
|
||||
# Each feature can have code fences that add new code
|
||||
# as well declaring, defining and overriding env variables
|
||||
features:
|
||||
snaps:
|
||||
# Each feature might have variables that only exist when built with that feature active
|
||||
env:
|
||||
# Whether to allow snaps from localhost - local://localhost:8080/snap.manifest.json
|
||||
# Enabled in Flask, will be disabled in Main
|
||||
- ALLOW_LOCAL_SNAPS
|
||||
# Whether to verify that a snap can be installed using an allow list
|
||||
- REQUIRE_SNAPS_ALLOWLIST
|
||||
assets:
|
||||
- ./{app,shared,ui}/**/snaps/**
|
||||
desktop:
|
||||
env:
|
||||
- COMPATIBILITY_VERSION_EXTENSION: 1
|
||||
- DISABLE_WEB_SOCKET_ENCRYPTION: false
|
||||
- SKIP_OTP_PAIRING_FLOW: false
|
||||
- WEB_SOCKET_PORT: null
|
||||
|
||||
###
|
||||
# Build Type code extensions. Things like different support links, warning pages, banners
|
||||
###
|
||||
|
||||
build-main:
|
||||
build-beta:
|
||||
assets:
|
||||
# Assets that will be copied
|
||||
- src: ./app/build-types/beta/images/
|
||||
dest: images
|
||||
# Assets that are exclusively included in this feature and ignored in others
|
||||
# Supports globs
|
||||
- ./{app,shared,ui}/**/beta/**
|
||||
build-mmi:
|
||||
assets:
|
||||
- src: ./app/build-types/mmi/images/
|
||||
dest: images
|
||||
- ./{app,shared,ui}/**/mmi/**
|
||||
build-flask:
|
||||
assets:
|
||||
- src: ./app/build-types/flask/images/
|
||||
dest: images
|
||||
- ./{app,shared,ui}/**/flask/**
|
||||
|
||||
# Env variables that are required for all types of builds
|
||||
#
|
||||
# env object supports both declarations (- FOO), and definitions (- FOO: BAR).
|
||||
# Variables that were declared have to be defined somewhere in the load chain before usage
|
||||
env:
|
||||
- SWAPS_USE_DEV_APIS: false
|
||||
- PORTFOLIO_URL: https://portfolio.metamask.io
|
||||
- TOKEN_ALLOWANCE_IMPROVEMENTS: false
|
||||
- TRANSACTION_SECURITY_PROVIDER: false
|
||||
# The unlock password
|
||||
- PASSWORD: null
|
||||
# Also see METAMASK_DEBUG and NODE_DEBUG
|
||||
- DEBUG: null
|
||||
- SUPPORT_LINK: https://support.metamask.io
|
||||
- SUPPORT_REQUEST_LINK: https://metamask.zendesk.com/hc/en-us
|
||||
- SKIP_BACKGROUND_INITIALIZATION: false
|
||||
- MULTICHAIN: false
|
||||
|
||||
# TODO(ritave): Move ManifestV3 into a feature?
|
||||
- ENABLE_MV3: false
|
||||
- HARDWARE_WALLETS_MV3: false
|
||||
# These are exclusively used for MV3
|
||||
- APPLY_LAVAMOAT
|
||||
- FILE_NAMES
|
||||
|
||||
###
|
||||
# API keys to 3rd party services
|
||||
###
|
||||
|
||||
- PUBNUB_PUB_KEY: null
|
||||
- PUBNUB_SUB_KEY: null
|
||||
- SEGMENT_HOST: null
|
||||
- SENTRY_DSN: null
|
||||
- SENTRY_DSN_DEV: null
|
||||
- OPENSEA_KEY: null
|
||||
- ETHERSCAN_KEY: null
|
||||
# also INFURA_PROJECT_ID below
|
||||
|
||||
###
|
||||
# Build system backwards compatibility
|
||||
###
|
||||
|
||||
- INFURA_ENV_KEY_REF
|
||||
- SEGMENT_WRITE_KEY_REF
|
||||
|
||||
###
|
||||
# Variables that are modified with hardcoded code
|
||||
###
|
||||
|
||||
# Used for debugging changes to the phishing warning page.
|
||||
# Modified in <root>/development/build/scripts.js:@getPhishingWarningPageUrl
|
||||
- PHISHING_WARNING_PAGE_URL: null
|
||||
# Modified in <root>/development/build/scripts.js:@getInfuraProjectId
|
||||
- INFURA_PROJECT_ID
|
||||
# Modified in <root>/development/build/scripts.js:@getSegmentWriteKey
|
||||
- SEGMENT_WRITE_KEY: ''
|
||||
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
|
||||
# Also see DEBUG and NODE_DEBUG
|
||||
- METAMASK_DEBUG: false
|
||||
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
|
||||
- ICON_NAMES
|
||||
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
|
||||
- IN_TEST
|
||||
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
|
||||
- METAMASK_ENVIRONMENT
|
||||
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
|
||||
- METAMASK_VERSION
|
||||
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
|
||||
- METAMASK_BUILD_TYPE
|
||||
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
|
||||
- NODE_ENV
|
||||
# Defined by node itself
|
||||
# For the purposes of the build system we define it as empty below
|
||||
# if it's not inside process.env
|
||||
# Also see DEBUG and METAMASK_DEBUG
|
||||
- NODE_DEBUG: ''
|
||||
|
||||
###
|
||||
# Meta variables
|
||||
###
|
||||
|
||||
# Uses yaml anchors to DRY - https://juju.is/docs/sdk/yaml-anchors-and-aliases
|
||||
- METAMASK_BUILD_TYPE_DEFAULT: *default
|
@ -6,10 +6,10 @@
|
||||
// subset of files to check against these targets.
|
||||
module.exports = {
|
||||
global: {
|
||||
lines: 69.3,
|
||||
branches: 57,
|
||||
statements: 68.5,
|
||||
functions: 61.5,
|
||||
lines: 69.92,
|
||||
branches: 57.63,
|
||||
statements: 69.24,
|
||||
functions: 62.51,
|
||||
},
|
||||
transforms: {
|
||||
branches: 100,
|
||||
|
@ -1,26 +1,13 @@
|
||||
const path = require('path');
|
||||
const { readFile } = require('fs/promises');
|
||||
const assert = require('assert');
|
||||
const { AssertionError } = require('assert');
|
||||
const ini = require('ini');
|
||||
const { BuildType } = require('../lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('../lib/build-type');
|
||||
const { Variables } = require('../lib/variables');
|
||||
const { ENVIRONMENT } = require('./constants');
|
||||
|
||||
const configurationPropertyNames = [
|
||||
'MULTICHAIN',
|
||||
'INFURA_PROJECT_ID',
|
||||
'PHISHING_WARNING_PAGE_URL',
|
||||
'PORTFOLIO_URL',
|
||||
'SEGMENT_HOST',
|
||||
'SEGMENT_WRITE_KEY',
|
||||
'SENTRY_DSN_DEV',
|
||||
'SWAPS_USE_DEV_APIS',
|
||||
// Desktop
|
||||
'COMPATIBILITY_VERSION_EXTENSION',
|
||||
'DISABLE_WEB_SOCKET_ENCRYPTION',
|
||||
'METAMASK_DEBUG',
|
||||
'SKIP_OTP_PAIRING_FLOW',
|
||||
'ENABLE_MV3',
|
||||
];
|
||||
|
||||
const productionConfigurationPropertyNames = [
|
||||
const VARIABLES_REQUIRED_IN_PRODUCTION = [
|
||||
'INFURA_BETA_PROJECT_ID',
|
||||
'INFURA_FLASK_PROJECT_ID',
|
||||
'INFURA_PROD_PROJECT_ID',
|
||||
@ -30,96 +17,145 @@ const productionConfigurationPropertyNames = [
|
||||
'SENTRY_DSN',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get configuration for non-production builds.
|
||||
*
|
||||
* @returns {object} The production configuration.
|
||||
*/
|
||||
async function getConfig() {
|
||||
const configPath = path.resolve(__dirname, '..', '..', '.metamaskrc');
|
||||
async function fromIniFile(filepath) {
|
||||
let configContents = '';
|
||||
try {
|
||||
configContents = await readFile(configPath, {
|
||||
configContents = await readFile(filepath, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const environmentVariables = {};
|
||||
for (const propertyName of configurationPropertyNames) {
|
||||
if (process.env[propertyName]) {
|
||||
environmentVariables[propertyName] = process.env[propertyName];
|
||||
const variables = ini.parse(configContents);
|
||||
assert(
|
||||
!Object.values(variables).some((variable) => typeof variable === 'object'),
|
||||
`When loading ${filepath} - INI categories are not supported`,
|
||||
);
|
||||
const entries = Object.entries(variables);
|
||||
|
||||
const declarations = new Set(
|
||||
entries.filter(([, value]) => value === '').map(([key]) => key),
|
||||
);
|
||||
const definitions = new Map(
|
||||
entries
|
||||
.filter(([, value]) => value !== '')
|
||||
.map(([key, value]) => [key, value]),
|
||||
);
|
||||
|
||||
return { declarations, definitions };
|
||||
}
|
||||
|
||||
function fromEnv(declarations) {
|
||||
const definitions = new Map(
|
||||
[...declarations]
|
||||
.filter((declaration) => declaration in process.env)
|
||||
.map((declaration) => [declaration, process.env[declaration]]),
|
||||
);
|
||||
return { definitions, declarations: new Set() };
|
||||
}
|
||||
|
||||
function fromBuildsYML(buildType, config) {
|
||||
const extractDeclarations = (envArray) =>
|
||||
envArray === undefined
|
||||
? []
|
||||
: envArray.map((env) => (typeof env === 'string' ? env : env.key));
|
||||
const extractDefinitions = (envArray) =>
|
||||
envArray === undefined
|
||||
? []
|
||||
: envArray.filter((env) => typeof env !== 'string');
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
buildType = buildType ?? config.default;
|
||||
const activeBuild = config.buildTypes[buildType];
|
||||
const activeFeatures = activeBuild.features ?? [];
|
||||
|
||||
let declarations = [...extractDeclarations(config.env)];
|
||||
|
||||
activeFeatures
|
||||
.map((feature) => config.features[feature])
|
||||
.filter((feature) => feature !== null)
|
||||
.forEach(({ env }) => declarations.push(...extractDeclarations(env)));
|
||||
|
||||
declarations.push(...extractDeclarations(activeBuild.env));
|
||||
declarations = new Set(declarations);
|
||||
|
||||
const definitions = new Map();
|
||||
|
||||
// 1. root env
|
||||
extractDefinitions(config.env).forEach(({ key, value }) =>
|
||||
definitions.set(key, value),
|
||||
);
|
||||
// 2. features env
|
||||
activeFeatures
|
||||
.filter((key) => config.features[key] !== null)
|
||||
.map((key) => config.features[key].env)
|
||||
.map(extractDefinitions)
|
||||
.flat()
|
||||
.forEach(({ key, value }) => definitions.set(key, value));
|
||||
// 3. build type env
|
||||
extractDefinitions(activeBuild.env).forEach(({ key, value }) =>
|
||||
definitions.set(key, value),
|
||||
);
|
||||
|
||||
return { declarations, definitions, activeFeatures, activeBuild };
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string?} buildType - The chosen build type to build
|
||||
* @param environment
|
||||
* @returns Parsed configuration of the build pipeline
|
||||
*/
|
||||
async function getConfig(buildType, environment) {
|
||||
const config = loadBuildTypesConfig();
|
||||
const {
|
||||
declarations: ymlDeclarations,
|
||||
definitions: ymlDefinitions,
|
||||
activeBuild,
|
||||
activeFeatures,
|
||||
} = await fromBuildsYML(buildType, config);
|
||||
|
||||
const variables = new Variables(ymlDeclarations);
|
||||
|
||||
// notice that maps have inverted value and key pair in forEach
|
||||
ymlDefinitions.forEach((value, key) => variables.set(key, value));
|
||||
|
||||
(
|
||||
await fromIniFile(path.resolve(__dirname, '..', '..', '.metamaskrc'))
|
||||
)?.definitions.forEach((value, key) => variables.set(key, value));
|
||||
(
|
||||
await fromIniFile(path.resolve(__dirname, '..', '..', '.metamaskprodrc'))
|
||||
)?.definitions.forEach((value, key) => variables.set(key, value));
|
||||
|
||||
fromEnv(ymlDeclarations).definitions.forEach((value, key) =>
|
||||
variables.set(key, value),
|
||||
);
|
||||
|
||||
// TODO(ritave): Move build targets and environments to builds.yml
|
||||
if (environment === ENVIRONMENT.PRODUCTION) {
|
||||
const undefinedVariables = VARIABLES_REQUIRED_IN_PRODUCTION.filter(
|
||||
(variable) => !variables.isDefined(variable),
|
||||
);
|
||||
if (undefinedVariables.length !== 0) {
|
||||
const message = `Some variables required to build production target are not defined.
|
||||
- ${undefinedVariables.join('\n - ')}
|
||||
`;
|
||||
throw new AssertionError({ message });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...ini.parse(configContents),
|
||||
...environmentVariables,
|
||||
variables,
|
||||
activeBuild,
|
||||
activeFeatures,
|
||||
buildsYml: config,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for production builds and perform validation.
|
||||
*
|
||||
* This function validates that all required variables are present, and that
|
||||
* the production configuration file doesn't include any extraneous entries.
|
||||
*
|
||||
* @param {BuildType} buildType - The current build type (e.g. "main", "flask",
|
||||
* etc.).
|
||||
* @returns {object} The production configuration.
|
||||
*/
|
||||
async function getProductionConfig(buildType) {
|
||||
const prodConfigPath = path.resolve(__dirname, '..', '..', '.metamaskprodrc');
|
||||
let prodConfigContents = '';
|
||||
try {
|
||||
prodConfigContents = await readFile(prodConfigPath, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const environmentVariables = {};
|
||||
for (const propertyName of productionConfigurationPropertyNames) {
|
||||
if (process.env[propertyName]) {
|
||||
environmentVariables[propertyName] = process.env[propertyName];
|
||||
}
|
||||
}
|
||||
|
||||
const prodConfig = {
|
||||
...ini.parse(prodConfigContents),
|
||||
...environmentVariables,
|
||||
};
|
||||
|
||||
const requiredEnvironmentVariables = {
|
||||
all: ['SENTRY_DSN'],
|
||||
[BuildType.beta]: ['INFURA_BETA_PROJECT_ID', 'SEGMENT_BETA_WRITE_KEY'],
|
||||
[BuildType.flask]: ['INFURA_FLASK_PROJECT_ID', 'SEGMENT_FLASK_WRITE_KEY'],
|
||||
[BuildType.main]: ['INFURA_PROD_PROJECT_ID', 'SEGMENT_PROD_WRITE_KEY'],
|
||||
[BuildType.mmi]: ['INFURA_MMI_PROJECT_ID', 'SEGMENT_MMI_WRITE_KEY'],
|
||||
};
|
||||
|
||||
for (const required of [
|
||||
...requiredEnvironmentVariables.all,
|
||||
...requiredEnvironmentVariables[buildType],
|
||||
]) {
|
||||
if (!prodConfig[required]) {
|
||||
throw new Error(`Missing '${required}' environment variable`);
|
||||
}
|
||||
}
|
||||
|
||||
const allValid = Object.values(requiredEnvironmentVariables).flat();
|
||||
for (const environmentVariable of Object.keys(prodConfig)) {
|
||||
if (!allValid.includes(environmentVariable)) {
|
||||
throw new Error(`Invalid environment variable: '${environmentVariable}'`);
|
||||
}
|
||||
}
|
||||
return prodConfig;
|
||||
}
|
||||
|
||||
module.exports = { getConfig, getProductionConfig };
|
||||
module.exports = {
|
||||
getConfig,
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ const del = require('del');
|
||||
const pify = require('pify');
|
||||
const pump = pify(require('pump'));
|
||||
|
||||
const { BuildType } = require('../lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('../lib/build-type');
|
||||
const { TASKS } = require('./constants');
|
||||
const { createTask, composeParallel } = require('./task');
|
||||
|
||||
@ -42,7 +42,7 @@ function createEtcTasks({ browserPlatforms, buildType, livereload, version }) {
|
||||
function createZipTask(platform, buildType, version) {
|
||||
return async () => {
|
||||
const path =
|
||||
buildType === BuildType.main
|
||||
buildType === loadBuildTypesConfig().default
|
||||
? `metamask-${platform}-${version}`
|
||||
: `metamask-${buildType}-${platform}-${version}`;
|
||||
await pump(
|
||||
|
@ -10,8 +10,10 @@ const yargs = require('yargs/yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const { sync: globby } = require('globby');
|
||||
const lavapack = require('@lavamoat/lavapack');
|
||||
const difference = require('lodash/difference');
|
||||
const { intersection } = require('lodash');
|
||||
const { getVersion } = require('../lib/get-version');
|
||||
const { BuildType, BuildTypeInheritance } = require('../lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('../lib/build-type');
|
||||
const { TASKS, ENVIRONMENT } = require('./constants');
|
||||
const {
|
||||
createTask,
|
||||
@ -71,65 +73,70 @@ async function defineAndRunBuildTasks() {
|
||||
shouldLintFenceFiles,
|
||||
skipStats,
|
||||
version,
|
||||
platform,
|
||||
} = await parseArgv();
|
||||
|
||||
// scuttle on production/tests environment only
|
||||
const shouldScuttle = ['dist', 'prod', 'test'].includes(entryTask);
|
||||
const isRootTask = ['dist', 'prod', 'test', 'dev'].includes(entryTask);
|
||||
|
||||
console.log(
|
||||
`Building lavamoat runtime file`,
|
||||
`(scuttling is ${shouldScuttle ? 'on' : 'off'})`,
|
||||
);
|
||||
if (isRootTask) {
|
||||
// scuttle on production/tests environment only
|
||||
const shouldScuttle = ['dist', 'prod', 'test'].includes(entryTask);
|
||||
|
||||
// build lavamoat runtime file
|
||||
await lavapack.buildRuntime({
|
||||
scuttleGlobalThis: applyLavaMoat && shouldScuttle,
|
||||
scuttleGlobalThisExceptions: [
|
||||
// globals used by different mm deps outside of lm compartment
|
||||
'toString',
|
||||
'getComputedStyle',
|
||||
'addEventListener',
|
||||
'removeEventListener',
|
||||
'ShadowRoot',
|
||||
'HTMLElement',
|
||||
'Element',
|
||||
'pageXOffset',
|
||||
'pageYOffset',
|
||||
'visualViewport',
|
||||
'Reflect',
|
||||
'Set',
|
||||
'Object',
|
||||
'navigator',
|
||||
'harden',
|
||||
'console',
|
||||
'Image', // Used by browser to generate notifications
|
||||
// globals chrome driver needs to function (test env)
|
||||
/cdc_[a-zA-Z0-9]+_[a-zA-Z]+/iu,
|
||||
'performance',
|
||||
'parseFloat',
|
||||
'innerWidth',
|
||||
'innerHeight',
|
||||
'Symbol',
|
||||
'Math',
|
||||
'DOMRect',
|
||||
'Number',
|
||||
'Array',
|
||||
'crypto',
|
||||
'Function',
|
||||
'Uint8Array',
|
||||
'String',
|
||||
'Promise',
|
||||
// globals sentry needs to function
|
||||
'__SENTRY__',
|
||||
'appState',
|
||||
'extra',
|
||||
'stateHooks',
|
||||
'sentryHooks',
|
||||
'sentry',
|
||||
],
|
||||
});
|
||||
console.log(
|
||||
`Building lavamoat runtime file`,
|
||||
`(scuttling is ${shouldScuttle ? 'on' : 'off'})`,
|
||||
);
|
||||
|
||||
const browserPlatforms = ['firefox', 'chrome'];
|
||||
// build lavamoat runtime file
|
||||
await lavapack.buildRuntime({
|
||||
scuttleGlobalThis: applyLavaMoat && shouldScuttle,
|
||||
scuttleGlobalThisExceptions: [
|
||||
// globals used by different mm deps outside of lm compartment
|
||||
'toString',
|
||||
'getComputedStyle',
|
||||
'addEventListener',
|
||||
'removeEventListener',
|
||||
'ShadowRoot',
|
||||
'HTMLElement',
|
||||
'Element',
|
||||
'pageXOffset',
|
||||
'pageYOffset',
|
||||
'visualViewport',
|
||||
'Reflect',
|
||||
'Set',
|
||||
'Object',
|
||||
'navigator',
|
||||
'harden',
|
||||
'console',
|
||||
'Image', // Used by browser to generate notifications
|
||||
// globals chrome driver needs to function (test env)
|
||||
/cdc_[a-zA-Z0-9]+_[a-zA-Z]+/iu,
|
||||
'performance',
|
||||
'parseFloat',
|
||||
'innerWidth',
|
||||
'innerHeight',
|
||||
'Symbol',
|
||||
'Math',
|
||||
'DOMRect',
|
||||
'Number',
|
||||
'Array',
|
||||
'crypto',
|
||||
'Function',
|
||||
'Uint8Array',
|
||||
'String',
|
||||
'Promise',
|
||||
// globals sentry needs to function
|
||||
'__SENTRY__',
|
||||
'appState',
|
||||
'extra',
|
||||
'stateHooks',
|
||||
'sentryHooks',
|
||||
'sentry',
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const browserPlatforms = platform ? [platform] : ['firefox', 'chrome'];
|
||||
|
||||
const browserVersionMap = getBrowserVersionMap(browserPlatforms, version);
|
||||
|
||||
@ -271,9 +278,9 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`,
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('build-type', {
|
||||
default: BuildType.main,
|
||||
default: loadBuildTypesConfig().default,
|
||||
description: 'The type of build to create.',
|
||||
choices: Object.keys(BuildType),
|
||||
choices: Object.keys(loadBuildTypesConfig().buildTypes),
|
||||
})
|
||||
.option('build-version', {
|
||||
default: 0,
|
||||
@ -311,6 +318,13 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`,
|
||||
hidden: true,
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('platform', {
|
||||
default: '',
|
||||
description:
|
||||
'Specify a single browser platform to build for. Either `chrome` or `firefox`',
|
||||
hidden: true,
|
||||
type: 'string',
|
||||
})
|
||||
.check((args) => {
|
||||
if (!Number.isInteger(args.buildVersion)) {
|
||||
throw new Error(
|
||||
@ -335,6 +349,7 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`,
|
||||
policyOnly,
|
||||
skipStats,
|
||||
task,
|
||||
platform,
|
||||
} = argv;
|
||||
|
||||
// Manually default this to `false` for dev builds only.
|
||||
@ -365,6 +380,7 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`,
|
||||
shouldLintFenceFiles,
|
||||
skipStats,
|
||||
version,
|
||||
platform,
|
||||
};
|
||||
}
|
||||
|
||||
@ -376,29 +392,36 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`,
|
||||
* build, or `null` if no files are to be ignored.
|
||||
*/
|
||||
function getIgnoredFiles(currentBuildType) {
|
||||
const inheritedBuildTypes = BuildTypeInheritance[currentBuildType] || [];
|
||||
const excludedFiles = Object.values(BuildType)
|
||||
// This filter removes "main" and the current build type. The files of any
|
||||
// build types that remain in the array will be excluded. "main" is the
|
||||
// default build type, and has no files that are excluded from other builds.
|
||||
.filter(
|
||||
(buildType) =>
|
||||
buildType !== BuildType.main &&
|
||||
buildType !== currentBuildType &&
|
||||
!inheritedBuildTypes.includes(buildType),
|
||||
)
|
||||
// Compute globs targeting files for exclusion for each excluded build
|
||||
// type.
|
||||
.reduce((excludedGlobs, excludedBuildType) => {
|
||||
return excludedGlobs.concat([
|
||||
`../../app/**/${excludedBuildType}/**`,
|
||||
`../../shared/**/${excludedBuildType}/**`,
|
||||
`../../ui/**/${excludedBuildType}/**`,
|
||||
]);
|
||||
}, [])
|
||||
// This creates absolute paths of the form:
|
||||
// PATH_TO_REPOSITORY_ROOT/app/**/${excludedBuildType}/**
|
||||
.map((pathGlob) => path.resolve(__dirname, pathGlob));
|
||||
const buildConfig = loadBuildTypesConfig();
|
||||
const cwd = process.cwd();
|
||||
|
||||
return globby(excludedFiles);
|
||||
const exclusiveAssetsForFeatures = (features) =>
|
||||
globby(
|
||||
features
|
||||
.flatMap(
|
||||
(feature) =>
|
||||
buildConfig.features[feature].assets
|
||||
?.filter((asset) => 'exclusiveInclude' in asset)
|
||||
.map((asset) => asset.exclusiveInclude) ?? [],
|
||||
)
|
||||
.map((pathGlob) => path.resolve(cwd, pathGlob)),
|
||||
);
|
||||
|
||||
const allFeatures = Object.keys(buildConfig.features);
|
||||
const activeFeatures =
|
||||
buildConfig.buildTypes[currentBuildType].features ?? [];
|
||||
const inactiveFeatures = difference(allFeatures, activeFeatures);
|
||||
|
||||
const ignoredPaths = exclusiveAssetsForFeatures(inactiveFeatures);
|
||||
// We do a sanity check to verify that any inactive feature haven't excluded files
|
||||
// that active features are trying to include
|
||||
const activePaths = exclusiveAssetsForFeatures(activeFeatures);
|
||||
const conflicts = intersection(activePaths, ignoredPaths);
|
||||
if (conflicts.length !== 0) {
|
||||
throw new Error(`Below paths are required exclusively by both active and inactive features resulting in a conflict:
|
||||
\t-> ${conflicts.join('\n\t-> ')}
|
||||
Please fix builds.yml`);
|
||||
}
|
||||
|
||||
return ignoredPaths;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ const { mergeWith, cloneDeep, capitalize } = require('lodash');
|
||||
const baseManifest = process.env.ENABLE_MV3
|
||||
? require('../../app/manifest/v3/_base.json')
|
||||
: require('../../app/manifest/v2/_base.json');
|
||||
const { BuildType } = require('../lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('../lib/build-type');
|
||||
|
||||
const { TASKS, ENVIRONMENT } = require('./constants');
|
||||
const { createTask, composeSeries } = require('./task');
|
||||
@ -165,25 +165,24 @@ async function writeJson(obj, file) {
|
||||
* Get manifest modifications for the given build type, including modifications specific to the
|
||||
* given platform.
|
||||
*
|
||||
* @param {BuildType} buildType - The build type.
|
||||
* @param {string} buildType - The build type.
|
||||
* @param {string} platform - The platform (i.e. the browser).
|
||||
* @returns {object} The build modificantions for the given build type and platform.
|
||||
* @returns {object} The build modifications for the given build type and platform.
|
||||
*/
|
||||
async function getBuildModifications(buildType, platform) {
|
||||
if (!Object.values(BuildType).includes(buildType)) {
|
||||
const buildConfig = loadBuildTypesConfig();
|
||||
if (!(buildType in buildConfig.buildTypes)) {
|
||||
throw new Error(`Invalid build type: ${buildType}`);
|
||||
} else if (buildType === BuildType.main) {
|
||||
}
|
||||
|
||||
const overridesPath = buildConfig.buildTypes[buildType].manifestOverrides;
|
||||
if (overridesPath === undefined) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const builtTypeManifestDirectoryPath = path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'app',
|
||||
'build-types',
|
||||
buildType,
|
||||
'manifest',
|
||||
process.cwd(),
|
||||
overridesPath,
|
||||
);
|
||||
|
||||
const baseBuildTypeModificationsPath = path.join(
|
||||
|
@ -1,7 +1,10 @@
|
||||
// TODO(ritave): Remove switches on hardcoded build types
|
||||
|
||||
const { callbackify } = require('util');
|
||||
const path = require('path');
|
||||
const { writeFileSync, readFileSync } = require('fs');
|
||||
const EventEmitter = require('events');
|
||||
const assert = require('assert');
|
||||
const gulp = require('gulp');
|
||||
const watch = require('gulp-watch');
|
||||
const Vinyl = require('vinyl');
|
||||
@ -29,10 +32,9 @@ const bifyModuleGroups = require('bify-module-groups');
|
||||
|
||||
const phishingWarningManifest = require('@metamask/phishing-warning/package.json');
|
||||
const { streamFlatMap } = require('../stream-flat-map');
|
||||
const { BuildType } = require('../lib/build-type');
|
||||
const { generateIconNames } = require('../generate-icon-names');
|
||||
const { BUILD_TARGETS, ENVIRONMENT } = require('./constants');
|
||||
const { getConfig, getProductionConfig } = require('./config');
|
||||
const { getConfig } = require('./config');
|
||||
const {
|
||||
isDevBuild,
|
||||
isTestBuild,
|
||||
@ -101,67 +103,87 @@ const standardScuttlingConfig = {
|
||||
* Get the appropriate Infura project ID.
|
||||
*
|
||||
* @param {object} options - The Infura project ID options.
|
||||
* @param {BuildType} options.buildType - The current build type.
|
||||
* @param {object} options.config - The environment variable configuration.
|
||||
* @param {string} options.buildType - The current build type.
|
||||
* @param {ENVIRONMENT[keyof ENVIRONMENT]} options.environment - The build environment.
|
||||
* @param {boolean} options.testing - Whether this is a test build or not.
|
||||
* @param options.variables
|
||||
* @returns {string} The Infura project ID.
|
||||
*/
|
||||
function getInfuraProjectId({ buildType, config, environment, testing }) {
|
||||
function getInfuraProjectId({ buildType, variables, environment, testing }) {
|
||||
const EMPTY_PROJECT_ID = '00000000000000000000000000000000';
|
||||
if (testing) {
|
||||
return '00000000000000000000000000000000';
|
||||
return EMPTY_PROJECT_ID;
|
||||
} else if (environment !== ENVIRONMENT.PRODUCTION) {
|
||||
// Skip validation because this is unset on PRs from forks.
|
||||
return config.INFURA_PROJECT_ID;
|
||||
} else if (buildType === BuildType.main) {
|
||||
return config.INFURA_PROD_PROJECT_ID;
|
||||
} else if (buildType === BuildType.beta) {
|
||||
return config.INFURA_BETA_PROJECT_ID;
|
||||
} else if (buildType === BuildType.flask) {
|
||||
return config.INFURA_FLASK_PROJECT_ID;
|
||||
} else if (buildType === BuildType.mmi) {
|
||||
return config.INFURA_MMI_PROJECT_ID;
|
||||
// For forks, return empty project ID if we don't have one.
|
||||
if (
|
||||
!variables.isDefined('INFURA_PROJECT_ID') &&
|
||||
environment === ENVIRONMENT.PULL_REQUEST
|
||||
) {
|
||||
return EMPTY_PROJECT_ID;
|
||||
}
|
||||
return variables.get('INFURA_PROJECT_ID');
|
||||
}
|
||||
throw new Error(`Invalid build type: '${buildType}'`);
|
||||
/** @type {string|undefined} */
|
||||
const infuraKeyReference = process.env.INFURA_ENV_KEY_REF;
|
||||
assert(
|
||||
typeof infuraKeyReference === 'string' && infuraKeyReference.length > 0,
|
||||
`Build type "${buildType}" has improperly set INFURA_ENV_KEY_REF in builds.yml. Current value: "${infuraKeyReference}"`,
|
||||
);
|
||||
/** @type {string|undefined} */
|
||||
const infuraProjectId = variables.get(infuraKeyReference);
|
||||
assert(
|
||||
typeof infuraProjectId === 'string' && infuraProjectId.length > 0,
|
||||
`Infura Project ID environmental variable "${infuraKeyReference}" is set improperly.`,
|
||||
);
|
||||
return infuraProjectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate Segment write key.
|
||||
*
|
||||
* @param {object} options - The Segment write key options.
|
||||
* @param {BuildType} options.buildType - The current build type.
|
||||
* @param {object} options.config - The environment variable configuration.
|
||||
* @param {string} options.buildType - The current build type.
|
||||
* @param {keyof ENVIRONMENT} options.environment - The current build environment.
|
||||
* @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline
|
||||
* @returns {string} The Segment write key.
|
||||
*/
|
||||
function getSegmentWriteKey({ buildType, config, environment }) {
|
||||
function getSegmentWriteKey({ buildType, variables, environment }) {
|
||||
if (environment !== ENVIRONMENT.PRODUCTION) {
|
||||
// Skip validation because this is unset on PRs from forks, and isn't necessary for development builds.
|
||||
return config.SEGMENT_WRITE_KEY;
|
||||
} else if (buildType === BuildType.main) {
|
||||
return config.SEGMENT_PROD_WRITE_KEY;
|
||||
} else if (buildType === BuildType.beta) {
|
||||
return config.SEGMENT_BETA_WRITE_KEY;
|
||||
} else if (buildType === BuildType.flask) {
|
||||
return config.SEGMENT_FLASK_WRITE_KEY;
|
||||
} else if (buildType === BuildType.mmi) {
|
||||
return config.SEGMENT_MMI_WRITE_KEY;
|
||||
return variables.get('SEGMENT_WRITE_KEY');
|
||||
}
|
||||
throw new Error(`Invalid build type: '${buildType}'`);
|
||||
|
||||
const segmentKeyReference = process.env.SEGMENT_WRITE_KEY_REF;
|
||||
assert(
|
||||
typeof segmentKeyReference === 'string' && segmentKeyReference.length > 0,
|
||||
`Build type "${buildType}" has improperly set SEGMENT_WRITE_KEY_REF in builds.yml. Current value: "${segmentKeyReference}"`,
|
||||
);
|
||||
|
||||
const segmentWriteKey = variables.get(segmentKeyReference);
|
||||
assert(
|
||||
typeof segmentWriteKey === 'string' && segmentWriteKey.length > 0,
|
||||
`Segment Write Key environmental variable "${segmentKeyReference}" is set improperly.`,
|
||||
);
|
||||
return segmentWriteKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL for the phishing warning page, if it has been set.
|
||||
*
|
||||
* @param {object} options - The phishing warning page options.
|
||||
* @param {object} options.config - The environment variable configuration.
|
||||
* @param {boolean} options.testing - Whether this is a test build or not.
|
||||
* @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline
|
||||
* @returns {string} The URL for the phishing warning page, or `undefined` if no URL is set.
|
||||
*/
|
||||
function getPhishingWarningPageUrl({ config, testing }) {
|
||||
let phishingWarningPageUrl = config.PHISHING_WARNING_PAGE_URL;
|
||||
function getPhishingWarningPageUrl({ variables, testing }) {
|
||||
let phishingWarningPageUrl = variables.get('PHISHING_WARNING_PAGE_URL');
|
||||
|
||||
if (!phishingWarningPageUrl) {
|
||||
assert(
|
||||
phishingWarningPageUrl === null ||
|
||||
typeof phishingWarningPageUrl === 'string',
|
||||
);
|
||||
if (phishingWarningPageUrl === null) {
|
||||
phishingWarningPageUrl = testing
|
||||
? 'http://localhost:9999/'
|
||||
: `https://metamask.github.io/phishing-warning/v${phishingWarningManifest.version}/`;
|
||||
@ -209,7 +231,7 @@ module.exports = createScriptTasks;
|
||||
* LavaMoat at runtime or not.
|
||||
* @param {string[]} options.browserPlatforms - A list of browser platforms to
|
||||
* build bundles for.
|
||||
* @param {BuildType} options.buildType - The current build type (e.g. "main",
|
||||
* @param {string} options.buildType - The current build type (e.g. "main",
|
||||
* "flask", etc.).
|
||||
* @param {string[] | null} options.ignoredFiles - A list of files to exclude
|
||||
* from the current build.
|
||||
@ -455,7 +477,7 @@ function createScriptTasks({
|
||||
* @param {string[]} options.browserPlatforms - A list of browser platforms to
|
||||
* build bundles for.
|
||||
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
|
||||
* @param {BuildType} options.buildType - The current build type (e.g. "main",
|
||||
* @param {string} options.buildType - The current build type (e.g. "main",
|
||||
* "flask", etc.).
|
||||
* @param {string[] | null} options.ignoredFiles - A list of files to exclude
|
||||
* from the current build.
|
||||
@ -541,7 +563,7 @@ async function createManifestV3AppInitializationBundle({
|
||||
* @param {string[]} options.browserPlatforms - A list of browser platforms to
|
||||
* build bundles for.
|
||||
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
|
||||
* @param {BuildType} options.buildType - The current build type (e.g. "main",
|
||||
* @param {string} options.buildType - The current build type (e.g. "main",
|
||||
* "flask", etc.).
|
||||
* @param {string[]} options.entryFiles - A list of entry point file paths,
|
||||
* relative to the repository root directory.
|
||||
@ -576,18 +598,29 @@ function createFactoredBuild({
|
||||
const reloadOnChange = isDevBuild(buildTarget);
|
||||
const minify = !isDevBuild(buildTarget);
|
||||
|
||||
const envVars = await getEnvironmentVariables({
|
||||
const environment = getEnvironment({ buildTarget });
|
||||
const config = await getConfig(buildType, environment);
|
||||
const { variables, activeBuild } = config;
|
||||
await setEnvironmentVariables({
|
||||
buildTarget,
|
||||
buildType,
|
||||
environment,
|
||||
variables,
|
||||
activeBuild,
|
||||
version,
|
||||
});
|
||||
const features = {
|
||||
active: new Set(activeBuild.features ?? []),
|
||||
all: new Set(Object.keys(config.buildsYml.features)),
|
||||
};
|
||||
setupBundlerDefaults(buildConfiguration, {
|
||||
buildTarget,
|
||||
buildType,
|
||||
envVars,
|
||||
variables,
|
||||
envVars: buildSafeVariableObject(variables),
|
||||
ignoredFiles,
|
||||
policyOnly,
|
||||
minify,
|
||||
features,
|
||||
reloadOnChange,
|
||||
shouldLintFenceFiles,
|
||||
});
|
||||
@ -760,7 +793,7 @@ function createFactoredBuild({
|
||||
* @param {string[]} options.browserPlatforms - A list of browser platforms to
|
||||
* build the bundle for.
|
||||
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
|
||||
* @param {BuildType} options.buildType - The current build type (e.g. "main",
|
||||
* @param {string} options.buildType - The current build type (e.g. "main",
|
||||
* "flask", etc.).
|
||||
* @param {string} options.destFilepath - The file path the bundle should be
|
||||
* written to.
|
||||
@ -806,20 +839,31 @@ function createNormalBundle({
|
||||
const reloadOnChange = Boolean(devMode);
|
||||
const minify = Boolean(devMode) === false;
|
||||
|
||||
const envVars = {
|
||||
...(await getEnvironmentVariables({
|
||||
buildTarget,
|
||||
buildType,
|
||||
version,
|
||||
})),
|
||||
...extraEnvironmentVariables,
|
||||
const environment = getEnvironment({ buildTarget });
|
||||
const config = await getConfig(buildType, environment);
|
||||
const { activeBuild, variables } = config;
|
||||
await setEnvironmentVariables({
|
||||
buildTarget,
|
||||
buildType,
|
||||
variables,
|
||||
environment,
|
||||
activeBuild,
|
||||
version,
|
||||
});
|
||||
Object.entries(extraEnvironmentVariables ?? {}).forEach(([key, value]) =>
|
||||
variables.set(key, value),
|
||||
);
|
||||
|
||||
const features = {
|
||||
active: new Set(activeBuild.features ?? []),
|
||||
all: new Set(Object.keys(config.buildsYml.features)),
|
||||
};
|
||||
setupBundlerDefaults(buildConfiguration, {
|
||||
buildType,
|
||||
envVars,
|
||||
envVars: buildSafeVariableObject(variables),
|
||||
ignoredFiles,
|
||||
policyOnly,
|
||||
minify,
|
||||
features,
|
||||
reloadOnChange,
|
||||
shouldLintFenceFiles,
|
||||
applyLavaMoat,
|
||||
@ -865,11 +909,11 @@ function setupBundlerDefaults(
|
||||
buildConfiguration,
|
||||
{
|
||||
buildTarget,
|
||||
buildType,
|
||||
envVars,
|
||||
ignoredFiles,
|
||||
policyOnly,
|
||||
minify,
|
||||
features,
|
||||
reloadOnChange,
|
||||
shouldLintFenceFiles,
|
||||
applyLavaMoat,
|
||||
@ -882,7 +926,7 @@ function setupBundlerDefaults(
|
||||
// Source transforms
|
||||
transform: [
|
||||
// // Remove code that should be excluded from builds of the current type
|
||||
createRemoveFencedCodeTransform(buildType, shouldLintFenceFiles),
|
||||
createRemoveFencedCodeTransform(features, shouldLintFenceFiles),
|
||||
// Transpile top-level code
|
||||
[
|
||||
babelify,
|
||||
@ -1074,6 +1118,11 @@ async function createBundle(buildConfiguration, { reloadOnChange }) {
|
||||
logError(error);
|
||||
process.exit(1);
|
||||
});
|
||||
pipeline.on('error', (error) => {
|
||||
console.error('Pipeline failed! See details below.');
|
||||
logError(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
// trigger build pipeline instrumentations
|
||||
events.emit('configurePipeline', { pipeline, bundleStream });
|
||||
@ -1090,56 +1139,55 @@ async function createBundle(buildConfiguration, { reloadOnChange }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get environment variables to inject in the current build.
|
||||
* Sets environment variables to inject in the current build.
|
||||
*
|
||||
* @param {object} options - Build options.
|
||||
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
|
||||
* @param {BuildType} options.buildType - The current build type (e.g. "main",
|
||||
* @param {string} options.buildType - The current build type (e.g. "main",
|
||||
* "flask", etc.).
|
||||
* @param {string} options.version - The current version of the extension.
|
||||
* @returns {object} A map of environment variables to inject.
|
||||
* @param options.activeBuild
|
||||
* @param options.variables
|
||||
* @param options.environment
|
||||
*/
|
||||
async function getEnvironmentVariables({ buildTarget, buildType, version }) {
|
||||
const environment = getEnvironment({ buildTarget });
|
||||
const config =
|
||||
environment === ENVIRONMENT.PRODUCTION
|
||||
? await getProductionConfig(buildType)
|
||||
: await getConfig();
|
||||
|
||||
async function setEnvironmentVariables({
|
||||
buildTarget,
|
||||
buildType,
|
||||
activeBuild,
|
||||
environment,
|
||||
variables,
|
||||
version,
|
||||
}) {
|
||||
const devMode = isDevBuild(buildTarget);
|
||||
const testing = isTestBuild(buildTarget);
|
||||
const iconNames = await generateIconNames();
|
||||
return {
|
||||
|
||||
variables.set({
|
||||
ICON_NAMES: iconNames,
|
||||
MULTICHAIN: config.MULTICHAIN === '1',
|
||||
CONF: devMode ? config : {},
|
||||
ENABLE_MV3: config.ENABLE_MV3,
|
||||
IN_TEST: testing,
|
||||
INFURA_PROJECT_ID: getInfuraProjectId({
|
||||
buildType,
|
||||
config,
|
||||
activeBuild,
|
||||
variables,
|
||||
environment,
|
||||
testing,
|
||||
}),
|
||||
METAMASK_DEBUG: devMode || config.METAMASK_DEBUG === '1',
|
||||
METAMASK_DEBUG: devMode || variables.getMaybe('METAMASK_DEBUG') === true,
|
||||
METAMASK_ENVIRONMENT: environment,
|
||||
METAMASK_VERSION: version,
|
||||
METAMASK_BUILD_TYPE: buildType,
|
||||
NODE_ENV: devMode ? ENVIRONMENT.DEVELOPMENT : ENVIRONMENT.PRODUCTION,
|
||||
PHISHING_WARNING_PAGE_URL: getPhishingWarningPageUrl({ config, testing }),
|
||||
PORTFOLIO_URL: config.PORTFOLIO_URL || 'https://portfolio.metamask.io',
|
||||
SEGMENT_HOST: config.SEGMENT_HOST,
|
||||
SEGMENT_WRITE_KEY: getSegmentWriteKey({ buildType, config, environment }),
|
||||
SENTRY_DSN: config.SENTRY_DSN,
|
||||
SENTRY_DSN_DEV: config.SENTRY_DSN_DEV,
|
||||
SWAPS_USE_DEV_APIS: config.SWAPS_USE_DEV_APIS === '1',
|
||||
TOKEN_ALLOWANCE_IMPROVEMENTS: config.TOKEN_ALLOWANCE_IMPROVEMENTS === '1',
|
||||
TRANSACTION_SECURITY_PROVIDER: config.TRANSACTION_SECURITY_PROVIDER === '1',
|
||||
// Desktop
|
||||
COMPATIBILITY_VERSION_EXTENSION: config.COMPATIBILITY_VERSION_EXTENSION,
|
||||
DISABLE_WEB_SOCKET_ENCRYPTION: config.DISABLE_WEB_SOCKET_ENCRYPTION === '1',
|
||||
SKIP_OTP_PAIRING_FLOW: config.SKIP_OTP_PAIRING_FLOW === '1',
|
||||
};
|
||||
PHISHING_WARNING_PAGE_URL: getPhishingWarningPageUrl({
|
||||
variables,
|
||||
testing,
|
||||
}),
|
||||
SEGMENT_WRITE_KEY: getSegmentWriteKey({
|
||||
buildType,
|
||||
activeBuild,
|
||||
variables,
|
||||
environment,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function renderHtmlFile({
|
||||
@ -1172,6 +1220,29 @@ function renderHtmlFile({
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a javascript object that throws an error when trying to access a property that wasn't defined properly
|
||||
*
|
||||
* @param {Variables} variables
|
||||
* @returns {{[key: string]: unknown }} Variable definitions
|
||||
*/
|
||||
function buildSafeVariableObject(variables) {
|
||||
return new Proxy(
|
||||
{},
|
||||
{
|
||||
has(_, key) {
|
||||
return key !== '_'; // loose-envify uses "_" for settings
|
||||
},
|
||||
get(_, key) {
|
||||
if (key === '_') {
|
||||
return undefined; // loose-envify uses "_" for settings
|
||||
}
|
||||
return variables.get(key);
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function beep() {
|
||||
process.stdout.write('\x07');
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ const watch = require('gulp-watch');
|
||||
const glob = require('fast-glob');
|
||||
|
||||
const locales = require('../../app/_locales/index.json');
|
||||
const { BuildType } = require('../lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('../lib/build-type');
|
||||
|
||||
const { TASKS } = require('./constants');
|
||||
const { createTask, composeSeries } = require('./task');
|
||||
@ -31,41 +31,23 @@ module.exports = function createStaticAssetTasks({
|
||||
copyTargetsDevs[browser] = copyTargetsDev;
|
||||
});
|
||||
|
||||
const additionalBuildTargets = {
|
||||
[BuildType.beta]: [
|
||||
{
|
||||
src: './app/build-types/beta/images/',
|
||||
dest: `images`,
|
||||
},
|
||||
],
|
||||
[BuildType.flask]: [
|
||||
{
|
||||
src: './app/build-types/flask/images/',
|
||||
dest: `images`,
|
||||
},
|
||||
],
|
||||
[BuildType.desktop]: [
|
||||
{
|
||||
src: './app/build-types/desktop/images/',
|
||||
dest: `images`,
|
||||
},
|
||||
],
|
||||
[BuildType.mmi]: [
|
||||
{
|
||||
src: './app/build-types/mmi/images/',
|
||||
dest: `images`,
|
||||
},
|
||||
],
|
||||
};
|
||||
const buildConfig = loadBuildTypesConfig();
|
||||
|
||||
if (Object.keys(additionalBuildTargets).includes(buildType)) {
|
||||
Object.entries(copyTargetsProds).forEach(([_, copyTargetsProd]) =>
|
||||
copyTargetsProd.push(...additionalBuildTargets[buildType]),
|
||||
);
|
||||
Object.entries(copyTargetsDevs).forEach(([_, copyTargetsDev]) =>
|
||||
copyTargetsDev.push(...additionalBuildTargets[buildType]),
|
||||
);
|
||||
}
|
||||
const activeFeatures = buildConfig.buildTypes[buildType].features ?? [];
|
||||
|
||||
const additionalAssets = activeFeatures.flatMap(
|
||||
(feature) =>
|
||||
buildConfig.features[feature].assets?.filter(
|
||||
(asset) => !('exclusiveInclude' in asset),
|
||||
) ?? [],
|
||||
);
|
||||
|
||||
Object.entries(copyTargetsProds).forEach(([_, copyTargetsProd]) =>
|
||||
copyTargetsProd.push(...additionalAssets),
|
||||
);
|
||||
Object.entries(copyTargetsDevs).forEach(([_, copyTargetsDev]) =>
|
||||
copyTargetsDev.push(...additionalAssets),
|
||||
);
|
||||
|
||||
const prodTasks = [];
|
||||
Object.entries(copyTargetsProds).forEach(([browser, copyTargetsProd]) => {
|
||||
@ -120,7 +102,7 @@ module.exports = function createStaticAssetTasks({
|
||||
await Promise.all(
|
||||
sources.map(async (src) => {
|
||||
const relativePath = path.relative(baseDir, src);
|
||||
await fs.copy(src, `${dest}${relativePath}`);
|
||||
await fs.copy(src, `${dest}${relativePath}`, { overwrite: true });
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -66,12 +66,10 @@ function createStyleTasks({ livereload }) {
|
||||
|
||||
async function buildScssPipeline(src, dest, devMode, rtl) {
|
||||
if (!sass) {
|
||||
// eslint-disable-next-line node/global-require
|
||||
sass = require('gulp-dart-sass');
|
||||
// use our own compiler which runs sass in its own process
|
||||
// in order to not pollute the intrinsics
|
||||
// eslint-disable-next-line node/global-require
|
||||
sass.compiler = require('./sass-compiler');
|
||||
sass = require('gulp-sass')(require('./sass-compiler'));
|
||||
}
|
||||
await pump(
|
||||
...[
|
||||
|
@ -80,12 +80,9 @@ function runInChildProcess(
|
||||
);
|
||||
|
||||
// forward logs to main process
|
||||
// skip the first stdout event (announcing the process command)
|
||||
childProcess.stdout.once('data', () => {
|
||||
childProcess.stdout.on('data', (data) =>
|
||||
process.stdout.write(`${taskName}: ${data}`),
|
||||
);
|
||||
});
|
||||
childProcess.stdout.on('data', (data) =>
|
||||
process.stdout.write(`${taskName}: ${data}`),
|
||||
);
|
||||
|
||||
childProcess.stderr.on('data', (data) =>
|
||||
process.stderr.write(`${taskName}: ${data}`),
|
||||
|
@ -29,7 +29,7 @@ this.store.updateStructure({
|
||||
...,
|
||||
GasFeeController: this.gasFeeController,
|
||||
TokenListController: this.tokenListController,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
SnapController: this.snapController,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
});
|
||||
@ -49,7 +49,7 @@ Note that multiple build types can be specified by separating them with
|
||||
commands inside the parameter parentheses:
|
||||
|
||||
```javascript
|
||||
///: BEGIN:ONLY_INCLUDE_IN(beta,flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-beta,build-flask)
|
||||
```
|
||||
|
||||
### Gotchas
|
||||
@ -114,7 +114,7 @@ only be included in a particular build type, say `beta`, they should be wrapped
|
||||
as follows:
|
||||
|
||||
```javascript
|
||||
///: BEGIN:ONLY_INCLUDE_IN(beta)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
|
||||
console.log('I am only included in beta builds.');
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
```
|
||||
|
4
development/build/transforms/remove-fenced-code.d.ts
vendored
Normal file
4
development/build/transforms/remove-fenced-code.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export type Features = {
|
||||
active: ReadonlySet<string>;
|
||||
all: ReadonlySet<string>;
|
||||
};
|
@ -1,6 +1,5 @@
|
||||
const path = require('path');
|
||||
const { PassThrough, Transform } = require('stream');
|
||||
const { BuildType, BuildTypeInheritance } = require('../../lib/build-type');
|
||||
const { lintTransformedFile } = require('./utils');
|
||||
|
||||
const hasKey = (obj, key) => Reflect.hasOwnProperty.call(obj, key);
|
||||
@ -18,14 +17,14 @@ class RemoveFencedCodeTransform extends Transform {
|
||||
* Optionally lints the file if it was modified.
|
||||
*
|
||||
* @param {string} filePath - The path to the file being transformed.
|
||||
* @param {string} buildType - The type of the current build process.
|
||||
* @param {import('./remove-fenced-code').Features} features - Features that are currently enabled.
|
||||
* @param {boolean} shouldLintTransformedFiles - Whether the file should be
|
||||
* linted if modified by the transform.
|
||||
*/
|
||||
constructor(filePath, buildType, shouldLintTransformedFiles) {
|
||||
constructor(filePath, features, shouldLintTransformedFiles) {
|
||||
super();
|
||||
this.filePath = filePath;
|
||||
this.buildType = buildType;
|
||||
this.features = features;
|
||||
this.shouldLintTransformedFiles = shouldLintTransformedFiles;
|
||||
this._fileBuffers = [];
|
||||
}
|
||||
@ -45,7 +44,7 @@ class RemoveFencedCodeTransform extends Transform {
|
||||
try {
|
||||
[fileContent, didModify] = removeFencedCode(
|
||||
this.filePath,
|
||||
this.buildType,
|
||||
this.features,
|
||||
Buffer.concat(this._fileBuffers).toString('utf8'),
|
||||
);
|
||||
} catch (error) {
|
||||
@ -82,20 +81,14 @@ class RemoveFencedCodeTransform extends Transform {
|
||||
* file is ignored by ESLint, since linting is our first line of defense against
|
||||
* making un-syntactic modifications to files using code fences.
|
||||
*
|
||||
* @param {string} buildType - The type of the current build.
|
||||
* @param {import('./remove-fenced-code').Features} features - Features that are currently enabled.
|
||||
* @param {boolean} shouldLintTransformedFiles - Whether to lint transformed files.
|
||||
* @returns {(filePath: string) => Transform} The transform function.
|
||||
*/
|
||||
function createRemoveFencedCodeTransform(
|
||||
buildType,
|
||||
features,
|
||||
shouldLintTransformedFiles = true,
|
||||
) {
|
||||
if (!hasKey(BuildType, buildType)) {
|
||||
throw new Error(
|
||||
`Code fencing transform received unrecognized build type "${buildType}".`,
|
||||
);
|
||||
}
|
||||
|
||||
// Browserify transforms are functions that receive a file name and return a
|
||||
// duplex stream. The stream receives the file contents piecemeal in the form
|
||||
// of Buffers.
|
||||
@ -117,7 +110,7 @@ function createRemoveFencedCodeTransform(
|
||||
|
||||
return new RemoveFencedCodeTransform(
|
||||
filePath,
|
||||
buildType,
|
||||
features,
|
||||
shouldLintTransformedFiles,
|
||||
);
|
||||
};
|
||||
@ -132,31 +125,39 @@ const DirectiveCommands = {
|
||||
ONLY_INCLUDE_IN: 'ONLY_INCLUDE_IN',
|
||||
};
|
||||
|
||||
const CommandValidators = {
|
||||
[DirectiveCommands.ONLY_INCLUDE_IN]: (params, filePath) => {
|
||||
if (!params || params.length === 0) {
|
||||
throw new Error(
|
||||
getInvalidParamsMessage(
|
||||
filePath,
|
||||
DirectiveCommands.ONLY_INCLUDE_IN,
|
||||
`No params specified.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
params.forEach((param) => {
|
||||
if (!hasKey(BuildType, param)) {
|
||||
/**
|
||||
* Factory function for command validators.
|
||||
*
|
||||
* @param {{ features: import('./remove-fenced-code').Features }} config - Configuration required for validation.
|
||||
* @returns A mapping of command -> validator function.
|
||||
*/
|
||||
function CommandValidators({ features }) {
|
||||
return {
|
||||
[DirectiveCommands.ONLY_INCLUDE_IN]: (params, filePath) => {
|
||||
if (!params || params.length === 0) {
|
||||
throw new Error(
|
||||
getInvalidParamsMessage(
|
||||
filePath,
|
||||
DirectiveCommands.ONLY_INCLUDE_IN,
|
||||
`"${param}" is not a valid build type.`,
|
||||
`No params specified.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
for (const param of params) {
|
||||
if (!features.all.has(param)) {
|
||||
throw new Error(
|
||||
getInvalidParamsMessage(
|
||||
filePath,
|
||||
DirectiveCommands.ONLY_INCLUDE_IN,
|
||||
`"${param}" is not a declared build feature.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Matches lines starting with "///:", and any preceding whitespace, except
|
||||
// newlines. We except newlines to avoid eating blank lines preceding a fenced
|
||||
@ -171,7 +172,8 @@ const fenceSentinelRegex = /^\s*\/\/\/:/u;
|
||||
// At this stage of parsing, we are looking for one of:
|
||||
// - TERMINUS:COMMAND(PARAMS)
|
||||
// - TERMINUS:COMMAND
|
||||
const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u;
|
||||
const directiveParsingRegex =
|
||||
/^([A-Z]+):([A-Z_]+)(?:\(((?:\w[-\w]*,)*\w[-\w]*)\))?$/u;
|
||||
|
||||
/**
|
||||
* Removes fenced code from the given JavaScript source string. "Fenced code"
|
||||
@ -186,7 +188,7 @@ const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u;
|
||||
* Here's an example of a valid fence:
|
||||
*
|
||||
* ```javascript
|
||||
* ///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
* ///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
* console.log('I am Flask.');
|
||||
* ///: END:ONLY_INCLUDE_IN
|
||||
* ```
|
||||
@ -194,12 +196,12 @@ const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u;
|
||||
* For details, please see the documentation.
|
||||
*
|
||||
* @param {string} filePath - The path to the file being transformed.
|
||||
* @param {string} typeOfCurrentBuild - The type of the current build.
|
||||
* @param {import('./remove-fenced-code').Features} features - Features that are currently active.
|
||||
* @param {string} fileContent - The contents of the file being transformed.
|
||||
* @returns {[string, modified]} A tuple of the post-transform file contents and
|
||||
* a boolean indicating whether they were modified.
|
||||
*/
|
||||
function removeFencedCode(filePath, typeOfCurrentBuild, fileContent) {
|
||||
function removeFencedCode(filePath, features, fileContent) {
|
||||
// Do not modify the file if we detect an inline sourcemap. For reasons
|
||||
// yet to be determined, the transform receives every file twice while in
|
||||
// watch mode, the second after Babel has transpiled the file. Babel adds
|
||||
@ -318,6 +320,7 @@ function removeFencedCode(filePath, typeOfCurrentBuild, fileContent) {
|
||||
const splicingIndices = [];
|
||||
let shouldSplice = false;
|
||||
let currentCommand;
|
||||
const commandValidators = CommandValidators({ features });
|
||||
|
||||
for (let i = 0; i < parsedDirectives.length; i++) {
|
||||
const { line, indices, terminus, command, parameters } =
|
||||
@ -335,21 +338,17 @@ function removeFencedCode(filePath, typeOfCurrentBuild, fileContent) {
|
||||
|
||||
currentCommand = command;
|
||||
// Throws an error if the command parameters are invalid
|
||||
CommandValidators[command](parameters, filePath);
|
||||
commandValidators[command](parameters, filePath);
|
||||
|
||||
const validBuildTypes = [
|
||||
typeOfCurrentBuild,
|
||||
...(BuildTypeInheritance[typeOfCurrentBuild] || []),
|
||||
];
|
||||
|
||||
const buildTypeMatches = validBuildTypes.some((validBuildType) =>
|
||||
parameters.includes(validBuildType),
|
||||
const blockIsActive = parameters.some((param) =>
|
||||
features.active.has(param),
|
||||
);
|
||||
|
||||
if (buildTypeMatches) {
|
||||
if (blockIsActive) {
|
||||
shouldSplice = false;
|
||||
} else {
|
||||
shouldSplice = true;
|
||||
|
||||
// Add start index of BEGIN directive line to splicing indices
|
||||
splicingIndices.push(indices[0]);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
const deepFreeze = require('deep-freeze-strict');
|
||||
const { BuildType } = require('../../lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('../../lib/build-type');
|
||||
const {
|
||||
createRemoveFencedCodeTransform,
|
||||
removeFencedCode,
|
||||
@ -14,12 +14,22 @@ jest.mock('./utils', () => ({
|
||||
// file because it takes up a lot of lines and is very distracting.
|
||||
const testData = getTestData();
|
||||
|
||||
const getMinimalFencedCode = (params = 'flask') =>
|
||||
const MAIN_BUILD = 'build-main';
|
||||
const FLASK_BUILD = 'build-flask';
|
||||
|
||||
const getMinimalFencedCode = (params = FLASK_BUILD) =>
|
||||
`///: BEGIN:ONLY_INCLUDE_IN(${params})
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
`;
|
||||
|
||||
const getFeatures = ({ all, active }) => ({
|
||||
all: new Set(all),
|
||||
active: new Set(active),
|
||||
});
|
||||
|
||||
const buildTypesConfig = loadBuildTypesConfig();
|
||||
|
||||
describe('build/transforms/remove-fenced-code', () => {
|
||||
describe('createRemoveFencedCodeTransform', () => {
|
||||
const { lintTransformedFile: lintTransformedFileMock } = transformUtils;
|
||||
@ -29,15 +39,14 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
lintTransformedFileMock.mockImplementation(() => Promise.resolve());
|
||||
});
|
||||
|
||||
it('rejects invalid build types', () => {
|
||||
expect(() => createRemoveFencedCodeTransform('foobar')).toThrow(
|
||||
/received unrecognized build type "foobar".$/u,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns a PassThrough stream for files with ignored extensions', async () => {
|
||||
const fileContent = '"Valid JSON content"\n';
|
||||
const stream = createRemoveFencedCodeTransform('main')('file.json');
|
||||
const stream = createRemoveFencedCodeTransform(
|
||||
getFeatures({
|
||||
active: [MAIN_BUILD],
|
||||
all: [MAIN_BUILD],
|
||||
}),
|
||||
)('file.json');
|
||||
let streamOutput = '';
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@ -60,7 +69,12 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
const filePrefix = '// A comment\n';
|
||||
const fileContent = filePrefix.concat(getMinimalFencedCode());
|
||||
|
||||
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
|
||||
const stream = createRemoveFencedCodeTransform(
|
||||
getFeatures({
|
||||
active: [MAIN_BUILD],
|
||||
all: [MAIN_BUILD, FLASK_BUILD],
|
||||
}),
|
||||
)(mockJsFileName);
|
||||
let streamOutput = '';
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@ -92,7 +106,12 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
.filter((line) => line !== '')
|
||||
.map((line) => `${line}\n`);
|
||||
|
||||
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
|
||||
const stream = createRemoveFencedCodeTransform(
|
||||
getFeatures({
|
||||
active: [MAIN_BUILD],
|
||||
all: [MAIN_BUILD, FLASK_BUILD],
|
||||
}),
|
||||
)(mockJsFileName);
|
||||
let streamOutput = '';
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@ -116,9 +135,14 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
});
|
||||
|
||||
it('handles file with fences that is unmodified by the transform', async () => {
|
||||
const fileContent = getMinimalFencedCode('main');
|
||||
const fileContent = getMinimalFencedCode(MAIN_BUILD);
|
||||
|
||||
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
|
||||
const stream = createRemoveFencedCodeTransform(
|
||||
getFeatures({
|
||||
active: [MAIN_BUILD],
|
||||
all: [MAIN_BUILD],
|
||||
}),
|
||||
)(mockJsFileName);
|
||||
let streamOutput = '';
|
||||
|
||||
await new Promise((resolve) => {
|
||||
@ -141,7 +165,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
const fileContent = filePrefix.concat(getMinimalFencedCode());
|
||||
|
||||
const stream = createRemoveFencedCodeTransform(
|
||||
'main',
|
||||
getFeatures({ all: [MAIN_BUILD, FLASK_BUILD], active: [MAIN_BUILD] }),
|
||||
false,
|
||||
)(mockJsFileName);
|
||||
let streamOutput = '';
|
||||
@ -166,7 +190,9 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
'///: END:ONLY_INCLUDE_IN',
|
||||
);
|
||||
|
||||
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
|
||||
const stream = createRemoveFencedCodeTransform(
|
||||
getFeatures({ all: [MAIN_BUILD, FLASK_BUILD], active: [MAIN_BUILD] }),
|
||||
)(mockJsFileName);
|
||||
|
||||
await new Promise((resolve) => {
|
||||
stream.on('error', (error) => {
|
||||
@ -191,7 +217,9 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
const filePrefix = '// A comment\n';
|
||||
const fileContent = filePrefix.concat(getMinimalFencedCode());
|
||||
|
||||
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
|
||||
const stream = createRemoveFencedCodeTransform(
|
||||
getFeatures({ all: [FLASK_BUILD], active: [] }),
|
||||
)(mockJsFileName);
|
||||
|
||||
await new Promise((resolve) => {
|
||||
stream.on('error', (error) => {
|
||||
@ -213,12 +241,15 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
const mockFileName = 'file.js';
|
||||
|
||||
// Valid inputs
|
||||
Object.keys(BuildType).forEach((buildType) => {
|
||||
['main', 'flask', 'beta'].forEach((buildType) => {
|
||||
const active = buildTypesConfig.buildTypes[buildType].features;
|
||||
const all = Object.keys(buildTypesConfig.features);
|
||||
const features = getFeatures({ all, active });
|
||||
it(`transforms file with fences for build type "${buildType}"`, () => {
|
||||
expect(
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
buildType,
|
||||
features,
|
||||
testData.validInputs.withFences,
|
||||
),
|
||||
).toStrictEqual(testData.validOutputs[buildType]);
|
||||
@ -226,15 +257,15 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
buildType,
|
||||
features,
|
||||
testData.validInputs.extraContentWithFences,
|
||||
),
|
||||
).toStrictEqual(testData.validOutputsWithExtraContent[buildType]);
|
||||
|
||||
// Ensure that the minimal input template is in fact valid
|
||||
const minimalInput = getMinimalFencedCode(buildType);
|
||||
const minimalInput = getMinimalFencedCode(`build-${buildType}`);
|
||||
expect(
|
||||
removeFencedCode(mockFileName, buildType, minimalInput),
|
||||
removeFencedCode(mockFileName, features, minimalInput),
|
||||
).toStrictEqual([minimalInput, false]);
|
||||
});
|
||||
|
||||
@ -242,7 +273,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
buildType,
|
||||
features,
|
||||
testData.validInputs.withoutFences,
|
||||
),
|
||||
).toStrictEqual([testData.validInputs.withoutFences, false]);
|
||||
@ -250,7 +281,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
buildType,
|
||||
features,
|
||||
testData.validInputs.extraContentWithoutFences,
|
||||
),
|
||||
).toStrictEqual([
|
||||
@ -265,29 +296,17 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getMinimalFencedCode('main'),
|
||||
getFeatures({
|
||||
active: [FLASK_BUILD],
|
||||
all: [FLASK_BUILD, MAIN_BUILD],
|
||||
}),
|
||||
getMinimalFencedCode(MAIN_BUILD),
|
||||
),
|
||||
).toStrictEqual(['', true]);
|
||||
});
|
||||
|
||||
it('keeps fences with inherited build types', () => {
|
||||
// Desktop inherits from the flask build type
|
||||
const minimalCode =
|
||||
getMinimalFencedCode(BuildType.flask) +
|
||||
getMinimalFencedCode(BuildType.desktop);
|
||||
|
||||
expect(
|
||||
removeFencedCode(mockFileName, BuildType.desktop, minimalCode),
|
||||
).toStrictEqual([minimalCode, false]);
|
||||
|
||||
expect(
|
||||
removeFencedCode(mockFileName, BuildType.flask, minimalCode),
|
||||
).toStrictEqual([getMinimalFencedCode(BuildType.flask), true]);
|
||||
});
|
||||
|
||||
it('ignores sentinels preceded by non-whitespace', () => {
|
||||
const validBeginDirective = '///: BEGIN:ONLY_INCLUDE_IN(flask)\n';
|
||||
const validBeginDirective = '///: BEGIN:ONLY_INCLUDE_IN(build-flask)\n';
|
||||
const ignoredLines = [
|
||||
`a ${validBeginDirective}`,
|
||||
`2 ${validBeginDirective}`,
|
||||
@ -299,8 +318,11 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getMinimalFencedCode('main').concat(ignoredLine),
|
||||
getFeatures({
|
||||
active: [FLASK_BUILD],
|
||||
all: [FLASK_BUILD, MAIN_BUILD],
|
||||
}),
|
||||
getMinimalFencedCode(MAIN_BUILD).concat(ignoredLine),
|
||||
),
|
||||
).toStrictEqual([ignoredLine, true]);
|
||||
|
||||
@ -311,7 +333,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
modifiedInputWithoutFences,
|
||||
),
|
||||
).toStrictEqual([modifiedInputWithoutFences, false]);
|
||||
@ -341,7 +363,11 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
|
||||
inputs.forEach((input) => {
|
||||
expect(() =>
|
||||
removeFencedCode(mockFileName, BuildType.flask, input),
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
input,
|
||||
),
|
||||
).toThrow(
|
||||
`Empty fence found in file "${mockFileName}":\n${emptyFence}`,
|
||||
);
|
||||
@ -368,7 +394,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
getMinimalFencedCode().replace(
|
||||
fenceSentinelAndTerminusRegex,
|
||||
replacement,
|
||||
@ -382,53 +408,53 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
|
||||
it('rejects malformed BEGIN directives', () => {
|
||||
// This is the first line of the minimal input template
|
||||
const directiveString = '///: BEGIN:ONLY_INCLUDE_IN(flask)';
|
||||
const directiveString = '///: BEGIN:ONLY_INCLUDE_IN(build-flask)';
|
||||
|
||||
const replacements = [
|
||||
// Invalid terminus
|
||||
'///: BE_GIN:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BE6IN:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BEGIN7:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BeGIN:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BE3:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BEG-IN:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BEG N:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BE_GIN:BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'///: BE6IN:BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'///: BEGIN7:BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'///: BeGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'///: BE3:BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'///: BEG-IN:BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'///: BEG N:BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
|
||||
// Invalid commands
|
||||
'///: BEGIN:ONLY-INCLUDE_IN(flask)',
|
||||
'///: BEGIN:ONLY_INCLUDE:IN(flask)',
|
||||
'///: BEGIN:ONL6_INCLUDE_IN(flask)',
|
||||
'///: BEGIN:ONLY_IN@LUDE_IN(flask)',
|
||||
'///: BEGIN:ONLy_INCLUDE_IN(flask)',
|
||||
'///: BEGIN:ONLy_INCLUDE_IN(build-flask)',
|
||||
'///: BEGIN:ONLY INCLUDE_IN(flask)',
|
||||
|
||||
// Invalid parameters
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(,flask)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask,)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask,,main)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask,)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask,,main)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(,)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN()',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN( )',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask]',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask]',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN[flask)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask.main)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask,@)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask.main)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask,@)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(fla k)',
|
||||
|
||||
// Stuff after the directive
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask) A',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask) 9',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask)A',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask)9',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask)_',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask))',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask) A',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask) 9',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)A',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)9',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)_',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask))',
|
||||
];
|
||||
|
||||
replacements.forEach((replacement) => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
getMinimalFencedCode().replace(directiveString, replacement),
|
||||
),
|
||||
).toThrow(
|
||||
@ -474,7 +500,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
getMinimalFencedCode().replace(directiveString, replacement),
|
||||
),
|
||||
).toThrow(
|
||||
@ -488,14 +514,14 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
|
||||
it('rejects files with uneven number of fence lines', () => {
|
||||
const additions = [
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(flask)',
|
||||
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'///: END:ONLY_INCLUDE_IN',
|
||||
];
|
||||
additions.forEach((addition) => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
getMinimalFencedCode().concat(addition),
|
||||
),
|
||||
).toThrow(
|
||||
@ -515,7 +541,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
getMinimalFencedCode().replace(validTerminus, replacement),
|
||||
),
|
||||
).toThrow(
|
||||
@ -539,7 +565,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
getMinimalFencedCode().replace(validCommand, replacement),
|
||||
),
|
||||
).toThrow(
|
||||
@ -557,10 +583,30 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
|
||||
it('rejects invalid command parameters', () => {
|
||||
const testCases = [
|
||||
['bar', ['bar', 'flask,bar', 'flask,beta,main,bar']],
|
||||
['Foo', ['Foo', 'flask,Foo', 'flask,beta,main,Foo']],
|
||||
['b3ta', ['b3ta', 'flask,b3ta', 'flask,beta,main,b3ta']],
|
||||
['bEta', ['bEta', 'flask,bEta', 'flask,beta,main,bEta']],
|
||||
[
|
||||
'bar',
|
||||
['bar', 'build-flask,bar', 'build-flask,build-beta,build-main,bar'],
|
||||
],
|
||||
[
|
||||
'Foo',
|
||||
['Foo', 'build-flask,Foo', 'build-flask,build-beta,build-main,Foo'],
|
||||
],
|
||||
[
|
||||
'b3ta',
|
||||
[
|
||||
'b3ta',
|
||||
'build-flask,b3ta',
|
||||
'build-flask,build-beta,build-main,b3ta',
|
||||
],
|
||||
],
|
||||
[
|
||||
'bEta',
|
||||
[
|
||||
'bEta',
|
||||
'build-flask,bEta',
|
||||
'build-flask,build-beta,build-main,bEta',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
testCases.forEach(([invalidParam, replacements]) => {
|
||||
@ -568,11 +614,17 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({
|
||||
active: [FLASK_BUILD],
|
||||
all: [FLASK_BUILD, MAIN_BUILD, 'build-beta'],
|
||||
}),
|
||||
getMinimalFencedCode(replacement),
|
||||
),
|
||||
).toThrow(
|
||||
new RegExp(`"${invalidParam}" is not a valid build type.$`, 'u'),
|
||||
new RegExp(
|
||||
`"${invalidParam}" is not a declared build feature.$`,
|
||||
'u',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -581,7 +633,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
getMinimalFencedCode('').replace('()', ''),
|
||||
),
|
||||
).toThrow(/No params specified.$/u);
|
||||
@ -589,7 +641,9 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
|
||||
it('rejects directive pairs with wrong terminus order', () => {
|
||||
// We need more than one directive pair for this test
|
||||
const input = getMinimalFencedCode().concat(getMinimalFencedCode('beta'));
|
||||
const input = getMinimalFencedCode().concat(
|
||||
getMinimalFencedCode('build-beta'),
|
||||
);
|
||||
|
||||
const expectedBeginError =
|
||||
'The first directive of a pair must be a "BEGIN" directive.';
|
||||
@ -597,17 +651,17 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
'The second directive of a pair must be an "END" directive.';
|
||||
const testCases = [
|
||||
[
|
||||
'BEGIN:ONLY_INCLUDE_IN(flask)',
|
||||
'BEGIN:ONLY_INCLUDE_IN(build-flask)',
|
||||
'END:ONLY_INCLUDE_IN',
|
||||
expectedBeginError,
|
||||
],
|
||||
[
|
||||
/END:ONLY_INCLUDE_IN/mu,
|
||||
'BEGIN:ONLY_INCLUDE_IN(main)',
|
||||
'BEGIN:ONLY_INCLUDE_IN(build-main)',
|
||||
expectedEndError,
|
||||
],
|
||||
[
|
||||
'BEGIN:ONLY_INCLUDE_IN(beta)',
|
||||
'BEGIN:ONLY_INCLUDE_IN(build-beta)',
|
||||
'END:ONLY_INCLUDE_IN',
|
||||
expectedBeginError,
|
||||
],
|
||||
@ -617,7 +671,7 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
expect(() =>
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
BuildType.flask,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
input.replace(target, replacement),
|
||||
),
|
||||
).toThrow(expectedError);
|
||||
@ -631,7 +685,11 @@ describe('build/transforms/remove-fenced-code', () => {
|
||||
'\n//# sourceMappingURL=as32e32wcwc2234f2ew32cnin4243f4nv9nsdoivnxzoivnd',
|
||||
);
|
||||
expect(
|
||||
removeFencedCode(mockFileName, BuildType.flask, input),
|
||||
removeFencedCode(
|
||||
mockFileName,
|
||||
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
|
||||
input,
|
||||
),
|
||||
).toStrictEqual([input, false]);
|
||||
});
|
||||
|
||||
@ -644,14 +702,14 @@ function getTestData() {
|
||||
const data = {
|
||||
validInputs: {
|
||||
withFences: `
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
|
||||
Conditionally_Included
|
||||
@ -660,7 +718,7 @@ Conditionally_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
@ -678,7 +736,7 @@ Conditionally_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
|
||||
@ -690,14 +748,14 @@ Conditionally_Included
|
||||
`,
|
||||
|
||||
extraContentWithFences: `
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
|
||||
Conditionally_Included
|
||||
@ -706,7 +764,7 @@ Conditionally_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
@ -723,7 +781,7 @@ Conditionally_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
|
||||
@ -781,14 +839,14 @@ Always_Included
|
||||
validOutputs: {
|
||||
beta: [
|
||||
`
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
|
||||
Conditionally_Included
|
||||
@ -810,14 +868,14 @@ Always_Included
|
||||
],
|
||||
flask: [
|
||||
`
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
|
||||
Conditionally_Included
|
||||
@ -826,7 +884,7 @@ Conditionally_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
@ -836,16 +894,25 @@ Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
`,
|
||||
true,
|
||||
false,
|
||||
],
|
||||
mmi: [
|
||||
`
|
||||
@ -872,14 +939,14 @@ Always_Included
|
||||
validOutputsWithExtraContent: {
|
||||
beta: [
|
||||
`
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
|
||||
Conditionally_Included
|
||||
@ -904,14 +971,14 @@ Always_Included
|
||||
],
|
||||
flask: [
|
||||
`
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
|
||||
Conditionally_Included
|
||||
|
||||
Conditionally_Included
|
||||
@ -920,7 +987,7 @@ Conditionally_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
@ -930,10 +997,19 @@ Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
Conditionally_Included
|
||||
Conditionally_Included
|
||||
|
||||
@ -942,7 +1018,7 @@ Always_Included
|
||||
Always_Included
|
||||
Always_Included
|
||||
`,
|
||||
true,
|
||||
false,
|
||||
],
|
||||
mmi: [
|
||||
`
|
||||
|
@ -1,6 +1,6 @@
|
||||
const path = require('path');
|
||||
const semver = require('semver');
|
||||
const { BuildType } = require('../lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('../lib/build-type');
|
||||
const { BUILD_TARGETS, ENVIRONMENT } = require('./constants');
|
||||
|
||||
/**
|
||||
@ -48,6 +48,8 @@ function getBrowserVersionMap(platforms, version) {
|
||||
let buildType, buildVersionSummary, buildVersion;
|
||||
if (prerelease) {
|
||||
[buildType, buildVersionSummary] = prerelease;
|
||||
// TODO(ritave): Figure out why the version 10.25.0-beta.1-flask.0 in the below comment is even possible
|
||||
// since those are two different build types
|
||||
// The version could be version: '10.25.0-beta.1-flask.0',
|
||||
// That results in buildVersionSummary becomes 1-flask
|
||||
// And we only want 1 from this string
|
||||
@ -57,14 +59,7 @@ function getBrowserVersionMap(platforms, version) {
|
||||
: buildVersionSummary;
|
||||
if (!String(buildVersion).match(/^\d+$/u)) {
|
||||
throw new Error(`Invalid prerelease build version: '${buildVersion}'`);
|
||||
} else if (
|
||||
![
|
||||
BuildType.beta,
|
||||
BuildType.flask,
|
||||
BuildType.desktop,
|
||||
BuildType.mmi,
|
||||
].includes(buildType)
|
||||
) {
|
||||
} else if (!loadBuildTypesConfig().buildTypes[buildType].isPrerelease) {
|
||||
throw new Error(`Invalid prerelease build type: ${buildType}`);
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
const {
|
||||
EXCLUDE_E2E_TESTS_REGEX,
|
||||
filterDiffAdditions,
|
||||
filterDiffByFilePath,
|
||||
hasNumberOfCodeBlocksIncreased,
|
||||
} = require('./shared');
|
||||
|
||||
function checkMochaSyntax(diff) {
|
||||
const ruleHeading = 'favor-jest-instead-of-mocha';
|
||||
const codeBlocks = [
|
||||
"import { strict as assert } from 'assert';",
|
||||
'assert.deepEqual',
|
||||
'assert.equal',
|
||||
'assert.rejects',
|
||||
'assert.strictEqual',
|
||||
'sinon.',
|
||||
];
|
||||
|
||||
console.log(`\nChecking ${ruleHeading}...`);
|
||||
|
||||
const diffByFilePath = filterDiffByFilePath(diff, EXCLUDE_E2E_TESTS_REGEX);
|
||||
const diffAdditions = filterDiffAdditions(diffByFilePath);
|
||||
const hashmap = hasNumberOfCodeBlocksIncreased(diffAdditions, codeBlocks);
|
||||
|
||||
Object.keys(hashmap).forEach((key) => {
|
||||
if (hashmap[key]) {
|
||||
console.error(`Number of occurences of "${key}" have increased.`);
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.values(hashmap).includes(true)) {
|
||||
console.error(
|
||||
`...changes have not been accepted by the fitness function.\nFor more info, see: https://github.com/MetaMask/metamask-extension/blob/develop/docs/testing.md#${ruleHeading}`,
|
||||
);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log(
|
||||
`...number of occurences has not increased for any code block.`,
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { checkMochaSyntax };
|
105
development/fitness-functions/common/constants.test.ts
Normal file
105
development/fitness-functions/common/constants.test.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { EXCLUDE_E2E_TESTS_REGEX, SHARED_FOLDER_JS_REGEX } from './constants';
|
||||
|
||||
describe('Regular Expressions used in Fitness Functions', (): void => {
|
||||
describe(`EXCLUDE_E2E_TESTS_REGEX "${EXCLUDE_E2E_TESTS_REGEX}"`, (): void => {
|
||||
const PATHS_IT_SHOULD_MATCH = [
|
||||
'file.js',
|
||||
'path/file.js',
|
||||
'much/longer/path/file.js',
|
||||
'file.ts',
|
||||
'path/file.ts',
|
||||
'much/longer/path/file.ts',
|
||||
'file.jsx',
|
||||
'path/file.jsx',
|
||||
'much/longer/path/file.jsx',
|
||||
];
|
||||
|
||||
const PATHS_IT_SHOULD_NOT_MATCH = [
|
||||
// any without JS, TS, JSX or TSX extension
|
||||
'file',
|
||||
'file.extension',
|
||||
'path/file.extension',
|
||||
'much/longer/path/file.extension',
|
||||
// any in the test/e2e directory
|
||||
'test/e2e/file',
|
||||
'test/e2e/file.extension',
|
||||
'test/e2e/path/file.extension',
|
||||
'test/e2e/much/longer/path/file.extension',
|
||||
'test/e2e/file.js',
|
||||
'test/e2e/path/file.ts',
|
||||
'test/e2e/much/longer/path/file.jsx',
|
||||
'test/e2e/much/longer/path/file.tsx',
|
||||
// any in the development/fitness-functions directory
|
||||
'development/fitness-functions/file',
|
||||
'development/fitness-functions/file.extension',
|
||||
'development/fitness-functions/path/file.extension',
|
||||
'development/fitness-functions/much/longer/path/file.extension',
|
||||
'development/fitness-functions/file.js',
|
||||
'development/fitness-functions/path/file.ts',
|
||||
'development/fitness-functions/much/longer/path/file.jsx',
|
||||
'development/fitness-functions/much/longer/path/file.tsx',
|
||||
];
|
||||
|
||||
describe('included paths', (): void => {
|
||||
PATHS_IT_SHOULD_MATCH.forEach((path: string): void => {
|
||||
it(`should match "${path}"`, (): void => {
|
||||
const result = new RegExp(EXCLUDE_E2E_TESTS_REGEX, 'u').test(path);
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('excluded paths', (): void => {
|
||||
PATHS_IT_SHOULD_NOT_MATCH.forEach((path: string): void => {
|
||||
it(`should not match "${path}"`, (): void => {
|
||||
const result = new RegExp(EXCLUDE_E2E_TESTS_REGEX, 'u').test(path);
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`SHARED_FOLDER_JS_REGEX "${SHARED_FOLDER_JS_REGEX}"`, (): void => {
|
||||
const PATHS_IT_SHOULD_MATCH = [
|
||||
'shared/file.js',
|
||||
'shared/path/file.js',
|
||||
'shared/much/longer/path/file.js',
|
||||
'shared/file.jsx',
|
||||
'shared/path/file.jsx',
|
||||
'shared/much/longer/path/file.jsx',
|
||||
];
|
||||
|
||||
const PATHS_IT_SHOULD_NOT_MATCH = [
|
||||
// any without JS or JSX extension
|
||||
'file',
|
||||
'file.extension',
|
||||
'path/file.extension',
|
||||
'much/longer/path/file.extension',
|
||||
'file.ts',
|
||||
'path/file.ts',
|
||||
'much/longer/path/file.tsx',
|
||||
];
|
||||
|
||||
describe('included paths', (): void => {
|
||||
PATHS_IT_SHOULD_MATCH.forEach((path: string): void => {
|
||||
it(`should match "${path}"`, (): void => {
|
||||
const result = new RegExp(SHARED_FOLDER_JS_REGEX, 'u').test(path);
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('excluded paths', (): void => {
|
||||
PATHS_IT_SHOULD_NOT_MATCH.forEach((path: string): void => {
|
||||
it(`should not match "${path}"`, (): void => {
|
||||
const result = new RegExp(SHARED_FOLDER_JS_REGEX, 'u').test(path);
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
15
development/fitness-functions/common/constants.ts
Normal file
15
development/fitness-functions/common/constants.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// include JS, TS, JSX, TSX files only excluding files in the e2e tests and
|
||||
// fitness functions directories
|
||||
const EXCLUDE_E2E_TESTS_REGEX =
|
||||
'^(?!test/e2e)(?!development/fitness).*.(js|ts|jsx|tsx)$';
|
||||
|
||||
// include JS and JSX files in the shared directory only
|
||||
const SHARED_FOLDER_JS_REGEX = '^(shared).*.(js|jsx)$';
|
||||
|
||||
enum AUTOMATION_TYPE {
|
||||
CI = 'ci',
|
||||
PRE_COMMIT_HOOK = 'pre-commit-hook',
|
||||
PRE_PUSH_HOOK = 'pre-push-hook',
|
||||
}
|
||||
|
||||
export { EXCLUDE_E2E_TESTS_REGEX, SHARED_FOLDER_JS_REGEX, AUTOMATION_TYPE };
|
54
development/fitness-functions/common/get-diff.ts
Normal file
54
development/fitness-functions/common/get-diff.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import { AUTOMATION_TYPE } from './constants';
|
||||
|
||||
function getDiffByAutomationType(
|
||||
automationType: AUTOMATION_TYPE,
|
||||
): string | undefined {
|
||||
if (!Object.values(AUTOMATION_TYPE).includes(automationType)) {
|
||||
console.error('Invalid automation type.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let diff;
|
||||
if (automationType === AUTOMATION_TYPE.CI) {
|
||||
const optionalArguments = process.argv.slice(3);
|
||||
if (optionalArguments.length !== 1) {
|
||||
console.error('Invalid number of arguments.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
diff = getCIDiff(optionalArguments[0]);
|
||||
} else if (automationType === AUTOMATION_TYPE.PRE_COMMIT_HOOK) {
|
||||
diff = getPreCommitHookDiff();
|
||||
} else if (automationType === AUTOMATION_TYPE.PRE_PUSH_HOOK) {
|
||||
diff = getPrePushHookDiff();
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
function getCIDiff(path: string): string {
|
||||
return fs.readFileSync(path, {
|
||||
encoding: 'utf8',
|
||||
flag: 'r',
|
||||
});
|
||||
}
|
||||
|
||||
function getPreCommitHookDiff(): string {
|
||||
return execSync(`git diff --cached HEAD`).toString().trim();
|
||||
}
|
||||
|
||||
function getPrePushHookDiff(): string {
|
||||
const currentBranch = execSync(`git rev-parse --abbrev-ref HEAD`)
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
return execSync(
|
||||
`git diff ${currentBranch} origin/${currentBranch} -- . ':(exclude)development/fitness-functions/'`,
|
||||
)
|
||||
.toString()
|
||||
.trim();
|
||||
}
|
||||
|
||||
export { getDiffByAutomationType };
|
@ -1,46 +1,48 @@
|
||||
const {
|
||||
EXCLUDE_E2E_TESTS_REGEX,
|
||||
filterDiffAdditions,
|
||||
import {
|
||||
filterDiffLineAdditions,
|
||||
hasNumberOfCodeBlocksIncreased,
|
||||
filterDiffByFilePath,
|
||||
} = require('./shared');
|
||||
filterDiffFileCreations,
|
||||
} from './shared';
|
||||
import { generateCreateFileDiff, generateModifyFilesDiff } from './test-data';
|
||||
|
||||
const generateCreateFileDiff = (filePath, content) => `
|
||||
diff --git a/${filePath} b/${filePath}
|
||||
new file mode 100644
|
||||
index 000000000..30d74d258
|
||||
--- /dev/null
|
||||
+++ b/${filePath}
|
||||
@@ -0,0 +1 @@
|
||||
+${content}
|
||||
`;
|
||||
|
||||
const generateModifyFilesDiff = (filePath, addition, removal) => `
|
||||
diff --git a/${filePath} b/${filePath}
|
||||
index 57d5de75c..808d8ba37 100644
|
||||
--- a/${filePath}
|
||||
+++ b/${filePath}
|
||||
@@ -1,3 +1,8 @@
|
||||
+${addition}
|
||||
@@ -34,33 +39,4 @@
|
||||
-${removal}
|
||||
`;
|
||||
|
||||
describe('filterDiffAdditions()', () => {
|
||||
it('should return code additions in the diff', () => {
|
||||
describe('filterDiffLineAdditions()', (): void => {
|
||||
it('should return code additions in the diff', (): void => {
|
||||
const testFilePath = 'new-file.js';
|
||||
const testAddition = 'foo';
|
||||
const testFileDiff = generateCreateFileDiff(testFilePath, testAddition);
|
||||
|
||||
const actualResult = filterDiffAdditions(testFileDiff);
|
||||
const actualResult = filterDiffLineAdditions(testFileDiff);
|
||||
const expectedResult = `+${testAddition}`;
|
||||
|
||||
expect(actualResult).toStrictEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasNumberOfCodeBlocksIncreased()', () => {
|
||||
it('should show which code blocks have increased', () => {
|
||||
describe('filterDiffFileCreations()', (): void => {
|
||||
it('should return code additions in the diff', (): void => {
|
||||
const testFileDiff = [
|
||||
generateModifyFilesDiff('new-file.ts', 'foo', 'bar'),
|
||||
generateCreateFileDiff('old-file.js', 'ping'),
|
||||
generateModifyFilesDiff('old-file.jsx', 'yin', 'yang'),
|
||||
].join('');
|
||||
|
||||
const actualResult = filterDiffFileCreations(testFileDiff);
|
||||
|
||||
expect(actualResult).toMatchInlineSnapshot(`
|
||||
"diff --git a/old-file.js b/old-file.js
|
||||
new file mode 100644
|
||||
index 000000000..30d74d258
|
||||
--- /dev/null
|
||||
+++ b/old-file.js
|
||||
@@ -0,0 +1 @@
|
||||
+ping"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasNumberOfCodeBlocksIncreased()', (): void => {
|
||||
it('should show which code blocks have increased', (): void => {
|
||||
const testDiffFragment = `
|
||||
+foo
|
||||
+bar
|
||||
@ -57,14 +59,14 @@ describe('hasNumberOfCodeBlocksIncreased()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterDiffByFilePath()', () => {
|
||||
describe('filterDiffByFilePath()', (): void => {
|
||||
const testFileDiff = [
|
||||
generateModifyFilesDiff('new-file.ts', 'foo', 'bar'),
|
||||
generateModifyFilesDiff('old-file.js', 'ping', 'pong'),
|
||||
generateModifyFilesDiff('old-file.jsx', 'yin', 'yang'),
|
||||
].join('');
|
||||
|
||||
it('should return the right diff for a generic matcher', () => {
|
||||
it('should return the right diff for a generic matcher', (): void => {
|
||||
const actualResult = filterDiffByFilePath(
|
||||
testFileDiff,
|
||||
'.*/.*.(js|ts)$|.*.(js|ts)$',
|
||||
@ -90,7 +92,7 @@ describe('filterDiffByFilePath()', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the right diff for a specific file in any dir matcher', () => {
|
||||
it('should return the right diff for a specific file in any dir matcher', (): void => {
|
||||
const actualResult = filterDiffByFilePath(testFileDiff, '.*old-file.js$');
|
||||
|
||||
expect(actualResult).toMatchInlineSnapshot(`
|
||||
@ -105,7 +107,7 @@ describe('filterDiffByFilePath()', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the right diff for a multiple file extension (OR) matcher', () => {
|
||||
it('should return the right diff for a multiple file extension (OR) matcher', (): void => {
|
||||
const actualResult = filterDiffByFilePath(
|
||||
testFileDiff,
|
||||
'^(./)*old-file.(js|ts|jsx)$',
|
||||
@ -131,7 +133,7 @@ describe('filterDiffByFilePath()', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the right diff for a file name negation matcher', () => {
|
||||
it('should return the right diff for a file name negation matcher', (): void => {
|
||||
const actualResult = filterDiffByFilePath(
|
||||
testFileDiff,
|
||||
'^(?!.*old-file.js$).*.[a-zA-Z]+$',
|
||||
@ -157,51 +159,3 @@ describe('filterDiffByFilePath()', () => {
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`EXCLUDE_E2E_TESTS_REGEX "${EXCLUDE_E2E_TESTS_REGEX}"`, () => {
|
||||
const PATHS_IT_SHOULD_MATCH = [
|
||||
'file.js',
|
||||
'path/file.js',
|
||||
'much/longer/path/file.js',
|
||||
'file.ts',
|
||||
'path/file.ts',
|
||||
'much/longer/path/file.ts',
|
||||
'file.jsx',
|
||||
'path/file.jsx',
|
||||
'much/longer/path/file.jsx',
|
||||
];
|
||||
|
||||
const PATHS_IT_SHOULD_NOT_MATCH = [
|
||||
'test/e2e/file',
|
||||
'test/e2e/file.extension',
|
||||
'test/e2e/path/file.extension',
|
||||
'test/e2e/much/longer/path/file.extension',
|
||||
'test/e2e/file.js',
|
||||
'test/e2e/path/file.ts',
|
||||
'test/e2e/much/longer/path/file.jsx',
|
||||
'file',
|
||||
'file.extension',
|
||||
'path/file.extension',
|
||||
'much/longer/path/file.extension',
|
||||
];
|
||||
|
||||
describe('included paths', () => {
|
||||
PATHS_IT_SHOULD_MATCH.forEach((path) => {
|
||||
it(`should match "${path}"`, () => {
|
||||
const result = new RegExp(EXCLUDE_E2E_TESTS_REGEX, 'u').test(path);
|
||||
|
||||
expect(result).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('excluded paths', () => {
|
||||
PATHS_IT_SHOULD_NOT_MATCH.forEach((path) => {
|
||||
it(`should not match "${path}"`, () => {
|
||||
const result = new RegExp(EXCLUDE_E2E_TESTS_REGEX, 'u').test(path);
|
||||
|
||||
expect(result).toStrictEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,6 +1,4 @@
|
||||
const EXCLUDE_E2E_TESTS_REGEX = '^(?!test/e2e/).*.(js|ts|jsx)$';
|
||||
|
||||
function filterDiffByFilePath(diff, regex) {
|
||||
function filterDiffByFilePath(diff: string, regex: string): string {
|
||||
// split by `diff --git` and remove the first element which is empty
|
||||
const diffBlocks = diff.split(`diff --git`).slice(1);
|
||||
|
||||
@ -34,7 +32,7 @@ function filterDiffByFilePath(diff, regex) {
|
||||
return filteredDiff;
|
||||
}
|
||||
|
||||
function filterDiffAdditions(diff) {
|
||||
function filterDiffLineAdditions(diff: string): string {
|
||||
const diffLines = diff.split('\n');
|
||||
|
||||
const diffAdditionLines = diffLines.filter((line) => {
|
||||
@ -46,10 +44,36 @@ function filterDiffAdditions(diff) {
|
||||
return diffAdditionLines.join('/n');
|
||||
}
|
||||
|
||||
function hasNumberOfCodeBlocksIncreased(diffFragment, codeBlocks) {
|
||||
function filterDiffFileCreations(diff: string): string {
|
||||
// split by `diff --git` and remove the first element which is empty
|
||||
const diffBlocks = diff.split(`diff --git`).slice(1);
|
||||
|
||||
const filteredDiff = diffBlocks
|
||||
.map((block) => block.trim())
|
||||
.filter((block) => {
|
||||
const isFileCreationLine =
|
||||
block
|
||||
// get the second line of the block which has the file mode
|
||||
.split('\n')[1]
|
||||
.trim()
|
||||
.substring(0, 13) === 'new file mode';
|
||||
|
||||
return isFileCreationLine;
|
||||
})
|
||||
// prepend `git --diff` to each block
|
||||
.map((block) => `diff --git ${block}`)
|
||||
.join('\n');
|
||||
|
||||
return filteredDiff;
|
||||
}
|
||||
|
||||
function hasNumberOfCodeBlocksIncreased(
|
||||
diffFragment: string,
|
||||
codeBlocks: string[],
|
||||
): { [codeBlock: string]: boolean } {
|
||||
const diffLines = diffFragment.split('\n');
|
||||
|
||||
const codeBlockFound = {};
|
||||
const codeBlockFound: { [codeBlock: string]: boolean } = {};
|
||||
|
||||
for (const codeBlock of codeBlocks) {
|
||||
codeBlockFound[codeBlock] = false;
|
||||
@ -65,9 +89,9 @@ function hasNumberOfCodeBlocksIncreased(diffFragment, codeBlocks) {
|
||||
return codeBlockFound;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
EXCLUDE_E2E_TESTS_REGEX,
|
||||
export {
|
||||
filterDiffByFilePath,
|
||||
filterDiffAdditions,
|
||||
filterDiffLineAdditions,
|
||||
filterDiffFileCreations,
|
||||
hasNumberOfCodeBlocksIncreased,
|
||||
};
|
40
development/fitness-functions/common/test-data.ts
Normal file
40
development/fitness-functions/common/test-data.ts
Normal file
@ -0,0 +1,40 @@
|
||||
const generateCreateFileDiff = (
|
||||
filePath = 'file.txt',
|
||||
content = 'Lorem ipsum',
|
||||
): string => `
|
||||
diff --git a/${filePath} b/${filePath}
|
||||
new file mode 100644
|
||||
index 000000000..30d74d258
|
||||
--- /dev/null
|
||||
+++ b/${filePath}
|
||||
@@ -0,0 +1 @@
|
||||
+${content}
|
||||
`;
|
||||
|
||||
const generateModifyFilesDiff = (
|
||||
filePath = 'file.txt',
|
||||
addition = 'Lorem ipsum',
|
||||
removal = '',
|
||||
): string => {
|
||||
const additionBlock = addition
|
||||
? `
|
||||
@@ -1,3 +1,8 @@
|
||||
+${addition}`.trim()
|
||||
: '';
|
||||
|
||||
const removalBlock = removal
|
||||
? `
|
||||
@@ -34,33 +39,4 @@
|
||||
-${removal}`.trim()
|
||||
: '';
|
||||
|
||||
return `
|
||||
diff --git a/${filePath} b/${filePath}
|
||||
index 57d5de75c..808d8ba37 100644
|
||||
--- a/${filePath}
|
||||
+++ b/${filePath}
|
||||
${additionBlock}
|
||||
${removalBlock}`;
|
||||
};
|
||||
|
||||
export { generateCreateFileDiff, generateModifyFilesDiff };
|
@ -1,53 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { execSync } = require('child_process');
|
||||
const { checkMochaSyntax } = require('./check-mocha-syntax');
|
||||
|
||||
const AUTOMATION_TYPE = Object.freeze({
|
||||
CI: 'ci',
|
||||
PRE_COMMIT_HOOK: 'pre-commit-hook',
|
||||
PRE_PUSH_HOOK: 'pre-push-hook',
|
||||
});
|
||||
|
||||
const automationType = process.argv[2];
|
||||
|
||||
if (automationType === AUTOMATION_TYPE.CI) {
|
||||
const optionalArguments = process.argv.slice(3);
|
||||
if (optionalArguments.length !== 1) {
|
||||
console.error('Invalid number of arguments.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const diff = fs.readFileSync(optionalArguments[0], {
|
||||
encoding: 'utf8',
|
||||
flag: 'r',
|
||||
});
|
||||
|
||||
checkMochaSyntax(diff);
|
||||
} else if (automationType === AUTOMATION_TYPE.PRE_COMMIT_HOOK) {
|
||||
const diff = getPreCommitHookDiff();
|
||||
|
||||
checkMochaSyntax(diff);
|
||||
} else if (automationType === AUTOMATION_TYPE.PRE_PUSH_HOOK) {
|
||||
const diff = getPrePushHookDiff();
|
||||
|
||||
checkMochaSyntax(diff);
|
||||
} else {
|
||||
console.error('Invalid automation type.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getPreCommitHookDiff() {
|
||||
return execSync(`git diff --cached HEAD`).toString().trim();
|
||||
}
|
||||
|
||||
function getPrePushHookDiff() {
|
||||
const currentBranch = execSync(`git rev-parse --abbrev-ref HEAD`)
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
return execSync(
|
||||
`git diff ${currentBranch} origin/${currentBranch} -- . ':(exclude)development/fitness-functions/'`,
|
||||
)
|
||||
.toString()
|
||||
.trim();
|
||||
}
|
11
development/fitness-functions/index.ts
Normal file
11
development/fitness-functions/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { AUTOMATION_TYPE } from './common/constants';
|
||||
import { getDiffByAutomationType } from './common/get-diff';
|
||||
import { IRule, RULES, runFitnessFunctionRule } from './rules';
|
||||
|
||||
const automationType: AUTOMATION_TYPE = process.argv[2] as AUTOMATION_TYPE;
|
||||
|
||||
const diff = getDiffByAutomationType(automationType);
|
||||
|
||||
if (typeof diff === 'string') {
|
||||
RULES.forEach((rule: IRule): void => runFitnessFunctionRule(rule, diff));
|
||||
}
|
42
development/fitness-functions/rules/index.ts
Normal file
42
development/fitness-functions/rules/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { preventSinonAssertSyntax } from './sinon-assert-syntax';
|
||||
import { preventJavaScriptFileAdditions } from './javascript-additions';
|
||||
|
||||
const RULES: IRule[] = [
|
||||
{
|
||||
name: "Don't use `sinon` or `assert` in unit tests",
|
||||
fn: preventSinonAssertSyntax,
|
||||
docURL:
|
||||
'https://github.com/MetaMask/metamask-extension/blob/develop/docs/testing.md#favor-jest-instead-of-mocha',
|
||||
},
|
||||
{
|
||||
name: "Don't add JS or JSX files to the `shared` directory",
|
||||
fn: preventJavaScriptFileAdditions,
|
||||
},
|
||||
];
|
||||
|
||||
interface IRule {
|
||||
name: string;
|
||||
fn: (diff: string) => boolean;
|
||||
docURL?: string;
|
||||
}
|
||||
|
||||
function runFitnessFunctionRule(rule: IRule, diff: string): void {
|
||||
const { name, fn, docURL } = rule;
|
||||
console.log(`Checking rule "${name}"...`);
|
||||
|
||||
const hasRulePassed: boolean = fn(diff) as boolean;
|
||||
if (hasRulePassed === true) {
|
||||
console.log(`...OK`);
|
||||
} else {
|
||||
console.log(`...FAILED. Changes not accepted by the fitness function.`);
|
||||
|
||||
if (docURL) {
|
||||
console.log(`For more info: ${docURL}.`);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export { RULES, runFitnessFunctionRule };
|
||||
export type { IRule };
|
@ -0,0 +1,51 @@
|
||||
import {
|
||||
generateModifyFilesDiff,
|
||||
generateCreateFileDiff,
|
||||
} from '../common/test-data';
|
||||
import { preventJavaScriptFileAdditions } from './javascript-additions';
|
||||
|
||||
describe('preventJavaScriptFileAdditions()', (): void => {
|
||||
it('should pass when receiving an empty diff', (): void => {
|
||||
const testDiff = '';
|
||||
|
||||
const hasRulePassed = preventJavaScriptFileAdditions(testDiff);
|
||||
|
||||
expect(hasRulePassed).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass when receiving a diff with a new TS file on the shared folder', (): void => {
|
||||
const testDiff = [
|
||||
generateModifyFilesDiff('new-file.ts', 'foo', 'bar'),
|
||||
generateModifyFilesDiff('old-file.js', undefined, 'pong'),
|
||||
generateCreateFileDiff('shared/test.ts', 'yada yada yada yada'),
|
||||
].join('');
|
||||
|
||||
const hasRulePassed = preventJavaScriptFileAdditions(testDiff);
|
||||
|
||||
expect(hasRulePassed).toBe(true);
|
||||
});
|
||||
|
||||
it('should not pass when receiving a diff with a new JS file on the shared folder', (): void => {
|
||||
const testDiff = [
|
||||
generateModifyFilesDiff('new-file.ts', 'foo', 'bar'),
|
||||
generateModifyFilesDiff('old-file.js', undefined, 'pong'),
|
||||
generateCreateFileDiff('shared/test.js', 'yada yada yada yada'),
|
||||
].join('');
|
||||
|
||||
const hasRulePassed = preventJavaScriptFileAdditions(testDiff);
|
||||
|
||||
expect(hasRulePassed).toBe(false);
|
||||
});
|
||||
|
||||
it('should not pass when receiving a diff with a new JSX file on the shared folder', (): void => {
|
||||
const testDiff = [
|
||||
generateModifyFilesDiff('new-file.ts', 'foo', 'bar'),
|
||||
generateModifyFilesDiff('old-file.js', undefined, 'pong'),
|
||||
generateCreateFileDiff('shared/test.jsx', 'yada yada yada yada'),
|
||||
].join('');
|
||||
|
||||
const hasRulePassed = preventJavaScriptFileAdditions(testDiff);
|
||||
|
||||
expect(hasRulePassed).toBe(false);
|
||||
});
|
||||
});
|
18
development/fitness-functions/rules/javascript-additions.ts
Normal file
18
development/fitness-functions/rules/javascript-additions.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { SHARED_FOLDER_JS_REGEX } from '../common/constants';
|
||||
import {
|
||||
filterDiffByFilePath,
|
||||
filterDiffFileCreations,
|
||||
} from '../common/shared';
|
||||
|
||||
function preventJavaScriptFileAdditions(diff: string): boolean {
|
||||
const sharedFolderDiff = filterDiffByFilePath(diff, SHARED_FOLDER_JS_REGEX);
|
||||
const sharedFolderCreationDiff = filterDiffFileCreations(sharedFolderDiff);
|
||||
|
||||
const hasCreatedAtLeastOneJSFileInShared = sharedFolderCreationDiff !== '';
|
||||
if (hasCreatedAtLeastOneJSFileInShared) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export { preventJavaScriptFileAdditions };
|
@ -0,0 +1,29 @@
|
||||
import { generateModifyFilesDiff } from '../common/test-data';
|
||||
import { preventSinonAssertSyntax } from './sinon-assert-syntax';
|
||||
|
||||
describe('preventSinonAssertSyntax()', (): void => {
|
||||
it('should pass when receiving an empty diff', (): void => {
|
||||
const testDiff = '';
|
||||
|
||||
const hasRulePassed = preventSinonAssertSyntax(testDiff);
|
||||
|
||||
expect(hasRulePassed).toBe(true);
|
||||
});
|
||||
|
||||
it('should not pass when receiving a diff with one of the blocked expressions', (): void => {
|
||||
const infringingExpression = 'assert.equal';
|
||||
const testDiff = [
|
||||
generateModifyFilesDiff('new-file.ts', 'foo', 'bar'),
|
||||
generateModifyFilesDiff('old-file.js', undefined, 'pong'),
|
||||
generateModifyFilesDiff(
|
||||
'test.js',
|
||||
`yada yada ${infringingExpression} yada yada`,
|
||||
undefined,
|
||||
),
|
||||
].join('');
|
||||
|
||||
const hasRulePassed = preventSinonAssertSyntax(testDiff);
|
||||
|
||||
expect(hasRulePassed).toBe(false);
|
||||
});
|
||||
});
|
30
development/fitness-functions/rules/sinon-assert-syntax.ts
Normal file
30
development/fitness-functions/rules/sinon-assert-syntax.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { EXCLUDE_E2E_TESTS_REGEX } from '../common/constants';
|
||||
import {
|
||||
filterDiffLineAdditions,
|
||||
filterDiffByFilePath,
|
||||
hasNumberOfCodeBlocksIncreased,
|
||||
} from '../common/shared';
|
||||
|
||||
const codeBlocks = [
|
||||
"import { strict as assert } from 'assert';",
|
||||
'assert.deepEqual',
|
||||
'assert.equal',
|
||||
'assert.rejects',
|
||||
'assert.strictEqual',
|
||||
'sinon.',
|
||||
];
|
||||
|
||||
function preventSinonAssertSyntax(diff: string): boolean {
|
||||
const diffByFilePath = filterDiffByFilePath(diff, EXCLUDE_E2E_TESTS_REGEX);
|
||||
const diffAdditions = filterDiffLineAdditions(diffByFilePath);
|
||||
const hashmap = hasNumberOfCodeBlocksIncreased(diffAdditions, codeBlocks);
|
||||
|
||||
const haveOccurencesOfAtLeastOneCodeBlockIncreased =
|
||||
Object.values(hashmap).includes(true);
|
||||
if (haveOccurencesOfAtLeastOneCodeBlockIncreased) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export { preventSinonAssertSyntax };
|
@ -2,11 +2,13 @@
|
||||
const concurrently = require('concurrently');
|
||||
const yargs = require('yargs/yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const { BuildType } = require('./lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('./lib/build-type');
|
||||
|
||||
const stableBuildTypes = Object.values(BuildType).filter(
|
||||
const buildTypesConfig = loadBuildTypesConfig();
|
||||
|
||||
const stableBuildTypes = Object.keys(buildTypesConfig.buildTypes).filter(
|
||||
// Skip generating policy for MMI until that build has stabilized
|
||||
(buildType) => buildType !== BuildType.mmi,
|
||||
(buildType) => buildType !== 'mmi',
|
||||
);
|
||||
|
||||
start().catch((error) => {
|
||||
@ -24,7 +26,7 @@ async function start() {
|
||||
yargsInstance
|
||||
.option('build-types', {
|
||||
alias: ['t'],
|
||||
choices: Object.values(BuildType),
|
||||
choices: Object.keys(buildTypesConfig.buildTypes),
|
||||
default: stableBuildTypes,
|
||||
demandOption: true,
|
||||
description: 'The build type(s) to generate policy files for.',
|
||||
|
6
development/lib/build-type.d.ts
vendored
Normal file
6
development/lib/build-type.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import { Infer, Struct } from 'superstruct';
|
||||
|
||||
export type Unique<Element extends Struct<any>> = (
|
||||
struct: Struct<Infer<Element>[], Infer<Element>>,
|
||||
eq?: (a: Infer<Element>, b: Infer<Element>) => boolean,
|
||||
) => Struct<Infer<Element>[], Infer<Element>>;
|
@ -1,18 +1,169 @@
|
||||
const fs = require('fs');
|
||||
const { AssertionError } = require('assert');
|
||||
const path = require('path');
|
||||
const {
|
||||
object,
|
||||
string,
|
||||
record,
|
||||
optional,
|
||||
array,
|
||||
refine,
|
||||
any,
|
||||
boolean,
|
||||
coerce,
|
||||
union,
|
||||
unknown,
|
||||
validate,
|
||||
nullable,
|
||||
never,
|
||||
} = require('superstruct');
|
||||
const yaml = require('js-yaml');
|
||||
const { uniqWith } = require('lodash');
|
||||
|
||||
const BUILDS_YML_PATH = path.resolve('./builds.yml');
|
||||
|
||||
/**
|
||||
* The distribution this build is intended for.
|
||||
*
|
||||
* This should be kept in-sync with the `BuildType` map in `shared/constants/app.js`.
|
||||
* @type {import('superstruct').Infer<typeof BuildTypesStruct> | null}
|
||||
*/
|
||||
const BuildType = {
|
||||
beta: 'beta',
|
||||
desktop: 'desktop',
|
||||
flask: 'flask',
|
||||
main: 'main',
|
||||
mmi: 'mmi',
|
||||
};
|
||||
let cachedBuildTypes = null;
|
||||
|
||||
const BuildTypeInheritance = {
|
||||
[BuildType.desktop]: [BuildType.flask],
|
||||
};
|
||||
/**
|
||||
* Ensures that the array item contains only elements that are distinct from each other
|
||||
*
|
||||
* @template {Struct<any>} Element
|
||||
* @type {import('./build-type').Unique<Element>}
|
||||
*/
|
||||
const unique = (struct, eq) =>
|
||||
refine(struct, 'unique', (value) => {
|
||||
if (uniqWith(value, eq).length === value.length) {
|
||||
return true;
|
||||
}
|
||||
return 'Array contains duplicated values';
|
||||
});
|
||||
|
||||
module.exports = { BuildType, BuildTypeInheritance };
|
||||
const EnvDefinitionStruct = coerce(
|
||||
object({ key: string(), value: unknown() }),
|
||||
refine(record(string(), any()), 'Env variable declaration', (value) => {
|
||||
if (Object.keys(value).length !== 1) {
|
||||
return 'Declaration should have only one property, the name';
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
(value) => ({ key: Object.keys(value)[0], value: Object.values(value)[0] }),
|
||||
);
|
||||
|
||||
const EnvArrayStruct = unique(
|
||||
array(union([string(), EnvDefinitionStruct])),
|
||||
(a, b) => {
|
||||
const keyA = typeof a === 'string' ? a : a.key;
|
||||
const keyB = typeof b === 'string' ? b : b.key;
|
||||
return keyA === keyB;
|
||||
},
|
||||
);
|
||||
|
||||
const BuildTypeStruct = object({
|
||||
features: optional(unique(array(string()))),
|
||||
env: optional(EnvArrayStruct),
|
||||
isPrerelease: optional(boolean()),
|
||||
manifestOverrides: optional(string()),
|
||||
});
|
||||
|
||||
const CopyAssetStruct = object({ src: string(), dest: string() });
|
||||
const ExclusiveIncludeAssetStruct = coerce(
|
||||
object({ exclusiveInclude: string() }),
|
||||
string(),
|
||||
(exclusiveInclude) => ({ exclusiveInclude }),
|
||||
);
|
||||
const AssetStruct = union([CopyAssetStruct, ExclusiveIncludeAssetStruct]);
|
||||
|
||||
const FeatureStruct = object({
|
||||
env: optional(EnvArrayStruct),
|
||||
// TODO(ritave): Check if the paths exist
|
||||
assets: optional(array(AssetStruct)),
|
||||
});
|
||||
|
||||
const FeaturesStruct = refine(
|
||||
record(
|
||||
string(),
|
||||
coerce(FeatureStruct, nullable(never()), () => ({})),
|
||||
),
|
||||
'feature definitions',
|
||||
function* (value) {
|
||||
let isValid = true;
|
||||
|
||||
const definitions = new Set();
|
||||
|
||||
for (const feature of Object.values(value)) {
|
||||
for (const env of feature?.env ?? []) {
|
||||
if (typeof env !== 'string') {
|
||||
if (definitions.has(env.key)) {
|
||||
isValid = false;
|
||||
yield `Multiple defined features have a definition of "${env}" env variable, resulting in a conflict`;
|
||||
}
|
||||
definitions.add(env.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
},
|
||||
);
|
||||
|
||||
const BuildTypesStruct = refine(
|
||||
object({
|
||||
default: string(),
|
||||
buildTypes: record(string(), BuildTypeStruct),
|
||||
features: FeaturesStruct,
|
||||
env: EnvArrayStruct,
|
||||
}),
|
||||
'BuildTypes',
|
||||
(value) => {
|
||||
if (!Object.keys(value.buildTypes).includes(value.default)) {
|
||||
return `Default build type "${value.default}" does not exist in builds declarations`;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Loads definitions of build type and what they are composed of.
|
||||
*
|
||||
* @returns {import('superstruct').Infer<typeof BuildTypesStruct>}
|
||||
*/
|
||||
function loadBuildTypesConfig() {
|
||||
if (cachedBuildTypes !== null) {
|
||||
return cachedBuildTypes;
|
||||
}
|
||||
const buildsData = yaml.load(fs.readFileSync(BUILDS_YML_PATH, 'utf8'), {
|
||||
json: true,
|
||||
});
|
||||
const [err, result] = validate(buildsData, BuildTypesStruct, {
|
||||
coerce: true,
|
||||
});
|
||||
if (err !== undefined) {
|
||||
throw new AssertionError({
|
||||
message: constructFailureMessage(err),
|
||||
});
|
||||
}
|
||||
cachedBuildTypes = result;
|
||||
return buildsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a user readable error message about parse failure.
|
||||
*
|
||||
* @param {import('superstruct').StructError} structError
|
||||
* @returns {string}
|
||||
*/
|
||||
function constructFailureMessage(structError) {
|
||||
return `Failed to parse builds.yml
|
||||
-> ${structError
|
||||
.failures()
|
||||
.map(
|
||||
(failure) =>
|
||||
`${failure.message} (${BUILDS_YML_PATH}:.${failure.path.join('/')})`,
|
||||
)
|
||||
.join('\n -> ')}
|
||||
`;
|
||||
}
|
||||
|
||||
module.exports = { loadBuildTypesConfig };
|
||||
|
@ -1,5 +1,5 @@
|
||||
const { version: manifestVersion } = require('../../package.json');
|
||||
const { BuildType } = require('./build-type');
|
||||
const { loadBuildTypesConfig } = require('./build-type');
|
||||
|
||||
/**
|
||||
* Get the current version of the MetaMask extension. The base manifest version
|
||||
@ -8,14 +8,14 @@ const { BuildType } = require('./build-type');
|
||||
* The build version is needed because certain build types (such as beta) may
|
||||
* be released multiple times during the release process.
|
||||
*
|
||||
* @param {BuildType} buildType - The build type.
|
||||
* @param {string} buildType - The build type.
|
||||
* @param {number} buildVersion - The build version.
|
||||
* @returns {string} The MetaMask extension version.
|
||||
*/
|
||||
function getVersion(buildType, buildVersion) {
|
||||
return buildType === BuildType.main || buildType === BuildType.beta
|
||||
? manifestVersion
|
||||
: `${manifestVersion}-${buildType}.${buildVersion}`;
|
||||
return loadBuildTypesConfig().buildTypes[buildType].isPrerelease === true
|
||||
? `${manifestVersion}-${buildType}.${buildVersion}`
|
||||
: manifestVersion;
|
||||
}
|
||||
|
||||
module.exports = { getVersion };
|
||||
|
114
development/lib/variables.js
Normal file
114
development/lib/variables.js
Normal file
@ -0,0 +1,114 @@
|
||||
/* eslint-disable jsdoc/check-tag-names */
|
||||
const assert = require('assert');
|
||||
|
||||
const DeclaredOnly = Symbol(
|
||||
'This variable was declared only without being defined',
|
||||
);
|
||||
|
||||
class Variables {
|
||||
/**
|
||||
* @type {Map<string, unknown | typeof DeclaredOnly>}
|
||||
*/
|
||||
#definitions = new Map();
|
||||
|
||||
/**
|
||||
* @param {Iterable<string>} declarations
|
||||
*/
|
||||
constructor(declarations) {
|
||||
for (const declaration of declarations) {
|
||||
this.#definitions.set(declaration, DeclaredOnly);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key - The name of the variable
|
||||
* @throws {TypeError} If there is no definition of a variable.
|
||||
*/
|
||||
get(key) {
|
||||
const value = this.getMaybe(key);
|
||||
assert(
|
||||
value !== DeclaredOnly,
|
||||
new TypeError(
|
||||
`Tried to access a declared, but not defined environmental variable "${key}"`,
|
||||
),
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a declared, but maybe not defined variable.
|
||||
*
|
||||
* @param {string} key - The name of the variable
|
||||
* @throws {TypeError} If there was no declaration of the variable.
|
||||
* @returns The value, or undefined if the variables wasn't defined.
|
||||
*/
|
||||
getMaybe(key) {
|
||||
assert(
|
||||
this.isDeclared(key),
|
||||
new TypeError(
|
||||
`Tried to access an environmental variable "${key}" that wasn't declared in builds.yml`,
|
||||
),
|
||||
);
|
||||
return this.#definitions.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets one key
|
||||
*
|
||||
* @overload
|
||||
* @param {string} key
|
||||
* @param {unknown} value
|
||||
* @returns {void}
|
||||
*/
|
||||
/**
|
||||
* @overload
|
||||
* @param {Record<string, unknown>} records - Key-Value object
|
||||
* @returns {void}
|
||||
*/
|
||||
/**
|
||||
* @param {string | Record<string, unknown>} keyOrRecord
|
||||
* @param {unknown} value
|
||||
* @returns {void}
|
||||
*/
|
||||
set(keyOrRecord, value) {
|
||||
if (typeof keyOrRecord === 'object') {
|
||||
for (const [key, recordValue] of Object.entries(keyOrRecord)) {
|
||||
this.set(key, recordValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const key = keyOrRecord;
|
||||
assert(
|
||||
this.isDeclared(key),
|
||||
`Tried to modify a variable "${key}" that wasn't declared in builds.yml`,
|
||||
);
|
||||
assert(value !== DeclaredOnly, `Tried to un-define "${key}" variable`);
|
||||
this.#definitions.set(key, value);
|
||||
}
|
||||
|
||||
isDeclared(key) {
|
||||
return this.#definitions.has(key);
|
||||
}
|
||||
|
||||
isDefined(key) {
|
||||
return (
|
||||
this.#definitions.has(key) && this.#definitions.get(key) !== DeclaredOnly
|
||||
);
|
||||
}
|
||||
|
||||
[Symbol.iterator] = this.declarations;
|
||||
|
||||
*declarations() {
|
||||
yield* this.#definitions.keys();
|
||||
}
|
||||
|
||||
*definitions() {
|
||||
for (const [key, value] of this.#definitions.entries()) {
|
||||
if (value !== DeclaredOnly) {
|
||||
yield [key, value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Variables };
|
@ -5,7 +5,7 @@ const { hideBin } = require('yargs/helpers');
|
||||
|
||||
const { runCommand, runInShell } = require('./lib/run-command');
|
||||
const { getVersion } = require('./lib/get-version');
|
||||
const { BuildType } = require('./lib/build-type');
|
||||
const { loadBuildTypesConfig } = require('./lib/build-type');
|
||||
|
||||
start().catch((error) => {
|
||||
console.error(error);
|
||||
@ -29,9 +29,9 @@ async function start() {
|
||||
type: 'string',
|
||||
})
|
||||
.option('build-type', {
|
||||
default: BuildType.main,
|
||||
default: loadBuildTypesConfig().default,
|
||||
description: 'The MetaMask extension build type',
|
||||
choices: Object.values(BuildType),
|
||||
choices: Object.keys(loadBuildTypesConfig().buildTypes),
|
||||
})
|
||||
.option('build-version', {
|
||||
default: 0,
|
||||
@ -86,7 +86,7 @@ async function start() {
|
||||
}
|
||||
|
||||
const additionalUploadArgs = [];
|
||||
if (buildType !== BuildType.main) {
|
||||
if (buildType !== loadBuildTypesConfig().default) {
|
||||
additionalUploadArgs.push('--dist-directory', `dist-${buildType}`);
|
||||
}
|
||||
// upload sentry source and sourcemaps
|
||||
|
@ -79,7 +79,6 @@
|
||||
"app/scripts/lib/createRPCMethodTrackingMiddleware.test.js",
|
||||
"app/scripts/lib/createStreamSink.js",
|
||||
"app/scripts/lib/createTabIdMiddleware.js",
|
||||
"app/scripts/lib/decrypt-message-manager.js",
|
||||
"app/scripts/lib/ens-ipfs/contracts/registry.js",
|
||||
"app/scripts/lib/ens-ipfs/contracts/resolver.js",
|
||||
"app/scripts/lib/ens-ipfs/resolver.js",
|
||||
|
@ -8,7 +8,8 @@ import pify from 'pify';
|
||||
import endOfStream from 'end-of-stream';
|
||||
import pump from 'pump';
|
||||
import gulp from 'gulp';
|
||||
import gulpDartSass from 'gulp-dart-sass';
|
||||
import gulpSass from 'gulp-sass';
|
||||
import sass from 'sass';
|
||||
import sourcemaps from 'gulp-sourcemaps';
|
||||
import autoprefixer from 'gulp-autoprefixer';
|
||||
import fg from 'fast-glob';
|
||||
@ -94,7 +95,7 @@ async function compileStylesheets(src: string, dest: string): Promise<void> {
|
||||
await promisifiedPump(
|
||||
gulp.src(src),
|
||||
sourcemaps.init(),
|
||||
gulpDartSass().on('error', (error: unknown) => {
|
||||
gulpSass(sass)().on('error', (error: unknown) => {
|
||||
console.error(`Couldn't compile stylesheets: ${error}`);
|
||||
}),
|
||||
autoprefixer(),
|
||||
|
@ -6,6 +6,7 @@ module.exports = {
|
||||
'!<rootDir>/app/scripts/controllers/network/**/test/*.ts',
|
||||
'<rootDir>/app/scripts/controllers/permissions/**/*.js',
|
||||
'<rootDir>/app/scripts/controllers/sign.ts',
|
||||
'<rootDir>/app/scripts/controllers/decrypt-message.ts',
|
||||
'<rootDir>/app/scripts/flask/**/*.js',
|
||||
'<rootDir>/app/scripts/lib/**/*.js',
|
||||
'<rootDir>/app/scripts/lib/createRPCMethodTrackingMiddleware.js',
|
||||
@ -44,6 +45,7 @@ module.exports = {
|
||||
'<rootDir>/app/scripts/controllers/network/**/*.test.ts',
|
||||
'<rootDir>/app/scripts/controllers/permissions/**/*.test.js',
|
||||
'<rootDir>/app/scripts/controllers/sign.test.ts',
|
||||
'<rootDir>/app/scripts/controllers/decrypt-message.test.ts',
|
||||
'<rootDir>/app/scripts/flask/**/*.test.js',
|
||||
'<rootDir>/app/scripts/lib/**/*.test.js',
|
||||
'<rootDir>/app/scripts/lib/**/*.test.ts',
|
||||
|
@ -716,7 +716,7 @@
|
||||
"@metamask/assets-controllers>@metamask/abi-utils": {
|
||||
"packages": {
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
|
||||
@ -725,10 +725,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>abort-controller": {
|
||||
@ -750,6 +750,7 @@
|
||||
},
|
||||
"@metamask/controller-utils": {
|
||||
"globals": {
|
||||
"URL": true,
|
||||
"console.error": true,
|
||||
"fetch": true,
|
||||
"setTimeout": true
|
||||
@ -820,10 +821,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
|
||||
@ -868,10 +869,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-provider": {
|
||||
@ -1191,10 +1192,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/key-tree>@noble/ed25519": {
|
||||
@ -1316,7 +1317,7 @@
|
||||
"@metamask/key-tree": true,
|
||||
"@metamask/key-tree>@noble/hashes": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/rpc-methods>@metamask/browser-passworder": {
|
||||
@ -1442,10 +1443,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
|
||||
@ -2658,10 +2659,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"eth-ens-namehash": {
|
||||
@ -2779,7 +2780,6 @@
|
||||
"@ethersproject/abi": true,
|
||||
"bn.js": true,
|
||||
"browserify>buffer": true,
|
||||
"browserify>process": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>bech32": true,
|
||||
|
@ -716,7 +716,7 @@
|
||||
"@metamask/assets-controllers>@metamask/abi-utils": {
|
||||
"packages": {
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
|
||||
@ -725,10 +725,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>abort-controller": {
|
||||
@ -750,6 +750,7 @@
|
||||
},
|
||||
"@metamask/controller-utils": {
|
||||
"globals": {
|
||||
"URL": true,
|
||||
"console.error": true,
|
||||
"fetch": true,
|
||||
"setTimeout": true
|
||||
@ -826,7 +827,6 @@
|
||||
"@metamask/desktop>otpauth": true,
|
||||
"browserify>buffer": true,
|
||||
"browserify>events": true,
|
||||
"browserify>process": true,
|
||||
"browserify>stream-browserify": true,
|
||||
"end-of-stream": true,
|
||||
"extension-port-stream": true,
|
||||
@ -892,10 +892,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
|
||||
@ -940,10 +940,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-provider": {
|
||||
@ -1263,10 +1263,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/key-tree>@noble/ed25519": {
|
||||
@ -1413,10 +1413,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/post-message-stream>readable-stream": {
|
||||
@ -1478,8 +1478,8 @@
|
||||
"@metamask/snaps-ui": true,
|
||||
"@metamask/snaps-utils": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true,
|
||||
"eth-rpc-errors": true
|
||||
"eth-rpc-errors": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/rpc-methods>@metamask/browser-passworder": {
|
||||
@ -1770,7 +1770,7 @@
|
||||
"@metamask/snaps-ui": {
|
||||
"packages": {
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/snaps-utils": {
|
||||
@ -1791,15 +1791,15 @@
|
||||
"@metamask/snaps-utils>rfdc": true,
|
||||
"@metamask/snaps-utils>validate-npm-package-name": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/snaps-utils>@metamask/snaps-registry": {
|
||||
"packages": {
|
||||
"@metamask/key-tree>@noble/secp256k1": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/snaps-utils>cron-parser": {
|
||||
@ -1835,10 +1835,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
|
||||
@ -3051,10 +3051,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"eth-ens-namehash": {
|
||||
@ -3172,7 +3172,6 @@
|
||||
"@ethersproject/abi": true,
|
||||
"bn.js": true,
|
||||
"browserify>buffer": true,
|
||||
"browserify>process": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>bech32": true,
|
||||
|
@ -716,7 +716,7 @@
|
||||
"@metamask/assets-controllers>@metamask/abi-utils": {
|
||||
"packages": {
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
|
||||
@ -725,10 +725,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>abort-controller": {
|
||||
@ -750,6 +750,7 @@
|
||||
},
|
||||
"@metamask/controller-utils": {
|
||||
"globals": {
|
||||
"URL": true,
|
||||
"console.error": true,
|
||||
"fetch": true,
|
||||
"setTimeout": true
|
||||
@ -826,7 +827,6 @@
|
||||
"@metamask/desktop>otpauth": true,
|
||||
"browserify>buffer": true,
|
||||
"browserify>events": true,
|
||||
"browserify>process": true,
|
||||
"browserify>stream-browserify": true,
|
||||
"end-of-stream": true,
|
||||
"extension-port-stream": true,
|
||||
@ -892,10 +892,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
|
||||
@ -940,10 +940,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-provider": {
|
||||
@ -1263,10 +1263,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/key-tree>@noble/ed25519": {
|
||||
@ -1413,10 +1413,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/post-message-stream>readable-stream": {
|
||||
@ -1478,8 +1478,8 @@
|
||||
"@metamask/snaps-ui": true,
|
||||
"@metamask/snaps-utils": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true,
|
||||
"eth-rpc-errors": true
|
||||
"eth-rpc-errors": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/rpc-methods>@metamask/browser-passworder": {
|
||||
@ -1770,7 +1770,7 @@
|
||||
"@metamask/snaps-ui": {
|
||||
"packages": {
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/snaps-utils": {
|
||||
@ -1791,15 +1791,15 @@
|
||||
"@metamask/snaps-utils>rfdc": true,
|
||||
"@metamask/snaps-utils>validate-npm-package-name": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/snaps-utils>@metamask/snaps-registry": {
|
||||
"packages": {
|
||||
"@metamask/key-tree>@noble/secp256k1": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/snaps-utils>cron-parser": {
|
||||
@ -1835,10 +1835,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
|
||||
@ -3051,10 +3051,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"eth-ens-namehash": {
|
||||
@ -3172,7 +3172,6 @@
|
||||
"@ethersproject/abi": true,
|
||||
"bn.js": true,
|
||||
"browserify>buffer": true,
|
||||
"browserify>process": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>bech32": true,
|
||||
|
@ -716,7 +716,7 @@
|
||||
"@metamask/assets-controllers>@metamask/abi-utils": {
|
||||
"packages": {
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
|
||||
@ -725,10 +725,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/assets-controllers>abort-controller": {
|
||||
@ -750,6 +750,7 @@
|
||||
},
|
||||
"@metamask/controller-utils": {
|
||||
"globals": {
|
||||
"URL": true,
|
||||
"console.error": true,
|
||||
"fetch": true,
|
||||
"setTimeout": true
|
||||
@ -820,10 +821,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
|
||||
@ -868,10 +869,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/eth-json-rpc-provider": {
|
||||
@ -1191,10 +1192,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/key-tree>@noble/ed25519": {
|
||||
@ -1316,7 +1317,7 @@
|
||||
"@metamask/key-tree": true,
|
||||
"@metamask/key-tree>@noble/hashes": true,
|
||||
"@metamask/utils": true,
|
||||
"@metamask/utils>superstruct": true
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/rpc-methods>@metamask/browser-passworder": {
|
||||
@ -1442,10 +1443,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
|
||||
@ -2658,10 +2659,10 @@
|
||||
"TextEncoder": true
|
||||
},
|
||||
"packages": {
|
||||
"@metamask/utils>superstruct": true,
|
||||
"browserify>buffer": true,
|
||||
"nock>debug": true,
|
||||
"semver": true
|
||||
"semver": true,
|
||||
"superstruct": true
|
||||
}
|
||||
},
|
||||
"eth-ens-namehash": {
|
||||
@ -2779,7 +2780,6 @@
|
||||
"@ethersproject/abi": true,
|
||||
"bn.js": true,
|
||||
"browserify>buffer": true,
|
||||
"browserify>process": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
|
||||
"eth-lattice-keyring>gridplus-sdk>bech32": true,
|
||||
|
@ -1272,16 +1272,6 @@
|
||||
"typescript": true
|
||||
}
|
||||
},
|
||||
"addons-linter>postcss>picocolors": {
|
||||
"builtin": {
|
||||
"tty.isatty": true
|
||||
},
|
||||
"globals": {
|
||||
"process.argv.includes": true,
|
||||
"process.env": true,
|
||||
"process.platform": true
|
||||
}
|
||||
},
|
||||
"babelify": {
|
||||
"builtin": {
|
||||
"path.extname": true,
|
||||
@ -1926,10 +1916,10 @@
|
||||
"packages": {
|
||||
"chokidar>braces": true,
|
||||
"chokidar>fsevents": true,
|
||||
"chokidar>glob-parent": true,
|
||||
"chokidar>is-binary-path": true,
|
||||
"chokidar>normalize-path": true,
|
||||
"depcheck>readdirp": true,
|
||||
"eslint>glob-parent": true,
|
||||
"eslint>is-glob": true,
|
||||
"watchify>anymatch": true
|
||||
}
|
||||
@ -1959,15 +1949,6 @@
|
||||
},
|
||||
"native": true
|
||||
},
|
||||
"chokidar>glob-parent": {
|
||||
"builtin": {
|
||||
"os.platform": true,
|
||||
"path.posix.dirname": true
|
||||
},
|
||||
"packages": {
|
||||
"eslint>is-glob": true
|
||||
}
|
||||
},
|
||||
"chokidar>is-binary-path": {
|
||||
"builtin": {
|
||||
"path.extname": true
|
||||
@ -3074,9 +3055,9 @@
|
||||
"process.cwd": true
|
||||
},
|
||||
"packages": {
|
||||
"eslint>glob-parent": true,
|
||||
"fast-glob>@nodelib/fs.stat": true,
|
||||
"fast-glob>@nodelib/fs.walk": true,
|
||||
"fast-glob>glob-parent": true,
|
||||
"globby>merge2": true,
|
||||
"stylelint>micromatch": true
|
||||
}
|
||||
@ -3131,15 +3112,6 @@
|
||||
"fast-glob>@nodelib/fs.walk>fastq>reusify": true
|
||||
}
|
||||
},
|
||||
"fast-glob>glob-parent": {
|
||||
"builtin": {
|
||||
"os.platform": true,
|
||||
"path.posix.dirname": true
|
||||
},
|
||||
"packages": {
|
||||
"eslint>is-glob": true
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"builtin": {
|
||||
"assert": true,
|
||||
@ -3287,9 +3259,9 @@
|
||||
"process.env.AUTOPREFIXER_GRID": true
|
||||
},
|
||||
"packages": {
|
||||
"addons-linter>postcss>picocolors": true,
|
||||
"gulp-autoprefixer>autoprefixer>fraction.js": true,
|
||||
"gulp-autoprefixer>postcss": true,
|
||||
"gulp-sass>picocolors": true,
|
||||
"stylelint>autoprefixer>normalize-range": true,
|
||||
"stylelint>postcss-value-parser": true,
|
||||
"webpack>browserslist": true,
|
||||
@ -3324,85 +3296,9 @@
|
||||
"process.env.NODE_ENV": true
|
||||
},
|
||||
"packages": {
|
||||
"addons-linter>postcss>picocolors": true,
|
||||
"addons-linter>postcss>source-map-js": true,
|
||||
"gulp-autoprefixer>postcss>nanoid": true
|
||||
}
|
||||
},
|
||||
"gulp-dart-sass": {
|
||||
"builtin": {
|
||||
"path.basename": true,
|
||||
"path.dirname": true,
|
||||
"path.extname": true,
|
||||
"path.join": true,
|
||||
"path.relative": true
|
||||
},
|
||||
"globals": {
|
||||
"process.cwd": true,
|
||||
"process.stderr.write": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-dart-sass>chalk": true,
|
||||
"gulp-dart-sass>lodash.clonedeep": true,
|
||||
"gulp-dart-sass>strip-ansi": true,
|
||||
"gulp-dart-sass>through2": true,
|
||||
"gulp-zip>plugin-error": true,
|
||||
"sass": true,
|
||||
"vinyl-sourcemaps-apply": true,
|
||||
"vinyl>replace-ext": true
|
||||
}
|
||||
},
|
||||
"gulp-dart-sass>chalk": {
|
||||
"globals": {
|
||||
"process.env.TERM": true,
|
||||
"process.platform": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-dart-sass>chalk>ansi-styles": true,
|
||||
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||
"gulp-dart-sass>chalk>supports-color": true
|
||||
}
|
||||
},
|
||||
"gulp-dart-sass>chalk>ansi-styles": {
|
||||
"packages": {
|
||||
"@metamask/jazzicon>color>color-convert": true
|
||||
}
|
||||
},
|
||||
"gulp-dart-sass>chalk>supports-color": {
|
||||
"builtin": {
|
||||
"os.release": true
|
||||
},
|
||||
"globals": {
|
||||
"process.env": true,
|
||||
"process.platform": true,
|
||||
"process.stderr": true,
|
||||
"process.stdout": true,
|
||||
"process.versions.node.split": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-dart-sass>chalk>supports-color>has-flag": true
|
||||
}
|
||||
},
|
||||
"gulp-dart-sass>chalk>supports-color>has-flag": {
|
||||
"globals": {
|
||||
"process.argv": true
|
||||
}
|
||||
},
|
||||
"gulp-dart-sass>strip-ansi": {
|
||||
"packages": {
|
||||
"gulp-dart-sass>strip-ansi>ansi-regex": true
|
||||
}
|
||||
},
|
||||
"gulp-dart-sass>through2": {
|
||||
"builtin": {
|
||||
"util.inherits": true
|
||||
},
|
||||
"globals": {
|
||||
"process.nextTick": true
|
||||
},
|
||||
"packages": {
|
||||
"readable-stream": true,
|
||||
"watchify>xtend": true
|
||||
"gulp-autoprefixer>postcss>nanoid": true,
|
||||
"gulp-sass>picocolors": true
|
||||
}
|
||||
},
|
||||
"gulp-livereload": {
|
||||
@ -3424,8 +3320,8 @@
|
||||
"process.platform": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||
"gulp-livereload>chalk>ansi-styles": true,
|
||||
"gulp-livereload>chalk>escape-string-regexp": true,
|
||||
"gulp-livereload>chalk>supports-color": true
|
||||
}
|
||||
},
|
||||
@ -3698,7 +3594,7 @@
|
||||
"process.platform": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||
"gulp-livereload>chalk>escape-string-regexp": true,
|
||||
"gulp-rtlcss>rtlcss>chalk>ansi-styles": true,
|
||||
"gulp-rtlcss>rtlcss>chalk>supports-color": true
|
||||
}
|
||||
@ -3757,6 +3653,48 @@
|
||||
"watchify>xtend": true
|
||||
}
|
||||
},
|
||||
"gulp-sass": {
|
||||
"builtin": {
|
||||
"path.basename": true,
|
||||
"path.dirname": true,
|
||||
"path.extname": true,
|
||||
"path.join": true,
|
||||
"path.relative": true,
|
||||
"stream.Transform": true
|
||||
},
|
||||
"globals": {
|
||||
"process.cwd": true,
|
||||
"process.exit": true,
|
||||
"process.stderr.write": true
|
||||
},
|
||||
"packages": {
|
||||
"eslint>strip-ansi": true,
|
||||
"gulp-sass>lodash.clonedeep": true,
|
||||
"gulp-sass>picocolors": true,
|
||||
"gulp-sass>replace-ext": true,
|
||||
"gulp-zip>plugin-error": true,
|
||||
"vinyl-sourcemaps-apply": true
|
||||
}
|
||||
},
|
||||
"gulp-sass>picocolors": {
|
||||
"builtin": {
|
||||
"tty.isatty": true
|
||||
},
|
||||
"globals": {
|
||||
"process.argv.includes": true,
|
||||
"process.env": true,
|
||||
"process.platform": true
|
||||
}
|
||||
},
|
||||
"gulp-sass>replace-ext": {
|
||||
"builtin": {
|
||||
"path.basename": true,
|
||||
"path.dirname": true,
|
||||
"path.extname": true,
|
||||
"path.join": true,
|
||||
"path.sep": true
|
||||
}
|
||||
},
|
||||
"gulp-sort": {
|
||||
"packages": {
|
||||
"gulp-sort>through2": true
|
||||
@ -4073,11 +4011,11 @@
|
||||
"setTimeout": true
|
||||
},
|
||||
"packages": {
|
||||
"eslint>glob-parent": true,
|
||||
"gulp-watch>ansi-colors": true,
|
||||
"gulp-watch>anymatch": true,
|
||||
"gulp-watch>chokidar": true,
|
||||
"gulp-watch>fancy-log": true,
|
||||
"gulp-watch>glob-parent": true,
|
||||
"gulp-watch>path-is-absolute": true,
|
||||
"gulp-watch>slash": true,
|
||||
"gulp-watch>vinyl-file": true,
|
||||
@ -4168,8 +4106,8 @@
|
||||
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>randomatic": {
|
||||
"packages": {
|
||||
"@babel/register>clone-deep>kind-of": true,
|
||||
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>randomatic>math-random": true,
|
||||
"gulp>undertaker>bach>array-last>is-number": true
|
||||
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>randomatic>is-number": true,
|
||||
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>randomatic>math-random": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>anymatch>micromatch>braces>expand-range>fill-range>randomatic>math-random": {
|
||||
@ -4221,15 +4159,7 @@
|
||||
"path.dirname": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-watch>anymatch>micromatch>parse-glob>glob-base>glob-parent": true,
|
||||
"gulp-watch>anymatch>micromatch>parse-glob>glob-base>is-glob": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>anymatch>micromatch>parse-glob>glob-base>glob-parent": {
|
||||
"builtin": {
|
||||
"path.dirname": true
|
||||
},
|
||||
"packages": {
|
||||
"eslint>glob-parent": true,
|
||||
"gulp-watch>anymatch>micromatch>parse-glob>glob-base>is-glob": true
|
||||
}
|
||||
},
|
||||
@ -4282,6 +4212,7 @@
|
||||
},
|
||||
"packages": {
|
||||
"chokidar>normalize-path": true,
|
||||
"eslint>glob-parent": true,
|
||||
"eslint>is-glob": true,
|
||||
"gulp-watch>chokidar>anymatch": true,
|
||||
"gulp-watch>chokidar>async-each": true,
|
||||
@ -4457,20 +4388,10 @@
|
||||
},
|
||||
"gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values": {
|
||||
"packages": {
|
||||
"gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number": true,
|
||||
"gulp-watch>chokidar>braces>fill-range>is-number": true,
|
||||
"gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>kind-of": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number": {
|
||||
"packages": {
|
||||
"gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number>kind-of": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>is-number>kind-of": {
|
||||
"packages": {
|
||||
"browserify>insert-module-globals>is-buffer": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>chokidar>braces>snapdragon>base>cache-base>has-value>has-values>kind-of": {
|
||||
"packages": {
|
||||
"browserify>insert-module-globals>is-buffer": true
|
||||
@ -4885,30 +4806,6 @@
|
||||
"fancy-log>time-stamp": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>glob-parent": {
|
||||
"builtin": {
|
||||
"os.platform": true,
|
||||
"path": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-watch>glob-parent>is-glob": true,
|
||||
"gulp-watch>glob-parent>path-dirname": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>glob-parent>is-glob": {
|
||||
"packages": {
|
||||
"gulp-watch>glob-parent>is-glob>is-extglob": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>glob-parent>path-dirname": {
|
||||
"builtin": {
|
||||
"path": true,
|
||||
"util.inspect": true
|
||||
},
|
||||
"globals": {
|
||||
"process.platform": true
|
||||
}
|
||||
},
|
||||
"gulp-watch>path-is-absolute": {
|
||||
"globals": {
|
||||
"process.platform": true
|
||||
@ -5206,9 +5103,9 @@
|
||||
},
|
||||
"packages": {
|
||||
"chokidar>normalize-path": true,
|
||||
"eslint>glob-parent": true,
|
||||
"eslint>is-glob": true,
|
||||
"gulp-watch>chokidar>async-each": true,
|
||||
"gulp-watch>glob-parent": true,
|
||||
"gulp-watch>path-is-absolute": true,
|
||||
"gulp>glob-watcher>anymatch": true,
|
||||
"gulp>glob-watcher>chokidar>braces": true,
|
||||
@ -5239,24 +5136,14 @@
|
||||
},
|
||||
"packages": {
|
||||
"gulp-watch>chokidar>braces>extend-shallow": true,
|
||||
"gulp>glob-watcher>chokidar>braces>fill-range>is-number": true,
|
||||
"gulp-watch>chokidar>braces>fill-range>is-number": true,
|
||||
"gulp>glob-watcher>chokidar>braces>fill-range>to-regex-range": true,
|
||||
"stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
|
||||
}
|
||||
},
|
||||
"gulp>glob-watcher>chokidar>braces>fill-range>is-number": {
|
||||
"packages": {
|
||||
"gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": true
|
||||
}
|
||||
},
|
||||
"gulp>glob-watcher>chokidar>braces>fill-range>is-number>kind-of": {
|
||||
"packages": {
|
||||
"browserify>insert-module-globals>is-buffer": true
|
||||
}
|
||||
},
|
||||
"gulp>glob-watcher>chokidar>braces>fill-range>to-regex-range": {
|
||||
"packages": {
|
||||
"gulp>glob-watcher>chokidar>braces>fill-range>is-number": true,
|
||||
"gulp-watch>chokidar>braces>fill-range>is-number": true,
|
||||
"stylelint>@stylelint/postcss-markdown>remark>remark-parse>repeat-string": true
|
||||
}
|
||||
},
|
||||
@ -5406,7 +5293,6 @@
|
||||
"gulp>gulp-cli>matchdep>micromatch>fragment-cache": true,
|
||||
"gulp>gulp-cli>matchdep>micromatch>nanomatch>define-property": true,
|
||||
"gulp>gulp-cli>matchdep>micromatch>nanomatch>extend-shallow": true,
|
||||
"gulp>gulp-cli>matchdep>micromatch>nanomatch>is-odd": true,
|
||||
"gulp>gulp-cli>matchdep>micromatch>regex-not": true,
|
||||
"nyc>spawn-wrap>is-windows": true
|
||||
}
|
||||
@ -5428,11 +5314,6 @@
|
||||
"@babel/register>clone-deep>is-plain-object": true
|
||||
}
|
||||
},
|
||||
"gulp>gulp-cli>matchdep>micromatch>nanomatch>is-odd": {
|
||||
"packages": {
|
||||
"gulp>undertaker>bach>array-last>is-number": true
|
||||
}
|
||||
},
|
||||
"gulp>gulp-cli>matchdep>micromatch>regex-not": {
|
||||
"packages": {
|
||||
"gulp-watch>chokidar>braces>to-regex>safe-regex": true,
|
||||
@ -5522,7 +5403,7 @@
|
||||
},
|
||||
"gulp>undertaker>bach>array-initial": {
|
||||
"packages": {
|
||||
"gulp>undertaker>bach>array-last>is-number": true,
|
||||
"gulp>undertaker>bach>array-initial>is-number": true,
|
||||
"gulp>undertaker>object.defaults>array-slice": true
|
||||
}
|
||||
},
|
||||
@ -5652,7 +5533,7 @@
|
||||
"process.nextTick": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-watch>glob-parent": true,
|
||||
"eslint>glob-parent": true,
|
||||
"gulp>glob-watcher>is-negated-glob": true,
|
||||
"gulp>vinyl-fs>glob-stream>ordered-read-streams": true,
|
||||
"gulp>vinyl-fs>glob-stream>pumpify": true,
|
||||
@ -6066,7 +5947,7 @@
|
||||
"process.platform": true
|
||||
},
|
||||
"packages": {
|
||||
"gulp-dart-sass>chalk>escape-string-regexp": true,
|
||||
"gulp-livereload>chalk>escape-string-regexp": true,
|
||||
"lavamoat>@babel/highlight>chalk>ansi-styles": true,
|
||||
"lavamoat>@babel/highlight>chalk>supports-color": true
|
||||
}
|
||||
@ -6688,6 +6569,7 @@
|
||||
"url.resolve": true
|
||||
},
|
||||
"globals": {
|
||||
"TextDecoder": true,
|
||||
"setImmediate": true
|
||||
},
|
||||
"packages": {
|
||||
@ -7610,7 +7492,7 @@
|
||||
},
|
||||
"terser": {
|
||||
"globals": {
|
||||
"Buffer.from": true,
|
||||
"Buffer": true,
|
||||
"atob": true,
|
||||
"btoa": true,
|
||||
"console.log": true,
|
||||
|
19
package.json
19
package.json
@ -91,15 +91,18 @@
|
||||
"test-storybook": "test-storybook -c .storybook",
|
||||
"test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn storybook:build && npx http-server storybook-build --port 6006 \" \"wait-on tcp:6006 && yarn test-storybook --maxWorkers=2\"",
|
||||
"githooks:install": "husky install",
|
||||
"fitness-functions": "node development/fitness-functions/index.js",
|
||||
"fitness-functions": "ts-node development/fitness-functions/index.ts",
|
||||
"generate-beta-commit": "node ./development/generate-beta-commit.js"
|
||||
},
|
||||
"resolutions": {
|
||||
"analytics-node/axios": "^0.21.2",
|
||||
"ganache-core/lodash": "^4.17.21",
|
||||
"git-url-parse@^12.0.0": "^13.1.0",
|
||||
"glob-parent": "^6.0.2",
|
||||
"netmask": "^2.0.1",
|
||||
"json-schema": "^0.4.0",
|
||||
"ast-types": "^0.14.2",
|
||||
"x-default-browser": "^0.5.2",
|
||||
"web3-provider-engine/eth-json-rpc-filters": "^6.0.0",
|
||||
"typescript@~4.4.0": "patch:typescript@npm:4.4.4#.yarn/patches/typescript-npm-4.4.4-3fedcc07a3.patch",
|
||||
"acorn@^7.0.0": "patch:acorn@npm:7.4.1#.yarn/patches/acorn-npm-7.4.1-f450b4646c.patch",
|
||||
@ -229,7 +232,7 @@
|
||||
"@metamask/assets-controllers": "^6.0.0",
|
||||
"@metamask/base-controller": "^2.0.0",
|
||||
"@metamask/contract-metadata": "^2.3.1",
|
||||
"@metamask/controller-utils": "^3.2.0",
|
||||
"@metamask/controller-utils": "^3.3.0",
|
||||
"@metamask/design-tokens": "^1.9.0",
|
||||
"@metamask/desktop": "^0.3.0",
|
||||
"@metamask/eth-json-rpc-infura": "^8.0.0",
|
||||
@ -243,7 +246,7 @@
|
||||
"@metamask/jazzicon": "^2.0.0",
|
||||
"@metamask/key-tree": "^7.0.0",
|
||||
"@metamask/logo": "^3.1.1",
|
||||
"@metamask/message-manager": "^3.0.0",
|
||||
"@metamask/message-manager": "^3.1.1",
|
||||
"@metamask/metamask-eth-abis": "^3.0.0",
|
||||
"@metamask/notification-controller": "^2.0.0",
|
||||
"@metamask/obs-store": "^8.1.0",
|
||||
@ -255,7 +258,7 @@
|
||||
"@metamask/rpc-methods": "^0.32.2",
|
||||
"@metamask/safe-event-emitter": "^2.0.0",
|
||||
"@metamask/scure-bip39": "^2.0.3",
|
||||
"@metamask/slip44": "^2.1.0",
|
||||
"@metamask/slip44": "^3.0.0",
|
||||
"@metamask/smart-transactions-controller": "^3.1.0",
|
||||
"@metamask/snaps-controllers": "^0.32.2",
|
||||
"@metamask/snaps-ui": "^0.32.2",
|
||||
@ -375,7 +378,7 @@
|
||||
"@metamask/eslint-config-typescript": "^9.0.1",
|
||||
"@metamask/forwarder": "^1.1.0",
|
||||
"@metamask/phishing-warning": "^2.1.0",
|
||||
"@metamask/test-dapp": "^5.6.0",
|
||||
"@metamask/test-dapp": "^6.0.0",
|
||||
"@sentry/cli": "^1.58.0",
|
||||
"@storybook/addon-a11y": "^6.5.13",
|
||||
"@storybook/addon-actions": "^6.5.13",
|
||||
@ -405,7 +408,7 @@
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/gulp": "^4.0.9",
|
||||
"@types/gulp-autoprefixer": "^0.0.33",
|
||||
"@types/gulp-dart-sass": "^1.0.1",
|
||||
"@types/gulp-sass": "^5.0.0",
|
||||
"@types/gulp-sourcemaps": "^0.0.35",
|
||||
"@types/jest": "^29.1.2",
|
||||
"@types/jest-when": "^3.5.2",
|
||||
@ -417,6 +420,7 @@
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react-redux": "^7.1.25",
|
||||
"@types/remote-redux-devtools": "^0.5.5",
|
||||
"@types/sass": "^1.43.1",
|
||||
"@types/sinon": "^10.0.13",
|
||||
"@types/w3c-web-hid": "^1.0.3",
|
||||
"@types/watchify": "^3.11.1",
|
||||
@ -465,10 +469,10 @@
|
||||
"globby": "^11.0.4",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-autoprefixer": "^8.0.0",
|
||||
"gulp-dart-sass": "^1.0.2",
|
||||
"gulp-livereload": "4.0.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-rtlcss": "^1.4.0",
|
||||
"gulp-sass": "^5.1.0",
|
||||
"gulp-sort": "^2.0.0",
|
||||
"gulp-sourcemaps": "^3.0.0",
|
||||
"gulp-stylelint": "^13.0.0",
|
||||
@ -528,6 +532,7 @@
|
||||
"string.prototype.matchall": "^4.0.2",
|
||||
"style-loader": "^0.21.0",
|
||||
"stylelint": "^13.6.1",
|
||||
"superstruct": "^1.0.3",
|
||||
"terser": "^5.7.0",
|
||||
"through2": "^4.0.2",
|
||||
"ts-node": "^10.5.0",
|
||||
|
@ -1,4 +1,4 @@
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
import { DialogType } from '@metamask/rpc-methods';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import { RestrictedMethods } from './permissions';
|
||||
@ -20,18 +20,6 @@ export const ENVIRONMENT_TYPE_NOTIFICATION = 'notification';
|
||||
export const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen';
|
||||
export const ENVIRONMENT_TYPE_BACKGROUND = 'background';
|
||||
|
||||
/**
|
||||
* The distribution this build is intended for.
|
||||
*
|
||||
* This should be kept in-sync with the `BuildType` map in `development/build/utils.js`.
|
||||
*/
|
||||
export const BuildType = {
|
||||
beta: 'beta',
|
||||
desktop: 'desktop',
|
||||
flask: 'flask',
|
||||
main: 'main',
|
||||
} as const;
|
||||
|
||||
export const PLATFORM_BRAVE = 'Brave';
|
||||
export const PLATFORM_CHROME = 'Chrome';
|
||||
export const PLATFORM_EDGE = 'Edge';
|
||||
@ -57,12 +45,12 @@ export const MESSAGE_TYPE = {
|
||||
WALLET_REQUEST_PERMISSIONS: 'wallet_requestPermissions',
|
||||
WATCH_ASSET: 'wallet_watchAsset',
|
||||
WATCH_ASSET_LEGACY: 'metamask_watchAsset',
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
SNAP_DIALOG_ALERT: `${RestrictedMethods.snap_dialog}:alert`,
|
||||
SNAP_DIALOG_CONFIRMATION: `${RestrictedMethods.snap_dialog}:confirmation`,
|
||||
SNAP_DIALOG_PROMPT: `${RestrictedMethods.snap_dialog}:prompt`,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
MMI_AUTHENTICATE: 'metamaskinstitutional_authenticate',
|
||||
MMI_REAUTHENTICATE: 'metamaskinstitutional_reauthenticate',
|
||||
MMI_REFRESH_TOKEN: 'metamaskinstitutional_refresh_token',
|
||||
@ -75,7 +63,7 @@ export const MESSAGE_TYPE = {
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} as const;
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
export const SNAP_DIALOG_TYPES = {
|
||||
[DialogType.Alert]: MESSAGE_TYPE.SNAP_DIALOG_ALERT,
|
||||
[DialogType.Confirmation]: MESSAGE_TYPE.SNAP_DIALOG_CONFIRMATION,
|
||||
|
@ -1,2 +0,0 @@
|
||||
export const isMain = process.env.METAMASK_BUILD_TYPE === 'main';
|
||||
export const isFlask = process.env.METAMASK_BUILD_TYPE === 'flask';
|
@ -33,7 +33,7 @@ export enum HardwareAffiliateLinks {
|
||||
ledger = 'https://shop.ledger.com/?r=17c4991a03fa',
|
||||
gridplus = 'https://gridplus.io/?afmc=7p',
|
||||
trezor = 'https://shop.trezor.io/product/trezor-one-black?offer_id=35&aff_id=11009',
|
||||
keystone = 'https://shop.keyst.one/?rfsn=6088257.656b3e9&utm_source=refersion&utm_medium=affiliate&utm_campaign=6088257.656b3e9',
|
||||
keystone = 'https://keyst.one/metamask?rfsn=6088257.656b3e9&utm_source=refersion&utm_medium=affiliate&utm_campaign=6088257.656b3e9',
|
||||
airgap = 'https://airgap.it/',
|
||||
coolwallet = 'https://www.coolwallet.io/',
|
||||
dcent = 'https://dcentwallet.com/',
|
||||
|
@ -538,6 +538,9 @@ export enum MetaMetricsEventName {
|
||||
OnboardingWalletVideoPlay = 'SRP Intro Video Played',
|
||||
OnboardingTwitterClick = 'External Link Clicked',
|
||||
ServiceWorkerRestarted = 'Service Worker Restarted',
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
UserClickedDeepLink = 'User clicked deeplink',
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
|
||||
export enum MetaMetricsEventAccountType {
|
||||
@ -577,6 +580,9 @@ export enum MetaMetricsEventCategory {
|
||||
Wallet = 'Wallet',
|
||||
Desktop = 'Desktop',
|
||||
ServiceWorkers = 'service_workers',
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
MMI = 'Institutional',
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
}
|
||||
|
||||
export enum MetaMetricsEventLinkType {
|
||||
|
@ -540,16 +540,13 @@ export const BUYABLE_CHAINS_MAP: {
|
||||
| typeof CHAIN_IDS.MOONRIVER
|
||||
| typeof CHAIN_IDS.AURORA
|
||||
| typeof CHAIN_IDS.LINEA_TESTNET
|
||||
| typeof CHAIN_IDS.GOERLI
|
||||
>]: BuyableChainSettings;
|
||||
} = {
|
||||
[CHAIN_IDS.MAINNET]: {
|
||||
nativeCurrency: CURRENCY_SYMBOLS.ETH,
|
||||
network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
|
||||
},
|
||||
[CHAIN_IDS.GOERLI]: {
|
||||
nativeCurrency: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.GOERLI],
|
||||
network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
|
||||
},
|
||||
[CHAIN_IDS.SEPOLIA]: {
|
||||
nativeCurrency: TEST_NETWORK_TICKER_MAP[NETWORK_TYPES.SEPOLIA],
|
||||
network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME,
|
||||
|
@ -4,7 +4,7 @@ export const CaveatTypes = Object.freeze({
|
||||
|
||||
export const RestrictedMethods = Object.freeze({
|
||||
eth_accounts: 'eth_accounts',
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
snap_dialog: 'snap_dialog',
|
||||
snap_notify: 'snap_notify',
|
||||
snap_manageState: 'snap_manageState',
|
||||
@ -16,7 +16,7 @@ export const RestrictedMethods = Object.freeze({
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} as const);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
/**
|
||||
* Exclude permissions by code fencing them to avoid any potential usage of excluded permissions at runtime. See: https://github.com/MetaMask/metamask-extension/pull/17321#pullrequestreview-1287014285.
|
||||
* This is a fix for https://github.com/MetaMask/snaps-monorepo/issues/1103 and https://github.com/MetaMask/snaps-monorepo/issues/990.
|
||||
|
@ -1,4 +1,4 @@
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||
import type { SupportedCurve } from '@metamask/key-tree';
|
||||
|
||||
type SnapsMetadata = {
|
||||
|
@ -308,7 +308,7 @@ interface DappSuggestedGasFees {
|
||||
* An object representing a transaction, in whatever state it is in.
|
||||
*/
|
||||
export interface TransactionMeta {
|
||||
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
|
||||
custodyStatus: string;
|
||||
custodyId?: string;
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
@ -1,4 +1,4 @@
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
import browser from 'webextension-polyfill';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
import { memoize } from 'lodash';
|
||||
@ -7,7 +7,7 @@ import {
|
||||
fetchLocale,
|
||||
loadRelativeTimeFormatLocaleData,
|
||||
} from '../../ui/helpers/utils/i18n-helper';
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
import { renderDesktopError } from '../../ui/pages/desktop-error/render-desktop-error';
|
||||
import { EXTENSION_ERROR_PAGE_TYPES } from '../constants/desktop';
|
||||
import { openCustomProtocol } from './deep-linking';
|
||||
@ -44,7 +44,7 @@ export async function getErrorHtml(
|
||||
errorKey,
|
||||
supportLink,
|
||||
metamaskState,
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
err,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
) {
|
||||
@ -65,7 +65,7 @@ export async function getErrorHtml(
|
||||
const { currentLocaleMessages, enLocaleMessages } = response;
|
||||
const t = getLocaleContext(currentLocaleMessages, enLocaleMessages);
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
const isDesktopEnabled = metamaskState?.desktopEnabled === true;
|
||||
|
||||
if (isDesktopEnabled) {
|
||||
@ -120,7 +120,7 @@ export async function getErrorHtml(
|
||||
`;
|
||||
}
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
///: BEGIN:ONLY_INCLUDE_IN(desktop)
|
||||
export const MMD_DOWNLOAD_LINK =
|
||||
'https://github.com/MetaMask/metamask-desktop/releases';
|
||||
|
||||
|
@ -1,14 +1,6 @@
|
||||
let _supportLink = 'https://support.metamask.io';
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(mmi)
|
||||
_supportLink = 'https://mmi-support.zendesk.com/hc/en-us';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
///: BEGIN:ONLY_INCLUDE_IN(flask)
|
||||
_supportLink = 'https://metamask-flask.zendesk.com/hc';
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
|
||||
export const SUPPORT_LINK = _supportLink;
|
||||
// no destructuring as process.env detection stops working
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
export const SUPPORT_LINK = process.env.SUPPORT_LINK;
|
||||
|
||||
export const COINGECKO_LINK = 'https://www.coingecko.com/';
|
||||
export const CRYPTOCOMPARE_LINK = 'https://www.cryptocompare.com/';
|
||||
|
@ -57,7 +57,43 @@
|
||||
"priorityFeeTrend": "down",
|
||||
"networkCongestion": 0.90625
|
||||
},
|
||||
"snaps": [{}],
|
||||
"snaps": {
|
||||
"npm:@metamask/test-snap-bip44": {
|
||||
"id": "npm:@metamask/test-snap-bip44",
|
||||
"origin": "npm:@metamask/test-snap-bip44",
|
||||
"version": "5.1.2",
|
||||
"iconUrl": null,
|
||||
"initialPermissions": {
|
||||
"endowment:ethereum-provider": {}
|
||||
},
|
||||
"manifest": {
|
||||
"description": "An example Snap that signs messages using BLS.",
|
||||
"proposedName": "BIP-44 Test Snap",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MetaMask/test-snaps.git"
|
||||
},
|
||||
"source": {
|
||||
"location": {
|
||||
"npm": {
|
||||
"filePath": "dist/bundle.js",
|
||||
"packageName": "@metamask/test-snap-bip44",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
}
|
||||
},
|
||||
"shasum": "L1k+dT9Q+y3KfIqzaH09MpDZVPS9ZowEh9w01ZMTWMU="
|
||||
},
|
||||
"version": "5.1.2"
|
||||
},
|
||||
"versionHistory": [
|
||||
{
|
||||
"date": 1680686075921,
|
||||
"origin": "https://metamask.github.io",
|
||||
"version": "5.1.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"preferences": {
|
||||
"hideZeroBalanceTokens": false,
|
||||
"showFiatInTestnets": false,
|
||||
|
@ -14,7 +14,7 @@ const {
|
||||
} = require('@metamask/test-dapp/dist/constants.json');
|
||||
|
||||
const hstFactory = {
|
||||
initialAmount: 100,
|
||||
initialAmount: 10,
|
||||
tokenName: 'TST',
|
||||
decimalUnits: 4,
|
||||
tokenSymbol: 'TST',
|
||||
|
@ -92,6 +92,10 @@ describe('Test Snap Management', function () {
|
||||
await driver.delay(1000);
|
||||
|
||||
// try to disable the snap
|
||||
await driver.clickElement({
|
||||
text: 'Notification Test Snap',
|
||||
tag: 'p',
|
||||
});
|
||||
await driver.clickElement('.toggle-button > div');
|
||||
|
||||
// switch back to test-snaps window
|
||||
@ -130,12 +134,11 @@ describe('Test Snap Management', function () {
|
||||
);
|
||||
assert.equal(await notificationResult.getText(), '1');
|
||||
|
||||
// click on see details
|
||||
await driver.clickElement({ text: 'See details', tag: 'button' });
|
||||
await driver.delay(1000);
|
||||
|
||||
// try to remove snap
|
||||
await driver.clickElement({ text: 'Remove snap', tag: 'button' });
|
||||
await driver.clickElement({
|
||||
text: 'Remove Notification Test Snap',
|
||||
tag: 'p',
|
||||
});
|
||||
await driver.delay(1000);
|
||||
|
||||
// try to click remove on popover
|
||||
|
@ -336,11 +336,11 @@ describe('Send ETH from dapp using advanced gas controls', function () {
|
||||
await driver.clickElement({ text: 'Save', tag: 'button' });
|
||||
await driver.waitForSelector({
|
||||
css: '.transaction-detail-item:nth-of-type(1) h6:nth-of-type(2)',
|
||||
text: '0.02367237 ETH',
|
||||
text: '0.04503836 ETH',
|
||||
});
|
||||
await driver.waitForSelector({
|
||||
css: '.transaction-detail-item:nth-of-type(2) h6:nth-of-type(2)',
|
||||
text: '0.02367237 ETH',
|
||||
text: '0.04503836 ETH',
|
||||
});
|
||||
|
||||
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
||||
|
@ -155,7 +155,7 @@ describe('Send ERC20 to a 40 character hexadecimal address', function () {
|
||||
});
|
||||
await driver.waitForSelector({
|
||||
css: '.transaction-detail-item',
|
||||
text: '0.00008346 ETH',
|
||||
text: '0.00008455 ETH',
|
||||
});
|
||||
await driver.clickElement({ text: 'Next', tag: 'button' });
|
||||
|
||||
@ -220,7 +220,7 @@ describe('Send ERC20 to a 40 character hexadecimal address', function () {
|
||||
});
|
||||
await driver.waitForSelector({
|
||||
css: '.transaction-detail-item',
|
||||
text: '0.00008346 ETH',
|
||||
text: '0.00008455 ETH',
|
||||
});
|
||||
await driver.clickElement({ text: 'Next', tag: 'button' });
|
||||
|
||||
|
@ -1 +1,2 @@
|
||||
process.env.METAMASK_ENV = 'test';
|
||||
process.env.METAMASK_ENVIRONMENT = 'test';
|
||||
process.env.SUPPORT_LINK = 'https://support.metamask.io';
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user