diff --git a/.depcheckrc.yml b/.depcheckrc.yml index f7c189f91..50c309973 100644 --- a/.depcheckrc.yml +++ b/.depcheckrc.yml @@ -19,6 +19,7 @@ ignores: - '@metamask/forwarder' - '@metamask/test-dapp' - '@metamask/design-tokens' # Only imported in index.css + - '@tsconfig/node14' # required dynamically by TS, used in tsconfig.json - '@sentry/cli' # invoked as `sentry-cli` - 'chromedriver' - 'depcheck' # ooo meta diff --git a/.eslintrc.js b/.eslintrc.js index 84b68eada..98a57e079 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,8 +7,9 @@ module.exports = { ignorePatterns: [ 'app/vendor/**', 'builds/**/*', - 'dist/**/*', 'development/chromereload.js', + 'dist/**/*', + 'node_modules/**/*', ], overrides: [ /** @@ -41,6 +42,7 @@ module.exports = { path.resolve(__dirname, '.eslintrc.base.js'), path.resolve(__dirname, '.eslintrc.node.js'), path.resolve(__dirname, '.eslintrc.babel.js'), + path.resolve(__dirname, '.eslintrc.typescript-compat.js'), ], parserOptions: { sourceType: 'module', @@ -50,6 +52,23 @@ module.exports = { // trust that all of the files specified above are indeed modules. 'import/unambiguous': 'off', }, + settings: { + 'import/resolver': { + // When determining the location of a `require()` call, use Node's + // resolution algorithm, then fall back to TypeScript's. This allows + // TypeScript files (which Node's algorithm doesn't recognize) to be + // imported from JavaScript files, while also preventing issues when + // using packages like `prop-types` (where we would otherwise get "No + // default export found in imported module 'prop-types'" from + // TypeScript because imports work differently there). + node: {}, + typescript: { + // Always try to resolve types under `/@types` directory even + // it doesn't contain any source code, like `@types/unist` + alwaysTryTypes: true, + }, + }, + }, }, /** * Modules (ES module syntax) @@ -75,10 +94,82 @@ module.exports = { path.resolve(__dirname, '.eslintrc.base.js'), path.resolve(__dirname, '.eslintrc.node.js'), path.resolve(__dirname, '.eslintrc.babel.js'), + path.resolve(__dirname, '.eslintrc.typescript-compat.js'), ], parserOptions: { sourceType: 'module', }, + settings: { + 'import/resolver': { + // When determining the location of an `import`, use Node's resolution + // algorithm, then fall back to TypeScript's. This allows TypeScript + // files (which Node's algorithm doesn't recognize) to be imported + // from JavaScript files, while also preventing issues when using + // packages like `prop-types` (where we would otherwise get "No + // default export found in imported module 'prop-types'" from + // TypeScript because imports work differently there). + node: {}, + typescript: { + // Always try to resolve types under `/@types` directory even + // it doesn't contain any source code, like `@types/unist` + alwaysTryTypes: true, + }, + }, + }, + }, + /** + * TypeScript files + */ + { + files: ['*.{ts,tsx}'], + extends: [ + path.resolve(__dirname, '.eslintrc.base.js'), + '@metamask/eslint-config-typescript', + path.resolve(__dirname, '.eslintrc.typescript-compat.js'), + ], + rules: { + // Turn these off, as it's recommended by typescript-eslint. + // See: + 'import/named': 'off', + 'import/namespace': 'off', + 'import/default': 'off', + 'import/no-named-as-default-member': 'off', + + // Disabled due to incompatibility with Record. + // See: + '@typescript-eslint/consistent-type-definitions': 'off', + + // Modified to include the 'ignoreRestSiblings' option. + // TODO: Migrate this rule change back into `@metamask/eslint-config` + '@typescript-eslint/no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'all', + argsIgnorePattern: '[_]+', + ignoreRestSiblings: true, + }, + ], + }, + settings: { + 'import/resolver': { + // When determining the location of an `import`, prefer TypeScript's + // resolution algorithm. Note that due to how we've configured + // TypeScript in `tsconfig.json`, we are able to import JavaScript + // files from TypeScript files. + typescript: { + // Always try to resolve types under `/@types` directory even + // it doesn't contain any source code, like `@types/unist` + alwaysTryTypes: true, + }, + }, + }, + }, + { + files: ['*.d.ts'], + parserOptions: { + sourceType: 'script', + }, }, /** diff --git a/.eslintrc.typescript-compat.js b/.eslintrc.typescript-compat.js new file mode 100644 index 000000000..564c025df --- /dev/null +++ b/.eslintrc.typescript-compat.js @@ -0,0 +1,8 @@ +module.exports = { + settings: { + 'import/extensions': ['.js', '.ts', '.tsx'], + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + }, +}; diff --git a/.gitignore b/.gitignore index 21c882a97..693537314 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,6 @@ notes.txt .nyc_output .metamaskrc + +# TypeScript +tsout/ diff --git a/.metamaskrc.dist b/.metamaskrc.dist index 8d7507e86..29dbcb2f7 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -5,3 +5,4 @@ SEGMENT_WRITE_KEY= ONBOARDING_V2= SWAPS_USE_DEV_APIS= COLLECTIBLES_V1= +TOKEN_DETECTION_V2= diff --git a/.storybook/__mocks__/webextension-polyfill.js b/.storybook/__mocks__/webextension-polyfill.js new file mode 100644 index 000000000..c8e3dea17 --- /dev/null +++ b/.storybook/__mocks__/webextension-polyfill.js @@ -0,0 +1,4 @@ +module.exports = { + runtime: {}, + }; + \ No newline at end of file diff --git a/.storybook/main.js b/.storybook/main.js index 5ce2f32d7..e7b859b84 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -23,6 +23,7 @@ module.exports = { config.node = { __filename: true, }; + config.resolve.alias['webextension-polyfill'] = require.resolve('./__mocks__/webextension-polyfill.js') config.module.strictExportPresence = true; config.module.rules.push({ test: /\.scss$/, diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json index 998726fa6..bfbbac952 100644 --- a/app/_locales/am/messages.json +++ b/app/_locales/am/messages.json @@ -38,12 +38,6 @@ "addSuggestedTokens": { "message": "የተጠቆሙ ተለዋጭ ስሞችን አክል" }, - "addToAddressBook": { - "message": "በአድራሻ መዝገብ ላይ አክል" - }, - "addToAddressBookModalPlaceholder": { - "message": "ለምሳሌ፡ ጆን ዲ." - }, "addToken": { "message": "ተለዋጭ ስም አክል" }, @@ -331,9 +325,6 @@ "ensRegistrationError": { "message": "በ ENS የስም ምዝገባ ላይ የተፈጠረ ስህተት" }, - "enterAnAlias": { - "message": "ተለዋጭ ስም ያስገቡ" - }, "enterPassword": { "message": "የይለፍ ቃል ያስገቡ" }, diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json index 925a51c0a..e87624ee2 100644 --- a/app/_locales/ar/messages.json +++ b/app/_locales/ar/messages.json @@ -52,12 +52,6 @@ "addSuggestedTokens": { "message": "أضف العملات الرمزية المقترحة" }, - "addToAddressBook": { - "message": "إضافة إلى دفتر العناوين" - }, - "addToAddressBookModalPlaceholder": { - "message": "مثلا جون دي" - }, "addToken": { "message": "إضافة عملة رمزية" }, @@ -348,9 +342,6 @@ "ensRegistrationError": { "message": "خطأ في تسجيل اسم ENS" }, - "enterAnAlias": { - "message": "أدخل اسمًا مستعارًا" - }, "enterPassword": { "message": "أدخل كلمة مرور" }, diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json index ce6c09dd5..9d4534adb 100644 --- a/app/_locales/bg/messages.json +++ b/app/_locales/bg/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Добавете препоръчани жетони" }, - "addToAddressBook": { - "message": "Добавяне към адресната книга" - }, - "addToAddressBookModalPlaceholder": { - "message": "напр. Джон Д." - }, "addToken": { "message": "Добавяне на жетон" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Грешка при регистрацията на име на ENS" }, - "enterAnAlias": { - "message": "Въведете псевдоним" - }, "enterPassword": { "message": "Въведете парола" }, diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json index e9a0a85fc..7dd74c5eb 100644 --- a/app/_locales/bn/messages.json +++ b/app/_locales/bn/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "প্রস্তাবিত টোকেনগুলি যোগ করুন" }, - "addToAddressBook": { - "message": "অ্যাড্রেস বুকে যোগ করুন" - }, - "addToAddressBookModalPlaceholder": { - "message": "যেমন জন.ডি." - }, "addToken": { "message": "টোকেন যোগ করুন" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "ENS নাম নিবন্ধীকরণে ত্রুটি হয়েছে" }, - "enterAnAlias": { - "message": "একটি উপনাম লিখুন" - }, "enterPassword": { "message": "পাসওয়ার্ড লিখুন" }, diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json index 2d0ab7177..34abb31dc 100644 --- a/app/_locales/ca/messages.json +++ b/app/_locales/ca/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Afegir Fitxes Suggerides" }, - "addToAddressBook": { - "message": "Afegir al llibre d'adreces" - }, - "addToAddressBookModalPlaceholder": { - "message": "p. ex. John D." - }, "addToken": { "message": "Afegir Fitxa" }, @@ -337,9 +331,6 @@ "ensRegistrationError": { "message": "Error al registre de nom ENS" }, - "enterAnAlias": { - "message": "Introdueix Àlies" - }, "enterPassword": { "message": "Introdueix contrasenya" }, diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json index 0067c616c..248404f43 100644 --- a/app/_locales/da/messages.json +++ b/app/_locales/da/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Tilføj foreslåede tokens" }, - "addToAddressBook": { - "message": "Føj til adressebogen" - }, - "addToAddressBookModalPlaceholder": { - "message": "f.eks. John D." - }, "addToken": { "message": "Tilføj Polet" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Fejl i ENS-navneregistrering" }, - "enterAnAlias": { - "message": "Indtast et alias" - }, "enterPassword": { "message": "Indtast kodeord" }, diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json index 35b7c39f5..4c7749a01 100644 --- a/app/_locales/de/messages.json +++ b/app/_locales/de/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Vorgeschlagene Token hinzufügen" }, - "addToAddressBook": { - "message": "Zum Adressbuch hinzufügen" - }, - "addToAddressBookModalPlaceholder": { - "message": "z.B. Max M." - }, "addToken": { "message": "Token hinzufügen" }, @@ -962,9 +956,6 @@ "ensUnknownError": { "message": "ENS Lookup fehlgeschlagen." }, - "enterAnAlias": { - "message": "Ein Alias eingeben" - }, "enterMaxSpendLimit": { "message": "Max. Ausgabelimit eingeben" }, @@ -1417,9 +1408,6 @@ "lastConnected": { "message": "Zuletzt verbunden" }, - "layer1Fees": { - "message": "Ebene 1 Gebühren" - }, "learmMoreAboutGas": { "message": "Wollen Sie 1 $ über Gas?" }, diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json index 0de718e3c..f91fc7653 100644 --- a/app/_locales/el/messages.json +++ b/app/_locales/el/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Προσθέστε τα Προτεινόμενα Tokens" }, - "addToAddressBook": { - "message": "Προσθήκη στο βιβλίο διευθύνσεων" - }, - "addToAddressBookModalPlaceholder": { - "message": "π.χ. John D." - }, "addToken": { "message": "Προσθήκη Token" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "Η αναζήτηση ENS απέτυχε." }, - "enterAnAlias": { - "message": "Δώστε ένα ψευδώνυμο" - }, "enterMaxSpendLimit": { "message": "Εισάγετε Το Μέγιστο Όριο Δαπάνης" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "Τελευταία Σύνδεση" }, - "layer1Fees": { - "message": "Τέλη επιπέδου 1" - }, "learmMoreAboutGas": { "message": "Θέλετε να $1 για το τέλος συναλλαγής;" }, diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index d74f6cca4..35f16c6fb 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -156,12 +156,6 @@ "addSuggestedTokens": { "message": "Add Suggested Tokens" }, - "addToAddressBook": { - "message": "Add to address book" - }, - "addToAddressBookModalPlaceholder": { - "message": "e.g. John D." - }, "addToken": { "message": "Add Token" }, @@ -411,6 +405,17 @@ "buy": { "message": "Buy" }, + "buyAsset": { + "message": "Buy $1", + "description": "$1 is the ticker symbol of a an asset the user is being prompted to purchase" + }, + "buyCryptoWithMoonPay": { + "message": "Buy $1 with MoonPay", + "description": "$1 represents the cypto symbol to be purchased" + }, + "buyCryptoWithMoonPayDescription": { + "message": "MoonPay supports popular payment methods, including Visa, Mastercard, Apple / Google / Samsung Pay, and bank transfers in 145+ countries. Tokens deposit into your MetaMask account." + }, "buyCryptoWithTransak": { "message": "Buy $1 with Transak", "description": "$1 represents the cypto symbol to be purchased" @@ -419,13 +424,6 @@ "message": "Transak supports credit & debit cards, Apple Pay, MobiKwik, and bank transfers (depending on location) in 100+ countries. $1 deposits directly into your MetaMask account.", "description": "$1 represents the crypto symbol to be purchased" }, - "buyEth": { - "message": "Buy ETH" - }, - "buyOther": { - "message": "Buy $1 or deposit from another account.", - "description": "$1 is a token symbol" - }, "buyWithWyre": { "message": "Buy ETH with Wyre" }, @@ -448,7 +446,7 @@ "message": "Cancel transaction" }, "cancelSpeedUp": { - "message": "cancel or speed up a tranaction." + "message": "cancel or speed up a transaction." }, "cancelSpeedUpLabel": { "message": "This gas fee will $1 the original.", @@ -617,6 +615,9 @@ "continue": { "message": "Continue" }, + "continueToMoonPay": { + "message": "Continue to MoonPay" + }, "continueToTransak": { "message": "Continue to Transak" }, @@ -1096,9 +1097,6 @@ "ensUnknownError": { "message": "ENS Lookup failed." }, - "enterAnAlias": { - "message": "Enter an alias" - }, "enterMaxSpendLimit": { "message": "Enter Max Spend Limit" }, @@ -1260,6 +1258,9 @@ "message": "From: $1", "description": "$1 is the address to include in the From label. It is typically shortened first using shortenAddress" }, + "fromTokenLists": { + "message": "From token lists: $1" + }, "functionApprove": { "message": "Function: Approve" }, @@ -1279,6 +1280,9 @@ "gasEstimatesUnavailableWarning": { "message": "Our low, medium and high estimates are not available." }, + "gasFee": { + "message": "Gas Fee" + }, "gasLimit": { "message": "Gas Limit" }, @@ -1524,9 +1528,13 @@ "insufficientBalance": { "message": "Insufficient balance." }, - "insufficientCurrency": { - "message": "You do not have enough $1 in your account to pay for transaction fees on $2 network.", - "description": "$1 is currency, $2 is network" + "insufficientCurrencyBuyOrDeposit": { + "message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. $3 or deposit from another account.", + "description": "$1 is the native currency of the network, $2 is the name of the current network, $3 is the key 'buy' + the ticker symbol of the native currency of the chain wrapped in a button" + }, + "insufficientCurrencyDeposit": { + "message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. Deposit $1 from another account.", + "description": "$1 is the native currency of the network, $2 is the name of the current network" }, "insufficientFunds": { "message": "Insufficient funds." @@ -1627,9 +1635,6 @@ "lastConnected": { "message": "Last Connected" }, - "layer1Fees": { - "message": "Layer 1 fees" - }, "learmMoreAboutGas": { "message": "Want to $1 about gas?" }, @@ -2095,6 +2100,28 @@ "notEnoughGas": { "message": "Not Enough Gas" }, + "notifications10ActionText": { + "message": "Visit in settings", + "description": "The 'call to action' on the button, or link, of the 'Visit in settings' notification. Upon clicking, users will be taken to settings page." + }, + "notifications10DescriptionOne": { + "message": "Improved token detection is currently available on Ethereum Mainnet, Polygon, BSC, and Avalanche networks. More to come!" + }, + "notifications10DescriptionThree": { + "message": "Token detection feature is ON by default. But you can disable it from Settings." + }, + "notifications10DescriptionTwo": { + "message": "We source tokens from third party tokens lists. Tokens listed on more than two token lists will be automatically detected." + }, + "notifications10Title": { + "message": "Improved token detection is here" + }, + "notifications11Description": { + "message": "Tokens can be created by anyone and can have duplicate names. If you see a token appear that you don’t trust or haven’t interacted with - it’s safer to not trust it." + }, + "notifications11Title": { + "message": "Scam and security risks" + }, "notifications1Description": { "message": "MetaMask Mobile users can now swap tokens inside their mobile wallet. Scan the QR code to get the mobile app and start swapping.", "description": "Description of a notification in the 'See What's New' popup. Describes the swapping on mobile feature." @@ -2278,9 +2305,6 @@ "or": { "message": "or" }, - "orDeposit": { - "message": "or deposit from another account." - }, "origin": { "message": "Origin" }, @@ -2362,6 +2386,10 @@ "message": "Store and manage its data on your device.", "description": "The description for the `snap_manageState` permission" }, + "permission_notifications": { + "message": "Show notifications.", + "description": "The description for the `snap_notify` permission" + }, "permission_unknown": { "message": "Unknown permission: $1", "description": "$1 is the name of a requested permission that is not recognized." @@ -2590,6 +2618,9 @@ "rpcUrl": { "message": "New RPC URL" }, + "safeTransferFrom": { + "message": "Safe Transfer From" + }, "save": { "message": "Save" }, @@ -3531,6 +3562,9 @@ "token": { "message": "Token" }, + "tokenAddress": { + "message": "Token address" + }, "tokenAlreadyAdded": { "message": "Token has already been added." }, @@ -3546,12 +3580,21 @@ "tokenDetails": { "message": "Token details" }, + "tokenDetection": { + "message": "Token detection" + }, "tokenDetectionAnnouncement": { "message": "New! Improved token detection is available on Ethereum Mainnet as an experimental feature. $1" }, + "tokenDetectionToggleDescription": { + "message": "ConsenSys’ token API aggregates a list of tokens from various third party token lists. Turning it off will stop detecting new tokens added to your wallet, but will keep the option to search for tokens to import." + }, "tokenId": { "message": "Token ID" }, + "tokenList": { + "message": "Token lists:" + }, "tokenSymbol": { "message": "Token Symbol" }, diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json index 0ce491da5..7f1cca3fd 100644 --- a/app/_locales/es/messages.json +++ b/app/_locales/es/messages.json @@ -79,12 +79,6 @@ "addSuggestedTokens": { "message": "Agregar tokens sugeridos" }, - "addToAddressBook": { - "message": "Agregar a la libreta de direcciones" - }, - "addToAddressBookModalPlaceholder": { - "message": "p. ej., John D." - }, "addToken": { "message": "Agregar token" }, @@ -612,9 +606,6 @@ "ensRegistrationError": { "message": "Error en el registro del nombre de ENS" }, - "enterAnAlias": { - "message": "Escribir un alias" - }, "enterMaxSpendLimit": { "message": "Escribir límite máximo de gastos" }, diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json index bb9858c3f..759bc10bf 100644 --- a/app/_locales/es_419/messages.json +++ b/app/_locales/es_419/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Agregar tokens sugeridos" }, - "addToAddressBook": { - "message": "Agregar a la libreta de direcciones" - }, - "addToAddressBookModalPlaceholder": { - "message": "p. ej., John D." - }, "addToken": { "message": "Agregar token" }, @@ -982,9 +976,6 @@ "ensUnknownError": { "message": "Error al buscar ENS." }, - "enterAnAlias": { - "message": "Escribir un alias" - }, "enterMaxSpendLimit": { "message": "Escribir límite máximo de gastos" }, @@ -1463,9 +1454,6 @@ "lastConnected": { "message": "Última conexión" }, - "layer1Fees": { - "message": "Cargos de capa 1" - }, "learmMoreAboutGas": { "message": "¿Quiere $1 sobre el gas?" }, diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json index b6f929aab..bb5be3a52 100644 --- a/app/_locales/et/messages.json +++ b/app/_locales/et/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Lisa soovitatud lube" }, - "addToAddressBook": { - "message": "Lisage aadressiraamatusse" - }, - "addToAddressBookModalPlaceholder": { - "message": "nt John D." - }, "addToken": { "message": "Lisage luba" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Tõrge ENS-i nime registreerimisel" }, - "enterAnAlias": { - "message": "Sisestage alias" - }, "enterPassword": { "message": "Sisestage parool" }, diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json index c4cc6a32c..2669d7321 100644 --- a/app/_locales/fa/messages.json +++ b/app/_locales/fa/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "اضافه رمزیاب های پیشنهاد شده" }, - "addToAddressBook": { - "message": "به کتاب آدرسها اضافه کنید" - }, - "addToAddressBookModalPlaceholder": { - "message": "مثلًا. John D." - }, "addToken": { "message": "یک رمز یاب اضافه کنید" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "خطا در ثبت نام ENS" }, - "enterAnAlias": { - "message": "یک نام مستعار را وارد کنید" - }, "enterPassword": { "message": "رمز عبور را وارد کنید" }, diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json index 3301f21dd..08e734237 100644 --- a/app/_locales/fi/messages.json +++ b/app/_locales/fi/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Lisää ehdotetut käyttötunnukset" }, - "addToAddressBook": { - "message": "Lisää osoitekirjaan" - }, - "addToAddressBookModalPlaceholder": { - "message": "esim. Jukka E." - }, "addToken": { "message": "Lisää tietue" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Virhe ENS-nimen rekisteröinnissä" }, - "enterAnAlias": { - "message": "Anna peitenimi" - }, "enterPassword": { "message": "Kirjoita salasana" }, diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json index 474dcc1b0..54a59f2ad 100644 --- a/app/_locales/fil/messages.json +++ b/app/_locales/fil/messages.json @@ -41,12 +41,6 @@ "addSuggestedTokens": { "message": "Magdagdag ng Mga Iminungkahing Token" }, - "addToAddressBook": { - "message": "Idagdag sa address book" - }, - "addToAddressBookModalPlaceholder": { - "message": "hal. Juan D." - }, "addToken": { "message": "Magdagdag ng Token" }, @@ -316,9 +310,6 @@ "ensRegistrationError": { "message": "May error sa pagrerehistro ng ENS name" }, - "enterAnAlias": { - "message": "Maglagay ng alias" - }, "enterPassword": { "message": "Ilagay ang password" }, diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json index e5cd939f9..69d8a2d5a 100644 --- a/app/_locales/fr/messages.json +++ b/app/_locales/fr/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Ajouter les jetons suggérés" }, - "addToAddressBook": { - "message": "Ajouter au carnet d’adresses" - }, - "addToAddressBookModalPlaceholder": { - "message": "p. ex. John D." - }, "addToken": { "message": "Ajouter un jeton" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "La recherche d’ENS a échoué." }, - "enterAnAlias": { - "message": "Saisissez un pseudonyme" - }, "enterMaxSpendLimit": { "message": "Saisissez la limite de dépenses maximale" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "Dernière connexion" }, - "layer1Fees": { - "message": "Frais de couche 1 (L1)" - }, "learmMoreAboutGas": { "message": "Souhaitez-vous $1 sur le carburant ?" }, diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json index 36beece50..1a9b8a265 100644 --- a/app/_locales/he/messages.json +++ b/app/_locales/he/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "הוסף/י אסימונים מוצעים" }, - "addToAddressBook": { - "message": "הוסף לפנקס הכתובות" - }, - "addToAddressBookModalPlaceholder": { - "message": "למשל ג'ון ד'" - }, "addToken": { "message": "הוסף/י אסימון" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "שגיאה ברישום שם ENS" }, - "enterAnAlias": { - "message": "יש להזין כינוי" - }, "enterPassword": { "message": "יש להזין ססמה" }, diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json index 8b8531835..69b111159 100644 --- a/app/_locales/hi/messages.json +++ b/app/_locales/hi/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "सुझाए गए टोकन जोड़ें" }, - "addToAddressBook": { - "message": "पता पुस्तिका में जोड़ें" - }, - "addToAddressBookModalPlaceholder": { - "message": "जैसे जॉन डी." - }, "addToken": { "message": "टोकन जोड़ें" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "ENS लुकअप विफल हुआ।" }, - "enterAnAlias": { - "message": "एक उपनाम दर्ज करें" - }, "enterMaxSpendLimit": { "message": "अधिकतम खर्च सीमा दर्ज करें" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "अंतिम बार कनेक्ट किया गया" }, - "layer1Fees": { - "message": "लेयर 1 शुल्क" - }, "learmMoreAboutGas": { "message": "गैस के बारे में $1 चाहते हैं?" }, diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json index c466cb53c..49c270406 100644 --- a/app/_locales/hr/messages.json +++ b/app/_locales/hr/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Dodaj predložene tokene" }, - "addToAddressBook": { - "message": "Dodaj u imenik" - }, - "addToAddressBookModalPlaceholder": { - "message": "npr. Ivan M." - }, "addToken": { "message": "Dodaj token" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Greška u registraciji naziva ENS" }, - "enterAnAlias": { - "message": "Upiši pseudonim" - }, "enterPassword": { "message": "Upiši lozinku" }, diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json index f96e8b911..10f60bba4 100644 --- a/app/_locales/hu/messages.json +++ b/app/_locales/hu/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Javasolt tokenek hozzáadása" }, - "addToAddressBook": { - "message": "Hozzáadás a címjegyzékhez" - }, - "addToAddressBookModalPlaceholder": { - "message": "pl. John D." - }, "addToken": { "message": "Token hozzáadása" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Hiba történt az ENS név regisztrációjakor" }, - "enterAnAlias": { - "message": "Adjon meg álnevet" - }, "enterPassword": { "message": "Adja meg a jelszót" }, diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json index a66e1d4d6..720cbd97d 100644 --- a/app/_locales/id/messages.json +++ b/app/_locales/id/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Tambahkan Token yang Disarankan" }, - "addToAddressBook": { - "message": "Tambahkan ke buku alamat" - }, - "addToAddressBookModalPlaceholder": { - "message": "contoh: John D." - }, "addToken": { "message": "Tambahkan Token" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "Pencarian ENS gagal." }, - "enterAnAlias": { - "message": "Masukkan alias" - }, "enterMaxSpendLimit": { "message": "Masukkan Batas Penggunaan Maksimum" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "Terakhir Terhubung" }, - "layer1Fees": { - "message": "Biaya lapis 1" - }, "learmMoreAboutGas": { "message": "Ingin $1 seputar gas?" }, diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json index 058c26f89..7e92d2b21 100644 --- a/app/_locales/it/messages.json +++ b/app/_locales/it/messages.json @@ -55,12 +55,6 @@ "addSuggestedTokens": { "message": "Aggiungi Token Suggeriti" }, - "addToAddressBook": { - "message": "Aggiungi alla rubrica" - }, - "addToAddressBookModalPlaceholder": { - "message": "Ad esempio, John D." - }, "addToken": { "message": "Aggiungi Token" }, @@ -527,9 +521,6 @@ "ensRegistrationError": { "message": "Errore nella registrazione del nome ENS" }, - "enterAnAlias": { - "message": "Inserisci un alias" - }, "enterMaxSpendLimit": { "message": "Inserisici Limite Spesa" }, diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json index b59af3082..4107b4396 100644 --- a/app/_locales/ja/messages.json +++ b/app/_locales/ja/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "推奨されたトークンを追加" }, - "addToAddressBook": { - "message": "アドレス帳に追加" - }, - "addToAddressBookModalPlaceholder": { - "message": "例: John D." - }, "addToken": { "message": "トークンを追加" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "ENSの検索に失敗しました。" }, - "enterAnAlias": { - "message": "別名を入力してください" - }, "enterMaxSpendLimit": { "message": "使用限度額の最大値を入力してください" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "前回の接続" }, - "layer1Fees": { - "message": "レイヤー1手数料" - }, "learmMoreAboutGas": { "message": "ガスについて$1しますか?" }, diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json index 1c575b4a9..1edff554c 100644 --- a/app/_locales/kn/messages.json +++ b/app/_locales/kn/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "ಸೂಚಿಸಲಾದ ಟೋಕನ್‌ಗಳನ್ನು ಸೇರಿಸಿ" }, - "addToAddressBook": { - "message": "ವಿಳಾಸ ಪುಸ್ತಕಕ್ಕೆ ಸೇರಿಸಿ" - }, - "addToAddressBookModalPlaceholder": { - "message": "ಉದಾ. ಜಾನ್ ಡಿ." - }, "addToken": { "message": "ಟೋಕನ್ ಸೇರಿಸಿ" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "ENS ಹೆಸರಿನ ನೋಂದಣಿಯಲ್ಲಿ ದೋಷ" }, - "enterAnAlias": { - "message": "ಆಲಿಯಾಸ್ ಅನ್ನು ನಮೂದಿಸಿ" - }, "enterPassword": { "message": "ಪಾಸ್‌ವರ್ಡ್‌ ಅನ್ನು ನಮೂದಿಸಿ" }, diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json index c000290ed..35c9e2c4d 100644 --- a/app/_locales/ko/messages.json +++ b/app/_locales/ko/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "추천 토큰 추가" }, - "addToAddressBook": { - "message": "주소록에 추가" - }, - "addToAddressBookModalPlaceholder": { - "message": "예: John D." - }, "addToken": { "message": "토큰 추가" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "ENS를 조회하지 못했습니다." }, - "enterAnAlias": { - "message": "별칭 입력" - }, "enterMaxSpendLimit": { "message": "최대 지출 한도 입력" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "마지막 연결" }, - "layer1Fees": { - "message": "레이어 1 요금" - }, "learmMoreAboutGas": { "message": "가스에 대해 $1하시겠습니까?" }, diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json index 240a7c198..f2866345c 100644 --- a/app/_locales/lt/messages.json +++ b/app/_locales/lt/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Pridėti siūlomų žetonų" }, - "addToAddressBook": { - "message": "Pridėti į adresų knygelę" - }, - "addToAddressBookModalPlaceholder": { - "message": "pvz., John D." - }, "addToken": { "message": "Pridėti žetoną" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "ENS pavadinimo registracijos klaida" }, - "enterAnAlias": { - "message": "Įveskite alternatyvųjį vardą" - }, "enterPassword": { "message": "Įveskite slaptažodį" }, diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json index e41892d56..13233a066 100644 --- a/app/_locales/lv/messages.json +++ b/app/_locales/lv/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Pievienot ieteiktos marķierus" }, - "addToAddressBook": { - "message": "Pievienot adrešu grāmatai" - }, - "addToAddressBookModalPlaceholder": { - "message": "piemēram, Džons D." - }, "addToken": { "message": "Pievienot žetonu" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Kļūda ENS vārda reģistrācijā" }, - "enterAnAlias": { - "message": "Ievadiet segvārdu" - }, "enterPassword": { "message": "Ievadiet paroli" }, diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json index b62072110..bc42b1bc2 100644 --- a/app/_locales/ms/messages.json +++ b/app/_locales/ms/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Tambah Token yang Disyorkan" }, - "addToAddressBook": { - "message": "Tambah kepada buku alamat" - }, - "addToAddressBookModalPlaceholder": { - "message": "cth. John D." - }, "addToken": { "message": "Tambah Token" }, @@ -337,9 +331,6 @@ "ensRegistrationError": { "message": "Ralat dalam pendaftaran nama ENS" }, - "enterAnAlias": { - "message": "Masukkan alias" - }, "enterPassword": { "message": "Masukkan kata laluan" }, diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json index 2f4f94f53..ceb83e982 100644 --- a/app/_locales/no/messages.json +++ b/app/_locales/no/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Legg til foreslåtte tokener " }, - "addToAddressBook": { - "message": "Legg til adressebok " - }, - "addToAddressBookModalPlaceholder": { - "message": "dvs. John D. " - }, "addToken": { "message": "Legg til token " }, @@ -337,9 +331,6 @@ "ensRegistrationError": { "message": "Feil i ENS-navneregistrering" }, - "enterAnAlias": { - "message": "Oppgi et alias" - }, "enterPassword": { "message": "Skriv inn passord " }, diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json index be9cafadc..36da6fd32 100644 --- a/app/_locales/ph/messages.json +++ b/app/_locales/ph/messages.json @@ -79,12 +79,6 @@ "addSuggestedTokens": { "message": "Magdagdag ng Mga Iminumungkahing Token" }, - "addToAddressBook": { - "message": "Idagdag sa address book" - }, - "addToAddressBookModalPlaceholder": { - "message": "hal. John D." - }, "addToken": { "message": "Magdagdag ng Token" }, @@ -615,9 +609,6 @@ "ensRegistrationError": { "message": "Nagka-error sa pag-register ng ENS name" }, - "enterAnAlias": { - "message": "Maglagay ng alias" - }, "enterMaxSpendLimit": { "message": "Ilagay ang Max na Limitasyon sa Paggastos" }, diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json index 8d5e1019e..d8637876a 100644 --- a/app/_locales/pl/messages.json +++ b/app/_locales/pl/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Dodaj sugerowane tokeny." }, - "addToAddressBook": { - "message": "Dodaj do książki adresowej" - }, - "addToAddressBookModalPlaceholder": { - "message": "np. John D." - }, "addToken": { "message": "Dodaj token" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Błąd w rejestracji nazwy ENS" }, - "enterAnAlias": { - "message": "Wpisz alias" - }, "enterPassword": { "message": "Wpisz hasło" }, diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json index 8b161b3cf..793a95650 100644 --- a/app/_locales/pt_BR/messages.json +++ b/app/_locales/pt_BR/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Adicionar tokens sugeridos" }, - "addToAddressBook": { - "message": "Adicionar à agenda de endereços" - }, - "addToAddressBookModalPlaceholder": { - "message": "por ex., João S." - }, "addToken": { "message": "Adicionar token" }, @@ -966,9 +960,6 @@ "ensUnknownError": { "message": "Falha na busca de ENS." }, - "enterAnAlias": { - "message": "Digite um pseudônimo" - }, "enterMaxSpendLimit": { "message": "Digite um limite máximo de gastos" }, @@ -1447,9 +1438,6 @@ "lastConnected": { "message": "Conectado pela última vez em" }, - "layer1Fees": { - "message": "Taxas de camada 1" - }, "learmMoreAboutGas": { "message": "Quer $1 sobre o gás?" }, diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json index 06cf6f692..929bede12 100644 --- a/app/_locales/ro/messages.json +++ b/app/_locales/ro/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Adăugați indicativele sugerate" }, - "addToAddressBook": { - "message": "Adăugați în agendă" - }, - "addToAddressBookModalPlaceholder": { - "message": "de ex. John D." - }, "addToken": { "message": "Adăugare simbol" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Eroare la înregistrarea numelui ENS" }, - "enterAnAlias": { - "message": "Introduceți un alias" - }, "enterPassword": { "message": "Introduceți parola" }, diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json index 49d76144d..fed0f9550 100644 --- a/app/_locales/ru/messages.json +++ b/app/_locales/ru/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Добавить рекомендованные токены" }, - "addToAddressBook": { - "message": "Добавить в адресную книгу" - }, - "addToAddressBookModalPlaceholder": { - "message": "напр., Джон Д." - }, "addToken": { "message": "Добавить токен" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "Ошибка поиска ENS." }, - "enterAnAlias": { - "message": "Ввести псевдоним" - }, "enterMaxSpendLimit": { "message": "Введите максимальный лимит расходов" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "Последнее подключение" }, - "layer1Fees": { - "message": "Комиссии 1-го уровня" - }, "learmMoreAboutGas": { "message": "Хотите $1 о газе?" }, diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json index 0fd6d8bb5..b5f1a087f 100644 --- a/app/_locales/sk/messages.json +++ b/app/_locales/sk/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Pridať navrhované tokeny" }, - "addToAddressBook": { - "message": "Pridať do adresára" - }, - "addToAddressBookModalPlaceholder": { - "message": "napr. Ján D." - }, "addToken": { "message": "Přidat token" }, @@ -334,9 +328,6 @@ "ensRegistrationError": { "message": "Chyba pri registrácii názvu ENS" }, - "enterAnAlias": { - "message": "Zadať alias" - }, "enterPassword": { "message": "Zadejte heslo" }, diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json index 4ebce0449..8a49c004a 100644 --- a/app/_locales/sl/messages.json +++ b/app/_locales/sl/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Dodaj priporočene žetone" }, - "addToAddressBook": { - "message": "Dodaj stik v imenik" - }, - "addToAddressBookModalPlaceholder": { - "message": "npr. Jaka N." - }, "addToken": { "message": "Dodaj žeton" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Napaka pri registraciji imena ENS" }, - "enterAnAlias": { - "message": "Vnesite vzdevek" - }, "enterPassword": { "message": "Vnesite geslo" }, diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json index f37701547..9f29a6bf5 100644 --- a/app/_locales/sr/messages.json +++ b/app/_locales/sr/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Dodajte sugerisane tokene" }, - "addToAddressBook": { - "message": "Dodaj u adresar" - }, - "addToAddressBookModalPlaceholder": { - "message": "npr. John D." - }, "addToken": { "message": "Dodaj token" }, @@ -337,9 +331,6 @@ "ensRegistrationError": { "message": "Greška u registraciji ENS imena." }, - "enterAnAlias": { - "message": "Unesite pseudonim" - }, "enterPassword": { "message": "Unesite lozinku" }, diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json index 447dcde42..2e1c5721e 100644 --- a/app/_locales/sv/messages.json +++ b/app/_locales/sv/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Lägg till föreslagna tokens" }, - "addToAddressBook": { - "message": "Lägg till i adressbok" - }, - "addToAddressBookModalPlaceholder": { - "message": "exempelvis John D." - }, "addToken": { "message": "Lägg till token" }, @@ -334,9 +328,6 @@ "ensRegistrationError": { "message": "Fel i ENS-namnregistrering" }, - "enterAnAlias": { - "message": "Ange ett alias" - }, "enterPassword": { "message": "Ange lösenord" }, diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json index dc1a61406..990fd2e43 100644 --- a/app/_locales/sw/messages.json +++ b/app/_locales/sw/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Ongeza Vianzio Vilivyopendekezwa" }, - "addToAddressBook": { - "message": "Ongeza kwenye kitabu cha anwani" - }, - "addToAddressBookModalPlaceholder": { - "message": "k.m John D." - }, "addToken": { "message": "Ongeza Kianzio" }, @@ -334,9 +328,6 @@ "ensRegistrationError": { "message": "Hitilafu imetokea kwenye usajili wa jina la ENS" }, - "enterAnAlias": { - "message": "Ingiza majina mengine" - }, "enterPassword": { "message": "Ingiza nenosiri" }, diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json index b9f400d7e..fa2d0773f 100644 --- a/app/_locales/tl/messages.json +++ b/app/_locales/tl/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Magdagdag ng Mga Iminumungkahing Token" }, - "addToAddressBook": { - "message": "Idagdag sa address book" - }, - "addToAddressBookModalPlaceholder": { - "message": "hal. John D." - }, "addToken": { "message": "Magdagdag ng Token" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "Bigong Makita ang ENS." }, - "enterAnAlias": { - "message": "Maglagay ng alias" - }, "enterMaxSpendLimit": { "message": "Ilagay ang Max na Limitasyon sa Paggastos" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "Huling Kumonekta" }, - "layer1Fees": { - "message": "Layer 1 fees" - }, "learmMoreAboutGas": { "message": "Gusto mo bang $1 ang tungkol sa gas?" }, diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json index 9cd734cdc..6633eccba 100644 --- a/app/_locales/tr/messages.json +++ b/app/_locales/tr/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Önerilen Tokenleri ekle" }, - "addToAddressBook": { - "message": "Adres defterine ekle" - }, - "addToAddressBookModalPlaceholder": { - "message": "ör. Hüseyin M." - }, "addToken": { "message": "Token ekle" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "ENS Arama başarısız oldu." }, - "enterAnAlias": { - "message": "Bir diğer ad girin" - }, "enterMaxSpendLimit": { "message": "Maks. Harcama Limiti Gir" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "Son Bağlanma" }, - "layer1Fees": { - "message": "Aşama 1 ücretleri" - }, "learmMoreAboutGas": { "message": "Gaz hakkında $1 istiyor musunuz?" }, diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json index 403cafd28..c2e046eb4 100644 --- a/app/_locales/uk/messages.json +++ b/app/_locales/uk/messages.json @@ -44,12 +44,6 @@ "addSuggestedTokens": { "message": "Додати рекомендовані токени" }, - "addToAddressBook": { - "message": "Додати до адресної книги" - }, - "addToAddressBookModalPlaceholder": { - "message": "Напр.: Джон Д." - }, "addToken": { "message": "Додати токен" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "Помилка у реєстрації ENS ім'я" }, - "enterAnAlias": { - "message": "Введіть псевдонім" - }, "enterPassword": { "message": "Введіть пароль" }, diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json index de29ab1d5..d2e38f2f1 100644 --- a/app/_locales/vi/messages.json +++ b/app/_locales/vi/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "Thêm token được đề xuất" }, - "addToAddressBook": { - "message": "Thêm vào sổ địa chỉ" - }, - "addToAddressBookModalPlaceholder": { - "message": "ví dụ: John D." - }, "addToken": { "message": "Thêm token" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "Tra Cứu ENS thất bại." }, - "enterAnAlias": { - "message": "Nhập một biệt danh" - }, "enterMaxSpendLimit": { "message": "Nhập giới hạn chi tiêu tối đa" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "Đã kết nối lần cuối" }, - "layer1Fees": { - "message": "Phí Lớp 1" - }, "learmMoreAboutGas": { "message": "Muốn $1 về gas?" }, diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json index 943a36afd..94bc07f8f 100644 --- a/app/_locales/zh_CN/messages.json +++ b/app/_locales/zh_CN/messages.json @@ -143,12 +143,6 @@ "addSuggestedTokens": { "message": "添加推荐代币" }, - "addToAddressBook": { - "message": "添加地址簿" - }, - "addToAddressBookModalPlaceholder": { - "message": "如:John D." - }, "addToken": { "message": "添加代币" }, @@ -952,9 +946,6 @@ "ensUnknownError": { "message": "ENS 查找失败。" }, - "enterAnAlias": { - "message": "输入别名" - }, "enterMaxSpendLimit": { "message": "输入最高消费额度" }, @@ -1414,9 +1405,6 @@ "lastConnected": { "message": "最后连接" }, - "layer1Fees": { - "message": "1层费用" - }, "learmMoreAboutGas": { "message": "想要有关燃料$1?" }, diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json index b16184e25..94a79bdb3 100644 --- a/app/_locales/zh_TW/messages.json +++ b/app/_locales/zh_TW/messages.json @@ -41,12 +41,6 @@ "addSuggestedTokens": { "message": "加入建議的代幣" }, - "addToAddressBook": { - "message": "新增至位址簿中" - }, - "addToAddressBookModalPlaceholder": { - "message": "如 John D。" - }, "addToken": { "message": "加入代幣" }, @@ -340,9 +334,6 @@ "ensRegistrationError": { "message": "ENS 名稱註冊錯誤" }, - "enterAnAlias": { - "message": "輸入化名" - }, "enterPassword": { "message": "請輸入密碼" }, diff --git a/app/images/address-book.svg b/app/images/address-book.svg deleted file mode 100644 index 7559babab..000000000 --- a/app/images/address-book.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/advanced-icon.svg b/app/images/advanced-icon.svg deleted file mode 100644 index e53e41788..000000000 --- a/app/images/advanced-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/alert-red.svg b/app/images/alert-red.svg deleted file mode 100644 index 29aa77e8c..000000000 --- a/app/images/alert-red.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/app/images/alert.svg b/app/images/alert.svg deleted file mode 100644 index 7238cfb29..000000000 --- a/app/images/alert.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/alerts-icon.svg b/app/images/alerts-icon.svg deleted file mode 100644 index 79498650c..000000000 --- a/app/images/alerts-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/arrow-popout.svg b/app/images/arrow-popout.svg deleted file mode 100644 index c3a11e45f..000000000 --- a/app/images/arrow-popout.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/arrow-right.svg b/app/images/arrow-right.svg deleted file mode 100644 index 158e63c82..000000000 --- a/app/images/arrow-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/check-green-solid.svg b/app/images/check-green-solid.svg deleted file mode 100644 index 68c46196d..000000000 --- a/app/images/check-green-solid.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/check-icon.svg b/app/images/check-icon.svg deleted file mode 100644 index ebd8ee11f..000000000 --- a/app/images/check-icon.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/check-white.svg b/app/images/check-white.svg deleted file mode 100644 index 9bbea3c1a..000000000 --- a/app/images/check-white.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/check_circle.svg b/app/images/check_circle.svg deleted file mode 100644 index 514183743..000000000 --- a/app/images/check_circle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/close-gray.svg b/app/images/close-gray.svg deleted file mode 100755 index cb198fa6c..000000000 --- a/app/images/close-gray.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/connect-icon.svg b/app/images/connect-icon.svg deleted file mode 100644 index 7b93be6b4..000000000 --- a/app/images/connect-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/contacts-icon.svg b/app/images/contacts-icon.svg deleted file mode 100644 index 50391a287..000000000 --- a/app/images/contacts-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/curve-high.svg b/app/images/curve-high.svg index f8d0f0c00..643f6fb46 100644 --- a/app/images/curve-high.svg +++ b/app/images/curve-high.svg @@ -1 +1 @@ - + diff --git a/app/images/curve-low.svg b/app/images/curve-low.svg index 385f89f86..7f1d41395 100644 --- a/app/images/curve-low.svg +++ b/app/images/curve-low.svg @@ -1 +1 @@ - + diff --git a/app/images/curve-medium.svg b/app/images/curve-medium.svg index e8a8f6a3f..3410b9604 100644 --- a/app/images/curve-medium.svg +++ b/app/images/curve-medium.svg @@ -1 +1 @@ - + diff --git a/app/images/deposit-eth.svg b/app/images/deposit-eth.svg deleted file mode 100644 index 997c3b7ac..000000000 --- a/app/images/deposit-eth.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/app/images/double-arrow.svg b/app/images/double-arrow.svg deleted file mode 100644 index db13703e7..000000000 --- a/app/images/double-arrow.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/images/down-arrow-grey.svg b/app/images/down-arrow-grey.svg deleted file mode 100644 index fcdb33eec..000000000 --- a/app/images/down-arrow-grey.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/down-arrow.svg b/app/images/down-arrow.svg deleted file mode 100644 index 9d367dfc9..000000000 --- a/app/images/down-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/download-alt.svg b/app/images/download-alt.svg deleted file mode 100644 index c59f815ef..000000000 --- a/app/images/download-alt.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/app/images/eth.svg b/app/images/eth.svg deleted file mode 100644 index 04e726b13..000000000 --- a/app/images/eth.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/experimental-icon.svg b/app/images/experimental-icon.svg deleted file mode 100644 index 90dc810cf..000000000 --- a/app/images/experimental-icon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/images/general-icon.svg b/app/images/general-icon.svg deleted file mode 100644 index 32029ef12..000000000 --- a/app/images/general-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/images/icons/cancelled.svg b/app/images/icons/cancelled.svg deleted file mode 100755 index 34a3e097b..000000000 --- a/app/images/icons/cancelled.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/confirm.svg b/app/images/icons/confirm.svg deleted file mode 100644 index fd81b03b7..000000000 --- a/app/images/icons/confirm.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/connected-sites.svg b/app/images/icons/connected-sites.svg deleted file mode 100644 index 4d90e233f..000000000 --- a/app/images/icons/connected-sites.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/icons/disconnect.svg b/app/images/icons/disconnect.svg deleted file mode 100644 index 146d6c727..000000000 --- a/app/images/icons/disconnect.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/icons/down-arrow.svg b/app/images/icons/down-arrow.svg deleted file mode 100644 index e55f29129..000000000 --- a/app/images/icons/down-arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/error.svg b/app/images/icons/error.svg deleted file mode 100644 index 2fcbc612f..000000000 --- a/app/images/icons/error.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/expand.svg b/app/images/icons/expand.svg deleted file mode 100644 index 6a6146840..000000000 --- a/app/images/icons/expand.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/images/icons/new.svg b/app/images/icons/new.svg deleted file mode 100755 index 6ed612bd9..000000000 --- a/app/images/icons/new.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/red-triangle-exclaim.svg b/app/images/icons/red-triangle-exclaim.svg deleted file mode 100644 index 064908c78..000000000 --- a/app/images/icons/red-triangle-exclaim.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/icons/retry.svg b/app/images/icons/retry.svg deleted file mode 100755 index b394bfb86..000000000 --- a/app/images/icons/retry.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/submitted.svg b/app/images/icons/submitted.svg deleted file mode 100755 index df2ce7044..000000000 --- a/app/images/icons/submitted.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/images/icons/swap.svg b/app/images/icons/swap.svg deleted file mode 100644 index 9d4bbbda5..000000000 --- a/app/images/icons/swap.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/images/icons/swap2.svg b/app/images/icons/swap2.svg deleted file mode 100644 index 8d280af0d..000000000 --- a/app/images/icons/swap2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/icons/yellow-bell.svg b/app/images/icons/yellow-bell.svg deleted file mode 100644 index f40e00194..000000000 --- a/app/images/icons/yellow-bell.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/import-account.svg b/app/images/import-account.svg deleted file mode 100644 index bbcec8e29..000000000 --- a/app/images/import-account.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/info-icon.svg b/app/images/info-icon.svg deleted file mode 100644 index 6fd99c9da..000000000 --- a/app/images/info-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/lattice-logo.png b/app/images/lattice-logo.png deleted file mode 100644 index 0100aba0f..000000000 Binary files a/app/images/lattice-logo.png and /dev/null differ diff --git a/app/images/ledger-logo.svg b/app/images/ledger-logo.svg deleted file mode 100644 index aa4118230..000000000 --- a/app/images/ledger-logo.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/images/level-arrow.svg b/app/images/level-arrow.svg deleted file mode 100644 index 73101e560..000000000 --- a/app/images/level-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/lock.svg b/app/images/lock.svg deleted file mode 100644 index 8ecb366ea..000000000 --- a/app/images/lock.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/mm-info-icon.svg b/app/images/mm-info-icon.svg deleted file mode 100644 index c6d4a4ad4..000000000 --- a/app/images/mm-info-icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/network-icon.svg b/app/images/network-icon.svg deleted file mode 100644 index 4e4685e06..000000000 --- a/app/images/network-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/permissions-check.svg b/app/images/permissions-check.svg deleted file mode 100644 index a45c5346b..000000000 --- a/app/images/permissions-check.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/images/plus-btn-white.svg b/app/images/plus-btn-white.svg deleted file mode 100644 index 13ce2e74b..000000000 --- a/app/images/plus-btn-white.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/popout.svg b/app/images/popout.svg deleted file mode 100644 index d4256391b..000000000 --- a/app/images/popout.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/app/images/qr-blue.svg b/app/images/qr-blue.svg deleted file mode 100644 index 7310e3e85..000000000 --- a/app/images/qr-blue.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/images/qrcode-wallet-logo.svg b/app/images/qrcode-wallet-logo.svg deleted file mode 100644 index a88a7635e..000000000 --- a/app/images/qrcode-wallet-logo.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - qr-logo的副本2 - - - - - - - - \ No newline at end of file diff --git a/app/images/search-black.svg b/app/images/search-black.svg deleted file mode 100644 index a6d230f4a..000000000 --- a/app/images/search-black.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/search.svg b/app/images/search.svg deleted file mode 100644 index de1f38e0c..000000000 --- a/app/images/search.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/security-icon.svg b/app/images/security-icon.svg deleted file mode 100644 index 4d3fa1459..000000000 --- a/app/images/security-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/app/images/settings.svg b/app/images/settings.svg deleted file mode 100644 index 5424d5f09..000000000 --- a/app/images/settings.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/single-arrow.svg b/app/images/single-arrow.svg deleted file mode 100644 index 2939fb606..000000000 --- a/app/images/single-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/support.svg b/app/images/support.svg deleted file mode 100644 index 4670dfb4f..000000000 --- a/app/images/support.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/app/images/thin-plus.svg b/app/images/thin-plus.svg deleted file mode 100644 index 3a1773102..000000000 --- a/app/images/thin-plus.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/times.svg b/app/images/times.svg deleted file mode 100644 index 5aa9be3a5..000000000 --- a/app/images/times.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/token-detection.svg b/app/images/token-detection.svg new file mode 100644 index 000000000..c7246e8f7 --- /dev/null +++ b/app/images/token-detection.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/images/tokensearch.svg b/app/images/tokensearch.svg deleted file mode 100644 index 89a839602..000000000 --- a/app/images/tokensearch.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/app/images/transak.svg b/app/images/transak.svg deleted file mode 100644 index 8f8d7790a..000000000 --- a/app/images/transak.svg +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/images/trezor-logo.svg b/app/images/trezor-logo.svg deleted file mode 100644 index 139ea7810..000000000 --- a/app/images/trezor-logo.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/twitter-icon.png b/app/images/twitter-icon.png deleted file mode 100644 index a71ad381f..000000000 Binary files a/app/images/twitter-icon.png and /dev/null differ diff --git a/app/images/up-arrow.svg b/app/images/up-arrow.svg deleted file mode 100644 index 76449fe0d..000000000 --- a/app/images/up-arrow.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/images/user-check.svg b/app/images/user-check.svg deleted file mode 100644 index 8ba739338..000000000 --- a/app/images/user-check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/images/warning-icon.png b/app/images/warning-icon.png deleted file mode 100644 index 06b4be332..000000000 Binary files a/app/images/warning-icon.png and /dev/null differ diff --git a/app/images/wyre.svg b/app/images/wyre.svg deleted file mode 100644 index ef524cb4c..000000000 --- a/app/images/wyre.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/app/phishing.html b/app/phishing.html index ae9d125da..f7c159437 100644 --- a/app/phishing.html +++ b/app/phishing.html @@ -1,40 +1,101 @@ - + MetaMask Phishing Detection - - - - + + + + - - + +
- +

MetaMask Phishing Detection @@ -42,26 +103,46 @@

- This domain is currently on the MetaMask domain warning list. This means that based on information available to us, - MetaMask believes this domain could currently compromise your security and, as an added safety feature, MetaMask - has restricted access to the site. To override this, please read the rest of this warning for instructions on how to continue at your own risk. + This domain is currently on the MetaMask domain warning list. This + means that based on information available to us, MetaMask believes + this domain could currently compromise your security and, as an added + safety feature, MetaMask has restricted access to the site. To + override this, please read the rest of this warning for instructions + on how to continue at your own risk.

- There are many reasons sites can appear on our warning list, and our warning list compiles from other widely used industry lists. - Such reasons can include known fraud or security risks, such as domains that test positive on the - Ethereum Phishing Detector. - Domains on these warning lists may include outright malicious websites and legitimate websites that have been compromised by a malicious actor. -

-

To read more about this site please search for the domain on CryptoScamDB.

-

- Note that this warning list is compiled on a voluntary basis. This list may be inaccurate or incomplete. - Just because a domain does not appear on this list is not an implicit guarantee of that domain's safety. - As always, your transactions are your own responsibility. If you wish to interact with any domain on our warning list, - you can do so by continuing at your own risk. + There are many reasons sites can appear on our warning list, and our + warning list compiles from other widely used industry lists. Such + reasons can include known fraud or security risks, such as domains + that test positive on the + Ethereum Phishing Detector. Domains on these warning lists may include outright malicious + websites and legitimate websites that have been compromised by a + malicious actor.

- If you think this domain is incorrectly flagged or if a blocked legitimate website has resolved its security issues, - please file an issue. + To read more about this site + please search for the domain on CryptoScamDB. +

+

+ Note that this warning list is compiled on a voluntary basis. This + list may be inaccurate or incomplete. Just because a domain does not + appear on this list is not an implicit guarantee of that domain's + safety. As always, your transactions are your own responsibility. If + you wish to interact with any domain on our warning list, you can do + so by continuing at your own risk. +

+

+ If you think this domain is incorrectly flagged or if a blocked + legitimate website has resolved its security issues, + please file an issue.

diff --git a/app/scripts/background.js b/app/scripts/background.js index 4e6176436..ae85d781b 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -6,7 +6,7 @@ import endOfStream from 'end-of-stream'; import pump from 'pump'; import debounce from 'debounce-stream'; import log from 'loglevel'; -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; import { storeAsStream, storeTransformStream } from '@metamask/obs-store'; import PortStream from 'extension-port-stream'; import { captureException } from '@sentry/browser'; @@ -224,7 +224,7 @@ function setupController(initState, initLangCode) { // platform specific api platform, notificationManager, - extension, + browser, getRequestAccountTabIds: () => { return requestAccountTabIds; }, @@ -294,8 +294,8 @@ function setupController(initState, initLangCode) { // // connect to other contexts // - extension.runtime.onConnect.addListener(connectRemote); - extension.runtime.onConnectExternal.addListener(connectExternal); + browser.runtime.onConnect.addListener(connectRemote); + browser.runtime.onConnectExternal.addListener(connectExternal); const metamaskInternalProcessHash = { [ENVIRONMENT_TYPE_POPUP]: true, @@ -359,8 +359,7 @@ function setupController(initState, initLangCode) { isMetaMaskInternalProcess = metamaskInternalProcessHash[processName]; } else { isMetaMaskInternalProcess = - remotePort.sender.origin === - `chrome-extension://${extension.runtime.id}`; + remotePort.sender.origin === `chrome-extension://${browser.runtime.id}`; } if (isMetaMaskInternalProcess) { @@ -481,8 +480,8 @@ function setupController(initState, initLangCode) { if (count) { label = String(count); } - extension.browserAction.setBadgeText({ text: label }); - extension.browserAction.setBadgeBackgroundColor({ color: '#037DD6' }); + browser.browserAction.setBadgeText({ text: label }); + browser.browserAction.setBadgeBackgroundColor({ color: '#037DD6' }); } function getUnapprovedTransactionCount() { @@ -627,7 +626,7 @@ async function openPopup() { } // On first install, open a new tab with MetaMask -extension.runtime.onInstalled.addListener(({ reason }) => { +browser.runtime.onInstalled.addListener(({ reason }) => { if ( reason === 'install' && !(process.env.METAMASK_DEBUG || process.env.IN_TEST) diff --git a/app/scripts/constants/on-ramp.js b/app/scripts/constants/on-ramp.js index 335d7a9ad..bfce16f70 100644 --- a/app/scripts/constants/on-ramp.js +++ b/app/scripts/constants/on-ramp.js @@ -1 +1,2 @@ export const TRANSAK_API_KEY = '25ac1309-a49b-4411-b20e-5e56c61a5b1c'; // It's a public key, which will be included in a URL for Transak. +export const MOONPAY_API_KEY = 'pk_live_WbCpe6PxSIcGPCSd6lKCbJNRht7uy'; // Publishable key. diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 236952f61..037f3de14 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -2,7 +2,7 @@ import querystring from 'querystring'; import pump from 'pump'; import { WindowPostMessageStream } from '@metamask/post-message-stream'; import ObjectMultiplex from 'obj-multiplex'; -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; import PortStream from 'extension-port-stream'; import { obj as createThoughStream } from 'through2'; @@ -14,7 +14,7 @@ const inpageContent = fs.readFileSync( path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js'), 'utf8', ); -const inpageSuffix = `//# sourceURL=${extension.runtime.getURL('inpage.js')}\n`; +const inpageSuffix = `//# sourceURL=${browser.runtime.getURL('inpage.js')}\n`; const inpageBundle = inpageContent + inpageSuffix; const CONTENT_SCRIPT = 'metamask-contentscript'; @@ -61,7 +61,7 @@ async function setupStreams() { name: CONTENT_SCRIPT, target: INPAGE, }); - const extensionPort = extension.runtime.connect({ name: CONTENT_SCRIPT }); + const extensionPort = browser.runtime.connect({ name: CONTENT_SCRIPT }); const extensionStream = new PortStream(extensionPort); // create and connect channel muxers @@ -301,7 +301,7 @@ function blockedDomainCheck() { */ function redirectToPhishingWarning() { console.debug('MetaMask: Routing to Phishing Warning component.'); - const extensionURL = extension.runtime.getURL('phishing.html'); + const extensionURL = browser.runtime.getURL('phishing.html'); window.location.href = `${extensionURL}#${querystring.stringify({ hostname: window.location.hostname, href: window.location.href, diff --git a/app/scripts/controllers/metametrics.js b/app/scripts/controllers/metametrics.js index fb0164153..5207eda11 100644 --- a/app/scripts/controllers/metametrics.js +++ b/app/scripts/controllers/metametrics.js @@ -1,4 +1,4 @@ -import { merge, omit, omitBy } from 'lodash'; +import { isEqual, merge, omit, omitBy, pickBy } from 'lodash'; import { ObservableStore } from '@metamask/obs-store'; import { bufferToHex, keccak } from 'ethereumjs-util'; import { generateUUID } from 'pubnub'; @@ -6,6 +6,7 @@ import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app'; import { METAMETRICS_ANONYMOUS_ID, METAMETRICS_BACKGROUND_PAGE_OBJECT, + TRAITS, } from '../../../shared/constants/metametrics'; import { SECOND } from '../../../shared/constants/time'; @@ -303,150 +304,6 @@ export default class MetaMetricsController { return this.store.getState(); } - /** - * Build the context object to attach to page and track events. - * - * @private - * @param {Pick} [referrer] - dapp origin that initialized - * the notification window. - * @param {Pick} [page] - page object describing the current - * view of the extension. Defaults to the background-process object. - * @returns {MetaMetricsContext} - */ - _buildContext(referrer, page = METAMETRICS_BACKGROUND_PAGE_OBJECT) { - return { - app: { - name: 'MetaMask Extension', - version: this.version, - }, - userAgent: window.navigator.userAgent, - page, - referrer, - }; - } - - /** - * Build's the event payload, processing all fields into a format that can be - * fed to Segment's track method - * - * @private - * @param { - * Omit - * } rawPayload - raw payload provided to trackEvent - * @returns {SegmentEventPayload} formatted event payload for segment - */ - _buildEventPayload(rawPayload) { - const { - event, - properties, - revenue, - value, - currency, - category, - page, - referrer, - environmentType = ENVIRONMENT_TYPE_BACKGROUND, - } = rawPayload; - return { - event, - properties: { - // These values are omitted from properties because they have special meaning - // in segment. https://segment.com/docs/connections/spec/track/#properties. - // to avoid accidentally using these inappropriately, you must add them as top - // level properties on the event payload. We also exclude locale to prevent consumers - // from overwriting this context level property. We track it as a property - // because not all destinations map locale from context. - ...omit(properties, ['revenue', 'locale', 'currency', 'value']), - revenue, - value, - currency, - category, - network: properties?.network ?? this.network, - locale: this.locale, - chain_id: properties?.chain_id ?? this.chainId, - environment_type: environmentType, - }, - context: this._buildContext(referrer, page), - }; - } - - /** - * Perform validation on the payload and update the id type to use before - * sending to Segment. Also examines the options to route and handle the - * event appropriately. - * - * @private - * @param {SegmentEventPayload} payload - properties to attach to event - * @param {MetaMetricsEventOptions} [options] - options for routing and - * handling the event - * @returns {Promise} - */ - _track(payload, options) { - const { - isOptIn, - metaMetricsId: metaMetricsIdOverride, - matomoEvent, - flushImmediately, - } = options || {}; - let idType = 'userId'; - let idValue = this.state.metaMetricsId; - let excludeMetaMetricsId = options?.excludeMetaMetricsId ?? false; - // This is carried over from the old implementation, and will likely need - // to be updated to work with the new tracking plan. I think we should use - // a config setting for this instead of trying to match the event name - const isSendFlow = Boolean(payload.event.match(/^send|^confirm/iu)); - if (isSendFlow) { - excludeMetaMetricsId = true; - } - // If we are tracking sensitive data we will always use the anonymousId - // property as well as our METAMETRICS_ANONYMOUS_ID. This prevents us from - // associating potentially identifiable information with a specific id. - // During the opt in flow we will track all events, but do so with the - // anonymous id. The one exception to that rule is after the user opts in - // to MetaMetrics. When that happens we receive back the user's new - // MetaMetrics id before it is fully persisted to state. To avoid a race - // condition we explicitly pass the new id to the track method. In that - // case we will track the opt in event to the user's id. In all other cases - // we use the metaMetricsId from state. - if (excludeMetaMetricsId || (isOptIn && !metaMetricsIdOverride)) { - idType = 'anonymousId'; - idValue = METAMETRICS_ANONYMOUS_ID; - } else if (isOptIn && metaMetricsIdOverride) { - idValue = metaMetricsIdOverride; - } - payload[idType] = idValue; - - // If this is an event on the old matomo schema, add a key to the payload - // to designate it as such - if (matomoEvent === true) { - payload.properties.legacy_event = true; - } - - // Promises will only resolve when the event is sent to segment. For any - // event that relies on this promise being fulfilled before performing UI - // updates, or otherwise delaying user interaction, supply the - // 'flushImmediately' flag to the trackEvent method. - return new Promise((resolve, reject) => { - const callback = (err) => { - if (err) { - // The error that segment gives us has some manipulation done to it - // that seemingly breaks with lockdown enabled. Creating a new error - // here prevents the system from freezing when the network request to - // segment fails for any reason. - const safeError = new Error(err.message); - safeError.stack = err.stack; - return reject(safeError); - } - return resolve(); - }; - - this.segment.track(payload, callback); - if (flushImmediately) { - this.segment.flush(); - } - }); - } - /** * track a page view with Segment * @@ -573,4 +430,185 @@ export default class MetaMetricsController { ); } } + + handleMetaMaskStateUpdate(newState) { + const userTraits = this._buildUserTraitsObject(newState); + if (userTraits) { + // this.identify(userTraits); + } + } + + /** PRIVATE METHODS */ + + /** + * Build the context object to attach to page and track events. + * + * @private + * @param {Pick} [referrer] - dapp origin that initialized + * the notification window. + * @param {Pick} [page] - page object describing the current + * view of the extension. Defaults to the background-process object. + * @returns {MetaMetricsContext} + */ + _buildContext(referrer, page = METAMETRICS_BACKGROUND_PAGE_OBJECT) { + return { + app: { + name: 'MetaMask Extension', + version: this.version, + }, + userAgent: window.navigator.userAgent, + page, + referrer, + }; + } + + /** + * Build's the event payload, processing all fields into a format that can be + * fed to Segment's track method + * + * @private + * @param { + * Omit + * } rawPayload - raw payload provided to trackEvent + * @returns {SegmentEventPayload} formatted event payload for segment + */ + _buildEventPayload(rawPayload) { + const { + event, + properties, + revenue, + value, + currency, + category, + page, + referrer, + environmentType = ENVIRONMENT_TYPE_BACKGROUND, + } = rawPayload; + return { + event, + properties: { + // These values are omitted from properties because they have special meaning + // in segment. https://segment.com/docs/connections/spec/track/#properties. + // to avoid accidentally using these inappropriately, you must add them as top + // level properties on the event payload. We also exclude locale to prevent consumers + // from overwriting this context level property. We track it as a property + // because not all destinations map locale from context. + ...omit(properties, ['revenue', 'locale', 'currency', 'value']), + revenue, + value, + currency, + category, + network: properties?.network ?? this.network, + locale: this.locale, + chain_id: properties?.chain_id ?? this.chainId, + environment_type: environmentType, + }, + context: this._buildContext(referrer, page), + }; + } + + _buildUserTraitsObject(metamaskState) { + const currentTraits = { + [TRAITS.LEDGER_CONNECTION_TYPE]: metamaskState.ledgerTransportType, + [TRAITS.NUMBER_OF_ACCOUNTS]: Object.values(metamaskState.identities) + .length, + [TRAITS.NETWORKS_ADDED]: metamaskState.frequentRpcListDetail.map( + (rpc) => rpc.chainId, + ), + [TRAITS.THREE_BOX_ENABLED]: metamaskState.threeBoxSyncingAllowed, + }; + + if (!this.previousTraits) { + this.previousTraits = currentTraits; + return currentTraits; + } + + if (this.previousTraits && !isEqual(this.previousTraits, currentTraits)) { + const updates = pickBy( + currentTraits, + (v, k) => !isEqual(this.previousTraits[k], v), + ); + this.previousTraits = currentTraits; + return updates; + } + + return null; + } + + /** + * Perform validation on the payload and update the id type to use before + * sending to Segment. Also examines the options to route and handle the + * event appropriately. + * + * @private + * @param {SegmentEventPayload} payload - properties to attach to event + * @param {MetaMetricsEventOptions} [options] - options for routing and + * handling the event + * @returns {Promise} + */ + _track(payload, options) { + const { + isOptIn, + metaMetricsId: metaMetricsIdOverride, + matomoEvent, + flushImmediately, + } = options || {}; + let idType = 'userId'; + let idValue = this.state.metaMetricsId; + let excludeMetaMetricsId = options?.excludeMetaMetricsId ?? false; + // This is carried over from the old implementation, and will likely need + // to be updated to work with the new tracking plan. I think we should use + // a config setting for this instead of trying to match the event name + const isSendFlow = Boolean(payload.event.match(/^send|^confirm/iu)); + if (isSendFlow) { + excludeMetaMetricsId = true; + } + // If we are tracking sensitive data we will always use the anonymousId + // property as well as our METAMETRICS_ANONYMOUS_ID. This prevents us from + // associating potentially identifiable information with a specific id. + // During the opt in flow we will track all events, but do so with the + // anonymous id. The one exception to that rule is after the user opts in + // to MetaMetrics. When that happens we receive back the user's new + // MetaMetrics id before it is fully persisted to state. To avoid a race + // condition we explicitly pass the new id to the track method. In that + // case we will track the opt in event to the user's id. In all other cases + // we use the metaMetricsId from state. + if (excludeMetaMetricsId || (isOptIn && !metaMetricsIdOverride)) { + idType = 'anonymousId'; + idValue = METAMETRICS_ANONYMOUS_ID; + } else if (isOptIn && metaMetricsIdOverride) { + idValue = metaMetricsIdOverride; + } + payload[idType] = idValue; + + // If this is an event on the old matomo schema, add a key to the payload + // to designate it as such + if (matomoEvent === true) { + payload.properties.legacy_event = true; + } + + // Promises will only resolve when the event is sent to segment. For any + // event that relies on this promise being fulfilled before performing UI + // updates, or otherwise delaying user interaction, supply the + // 'flushImmediately' flag to the trackEvent method. + return new Promise((resolve, reject) => { + const callback = (err) => { + if (err) { + // The error that segment gives us has some manipulation done to it + // that seemingly breaks with lockdown enabled. Creating a new error + // here prevents the system from freezing when the network request to + // segment fails for any reason. + const safeError = new Error(err.message); + safeError.stack = err.stack; + return reject(safeError); + } + return resolve(); + }; + + this.segment.track(payload, callback); + if (flushImmediately) { + this.segment.flush(); + } + }); + } } diff --git a/app/scripts/controllers/metametrics.test.js b/app/scripts/controllers/metametrics.test.js index a5c2dc1f5..3c3461aa3 100644 --- a/app/scripts/controllers/metametrics.test.js +++ b/app/scripts/controllers/metametrics.test.js @@ -5,8 +5,13 @@ import { createSegmentMock } from '../lib/segment'; import { METAMETRICS_ANONYMOUS_ID, METAMETRICS_BACKGROUND_PAGE_OBJECT, + TRAITS, } from '../../../shared/constants/metametrics'; import waitUntilCalled from '../../../test/lib/wait-until-called'; +import { + MAINNET_CHAIN_ID, + ROPSTEN_CHAIN_ID, +} from '../../../shared/constants/network'; import MetaMetricsController from './metametrics'; import { NETWORK_EVENTS } from './network'; @@ -518,6 +523,80 @@ describe('MetaMetricsController', function () { }); }); + describe('_buildUserTraitsObject', function () { + it('should return full user traits object on first call', function () { + const metaMetricsController = getMetaMetricsController(); + const traits = metaMetricsController._buildUserTraitsObject({ + frequentRpcListDetail: [ + { chainId: MAINNET_CHAIN_ID }, + { chainId: ROPSTEN_CHAIN_ID }, + ], + ledgerTransportType: 'web-hid', + identities: [{}, {}], + threeBoxSyncingAllowed: false, + }); + + assert.deepEqual(traits, { + [TRAITS.THREE_BOX_ENABLED]: false, + [TRAITS.LEDGER_CONNECTION_TYPE]: 'web-hid', + [TRAITS.NUMBER_OF_ACCOUNTS]: 2, + [TRAITS.NETWORKS_ADDED]: [MAINNET_CHAIN_ID, ROPSTEN_CHAIN_ID], + }); + }); + + it('should return only changed traits object on subsequent calls', function () { + const metaMetricsController = getMetaMetricsController(); + metaMetricsController._buildUserTraitsObject({ + frequentRpcListDetail: [ + { chainId: MAINNET_CHAIN_ID }, + { chainId: ROPSTEN_CHAIN_ID }, + ], + ledgerTransportType: 'web-hid', + identities: [{}, {}], + threeBoxSyncingAllowed: false, + }); + + const updatedTraits = metaMetricsController._buildUserTraitsObject({ + frequentRpcListDetail: [ + { chainId: MAINNET_CHAIN_ID }, + { chainId: ROPSTEN_CHAIN_ID }, + ], + ledgerTransportType: 'web-hid', + identities: [{}, {}, {}], + threeBoxSyncingAllowed: false, + }); + + assert.deepEqual(updatedTraits, { + [TRAITS.NUMBER_OF_ACCOUNTS]: 3, + }); + }); + + it('should return null if no traits changed', function () { + const metaMetricsController = getMetaMetricsController(); + metaMetricsController._buildUserTraitsObject({ + frequentRpcListDetail: [ + { chainId: MAINNET_CHAIN_ID }, + { chainId: ROPSTEN_CHAIN_ID }, + ], + ledgerTransportType: 'web-hid', + identities: [{}, {}], + threeBoxSyncingAllowed: false, + }); + + const updatedTraits = metaMetricsController._buildUserTraitsObject({ + frequentRpcListDetail: [ + { chainId: MAINNET_CHAIN_ID }, + { chainId: ROPSTEN_CHAIN_ID }, + ], + ledgerTransportType: 'web-hid', + identities: [{}, {}], + threeBoxSyncingAllowed: false, + }); + + assert.equal(updatedTraits, null); + }); + }); + afterEach(function () { // flush the queues manually after each test segment.flush(); diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 92c308f29..27cf59277 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -46,6 +46,7 @@ import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP, } from '../../../../shared/constants/network'; import { + determineTransactionAssetType, determineTransactionType, isEIP1559Transaction, } from '../../../../shared/modules/transaction.utils'; @@ -130,6 +131,7 @@ export default class TransactionController extends EventEmitter { this.getEventFragmentById = opts.getEventFragmentById; this.getDeviceModel = opts.getDeviceModel; this.getAccountType = opts.getAccountType; + this.getTokenStandardAndDetails = opts.getTokenStandardAndDetails; this.memStore = new ObservableStore({}); this.query = new EthQuery(this.provider); @@ -359,13 +361,30 @@ export default class TransactionController extends EventEmitter { return transactions[txId]; } - _checkIfTxStatusIsUnapproved(txId) { + /** + * @param {number} txId + * @returns {boolean} + */ + _isUnapprovedTransaction(txId) { return ( this.txStateManager.getTransaction(txId).status === TRANSACTION_STATUSES.UNAPPROVED ); } + /** + * @param {number} txId + * @param {string} fnName + */ + _throwErrorIfNotUnapprovedTx(txId, fnName) { + if (!this._isUnapprovedTransaction(txId)) { + throw new Error( + `TransactionsController: Can only call ${fnName} on an unapproved transaction. + Current tx status: ${this.txStateManager.getTransaction(txId).status}`, + ); + } + } + _updateTransaction(txId, proposedUpdate, note) { const txMeta = this.txStateManager.getTransaction(txId); const updated = merge(txMeta, proposedUpdate); @@ -376,19 +395,45 @@ export default class TransactionController extends EventEmitter { * updates the params that are editible in the send edit flow * * @param {string} txId - transaction id - * @param {object} editableParams - holds the editable parameters + * @param {object} previousGasParams - holds the parameter to update + * @param {string} previousGasParams.maxFeePerGas + * @param {string} previousGasParams.maxPriorityFeePerGas + * @param {string} previousGasParams.gasLimit + * @returns {TransactionMeta} the txMeta of the updated transaction + */ + updatePreviousGasParams( + txId, + { maxFeePerGas, maxPriorityFeePerGas, gasLimit }, + ) { + const previousGasParams = { + previousGas: { + maxFeePerGas, + maxPriorityFeePerGas, + gasLimit, + }, + }; + + // only update what is defined + previousGasParams.previousGas = pickBy(previousGasParams.previousGas); + const note = `Update Previous Gas for ${txId}`; + this._updateTransaction(txId, previousGasParams, note); + return this._getTransaction(txId); + } + + /** + * + * @param {string} txId - transaction id + * @param {object} editableParams - holds the eip1559 fees parameters * @param {object} editableParams.data * @param {string} editableParams.from * @param {string} editableParams.to * @param {string} editableParams.value + * @param {string} editableParams.gas + * @param {string} editableParams.gasPrice * @returns {TransactionMeta} the txMeta of the updated transaction */ - updateEditableParams(txId, { data, from, to, value }) { - if (!this._checkIfTxStatusIsUnapproved(txId)) { - throw new Error( - 'Cannot call updateEditableParams on a transaction that is not in an unapproved state', - ); - } + updateEditableParams(txId, { data, from, to, value, gas, gasPrice }) { + this._throwErrorIfNotUnapprovedTx(txId, 'updateEditableParams'); const editableParams = { txParams: { @@ -396,6 +441,8 @@ export default class TransactionController extends EventEmitter { from, to, value, + gas, + gasPrice, }, }; @@ -420,6 +467,8 @@ export default class TransactionController extends EventEmitter { * @param {string} txGasFees.defaultGasEstimates * @param {string} txGasFees.gas * @param {string} txGasFees.originalGasEstimate + * @param {string} txGasFees.userEditedGasLimit + * @param {string} txGasFees.userFeeLevel * @returns {TransactionMeta} the txMeta of the updated transaction */ updateTransactionGasFees( @@ -434,13 +483,11 @@ export default class TransactionController extends EventEmitter { estimateSuggested, defaultGasEstimates, originalGasEstimate, + userEditedGasLimit, + userFeeLevel, }, ) { - if (!this._checkIfTxStatusIsUnapproved(txId)) { - throw new Error( - 'Cannot call updateTransactionGasFees on a transaction that is not in an unapproved state', - ); - } + this._throwErrorIfNotUnapprovedTx(txId, 'updateTransactionGasFees'); let txGasFees = { txParams: { @@ -454,6 +501,8 @@ export default class TransactionController extends EventEmitter { estimateSuggested, defaultGasEstimates, originalGasEstimate, + userEditedGasLimit, + userFeeLevel, }; // only update what is defined @@ -477,11 +526,10 @@ export default class TransactionController extends EventEmitter { txId, { estimatedBaseFee, decEstimatedBaseFee }, ) { - if (!this._checkIfTxStatusIsUnapproved(txId)) { - throw new Error( - 'Cannot call updateTransactionEstimatedBaseFee on a transaction that is not in an unapproved state', - ); - } + this._throwErrorIfNotUnapprovedTx( + txId, + 'updateTransactionEstimatedBaseFee', + ); let txEstimateBaseFees = { estimatedBaseFee, decEstimatedBaseFee }; // only update what is defined @@ -503,11 +551,7 @@ export default class TransactionController extends EventEmitter { * @returns {TransactionMeta} the txMeta of the updated transaction */ updateSwapApprovalTransaction(txId, { type, sourceTokenSymbol }) { - if (!this._checkIfTxStatusIsUnapproved(txId)) { - throw new Error( - 'Cannot call updateSwapApprovalTransaction on a transaction that is not in an unapproved state', - ); - } + this._throwErrorIfNotUnapprovedTx(txId, 'updateSwapApprovalTransaction'); let swapApprovalTransaction = { type, sourceTokenSymbol }; // only update what is defined @@ -549,11 +593,8 @@ export default class TransactionController extends EventEmitter { approvalTxId, }, ) { - if (!this._checkIfTxStatusIsUnapproved(txId)) { - throw new Error( - 'Cannot call updateSwapTransaction on a transaction that is not in an unapproved state', - ); - } + this._throwErrorIfNotUnapprovedTx(txId, 'updateSwapTransaction'); + let swapTransaction = { sourceTokenSymbol, destinationTokenSymbol, @@ -584,11 +625,7 @@ export default class TransactionController extends EventEmitter { * @returns {TransactionMeta} the txMeta of the updated transaction */ updateTransactionUserSettings(txId, { userEditedGasLimit, userFeeLevel }) { - if (!this._checkIfTxStatusIsUnapproved(txId)) { - throw new Error( - 'Cannot call updateTransactionUserSettings on a transaction that is not in an unapproved state', - ); - } + this._throwErrorIfNotUnapprovedTx(txId, 'updateTransactionUserSettings'); let userSettings = { userEditedGasLimit, userFeeLevel }; // only update what is defined @@ -1858,6 +1895,12 @@ export default class TransactionController extends EventEmitter { } = txMeta; const source = referrer === 'metamask' ? 'user' : 'dapp'; + const { assetType, tokenStandard } = await determineTransactionAssetType( + txMeta, + this.query, + this.getTokenStandardAndDetails, + ); + const gasParams = {}; if (isEIP1559Transaction(txMeta)) { @@ -1931,6 +1974,8 @@ export default class TransactionController extends EventEmitter { gas_edit_attempted: 'none', account_type: await this.getAccountType(this.getSelectedAddress()), device_model: await this.getDeviceModel(this.getSelectedAddress()), + asset_type: assetType, + token_standard: tokenStandard, }; const sensitiveProperties = { diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index c0c189868..ce0a3339f 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -15,6 +15,7 @@ import { TRANSACTION_TYPES, TRANSACTION_ENVELOPE_TYPES, TRANSACTION_EVENTS, + ASSET_TYPES, } from '../../../../shared/constants/transaction'; import { SECOND } from '../../../../shared/constants/time'; @@ -24,6 +25,7 @@ import { } from '../../../../shared/constants/gas'; import { TRANSACTION_ENVELOPE_TYPE_NAMES } from '../../../../ui/helpers/constants/transactions'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; +import { TOKEN_STANDARDS } from '../../../../ui/helpers/constants/common'; import TransactionController from '.'; const noop = () => true; @@ -1469,6 +1471,8 @@ describe('Transaction Controller', function () { source: 'user', type: TRANSACTION_TYPES.SIMPLE_SEND, account_type: 'MetaMask', + asset_type: ASSET_TYPES.NATIVE, + token_standard: TOKEN_STANDARDS.NONE, device_model: 'N/A', }, sensitiveProperties: { @@ -1546,6 +1550,8 @@ describe('Transaction Controller', function () { source: 'user', type: TRANSACTION_TYPES.SIMPLE_SEND, account_type: 'MetaMask', + asset_type: ASSET_TYPES.NATIVE, + token_standard: TOKEN_STANDARDS.NONE, device_model: 'N/A', }, sensitiveProperties: { @@ -1633,6 +1639,8 @@ describe('Transaction Controller', function () { source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, account_type: 'MetaMask', + asset_type: ASSET_TYPES.NATIVE, + token_standard: TOKEN_STANDARDS.NONE, device_model: 'N/A', }, sensitiveProperties: { @@ -1712,6 +1720,8 @@ describe('Transaction Controller', function () { source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, account_type: 'MetaMask', + asset_type: ASSET_TYPES.NATIVE, + token_standard: TOKEN_STANDARDS.NONE, device_model: 'N/A', }, sensitiveProperties: { @@ -1791,6 +1801,8 @@ describe('Transaction Controller', function () { source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, account_type: 'MetaMask', + asset_type: ASSET_TYPES.NATIVE, + token_standard: TOKEN_STANDARDS.NONE, device_model: 'N/A', }, sensitiveProperties: { @@ -1852,6 +1864,8 @@ describe('Transaction Controller', function () { gas_edit_attempted: 'none', gas_edit_type: 'none', account_type: 'MetaMask', + asset_type: ASSET_TYPES.NATIVE, + token_standard: TOKEN_STANDARDS.NONE, device_model: 'N/A', }, sensitiveProperties: { @@ -1923,6 +1937,8 @@ describe('Transaction Controller', function () { source: 'dapp', type: TRANSACTION_TYPES.SIMPLE_SEND, account_type: 'MetaMask', + asset_type: ASSET_TYPES.NATIVE, + token_standard: TOKEN_STANDARDS.NONE, device_model: 'N/A', }, sensitiveProperties: { @@ -2170,10 +2186,10 @@ describe('Transaction Controller', function () { assert.equal(result.userFeeLevel, 'high'); }); - it('throws error if status is not unapproved', function () { + it('should not update and should throw error if status is not type "unapproved"', function () { txStateManager.addTransaction({ id: '4', - status: TRANSACTION_STATUSES.APPROVED, + status: TRANSACTION_STATUSES.DROPPED, metamaskNetworkId: currentNetworkId, txParams: { maxPriorityFeePerGas: '0x007', @@ -2184,14 +2200,18 @@ describe('Transaction Controller', function () { estimateUsed: '0x009', }); - try { - txController.updateTransactionGasFees('4', { maxFeePerGas: '0x0088' }); - } catch (e) { - assert.equal( - e.message, - 'Cannot call updateTransactionGasFees on a transaction that is not in an unapproved state', - ); - } + assert.throws( + () => + txController.updateTransactionGasFees('4', { + maxFeePerGas: '0x0088', + }), + Error, + `TransactionsController: Can only call updateTransactionGasFees on an unapproved transaction. + Current tx status: ${TRANSACTION_STATUSES.DROPPED}`, + ); + + const transaction = txStateManager.getTransaction('4'); + assert.equal(transaction.txParams.maxFeePerGas, '0x008'); }); it('does not update unknown parameters in update method', function () { diff --git a/app/scripts/lib/buy-url.js b/app/scripts/lib/buy-url.js index a7f23ccc3..ba4a0d86f 100644 --- a/app/scripts/lib/buy-url.js +++ b/app/scripts/lib/buy-url.js @@ -12,7 +12,7 @@ import { } from '../../../shared/constants/network'; import { SECOND } from '../../../shared/constants/time'; import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'; -import { TRANSAK_API_KEY } from '../constants/on-ramp'; +import { TRANSAK_API_KEY, MOONPAY_API_KEY } from '../constants/on-ramp'; const fetchWithTimeout = getFetchWithTimeout(SECOND * 30); @@ -67,6 +67,47 @@ const createTransakUrl = (walletAddress, chainId) => { return `https://global.transak.com/?${queryParams}`; }; +/** + * Create a MoonPay Checkout URL. + * + * @param {string} walletAddress - Destination address + * @param {string} chainId - Current chain ID + * @returns String + */ +const createMoonPayUrl = async (walletAddress, chainId) => { + const { + moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {}, + } = BUYABLE_CHAINS_MAP[chainId]; + const moonPayQueryParams = new URLSearchParams({ + apiKey: MOONPAY_API_KEY, + walletAddress, + defaultCurrencyCode, + showOnlyCurrencies, + }); + const queryParams = new URLSearchParams({ + url: `https://buy.moonpay.com?${moonPayQueryParams}`, + context: 'extension', + }); + const moonPaySignUrl = `${SWAPS_API_V2_BASE_URL}/moonpaySign/?${queryParams}`; + try { + const response = await fetchWithTimeout(moonPaySignUrl, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }); + const parsedResponse = await response.json(); + if (response.ok && parsedResponse.url) { + return parsedResponse.url; + } + log.warn('Failed to create a MoonPay purchase URL', parsedResponse); + } catch (err) { + log.warn('Failed to create a MoonPay purchase URL', err); + } + return ''; +}; + /** * Gives the caller a url at which the user can acquire eth, depending on the network they are in * @@ -89,6 +130,8 @@ export default async function getBuyUrl({ chainId, address, service }) { return await createWyrePurchaseUrl(address); case 'transak': return createTransakUrl(address, chainId); + case 'moonpay': + return createMoonPayUrl(address, chainId); case 'metamask-faucet': return 'https://faucet.metamask.io/'; case 'rinkeby-faucet': diff --git a/app/scripts/lib/buy-url.test.js b/app/scripts/lib/buy-url.test.js index 71c270fae..b582bae19 100644 --- a/app/scripts/lib/buy-url.test.js +++ b/app/scripts/lib/buy-url.test.js @@ -9,7 +9,7 @@ import { ETH_SYMBOL, BUYABLE_CHAINS_MAP, } from '../../../shared/constants/network'; -import { TRANSAK_API_KEY } from '../constants/on-ramp'; +import { TRANSAK_API_KEY, MOONPAY_API_KEY } from '../constants/on-ramp'; import { SWAPS_API_V2_BASE_URL } from '../../../shared/constants/swaps'; import getBuyUrl from './buy-url'; @@ -114,4 +114,35 @@ describe('buy-url', () => { const kovanUrl = await getBuyUrl(KOVAN); expect(kovanUrl).toStrictEqual('https://github.com/kovan-testnet/faucet'); }); + + it('returns a MoonPay url with a prefilled wallet address for the Ethereum network', async () => { + const { + moonPay: { defaultCurrencyCode, showOnlyCurrencies } = {}, + } = BUYABLE_CHAINS_MAP[MAINNET.chainId]; + const moonPayQueryParams = new URLSearchParams({ + apiKey: MOONPAY_API_KEY, + walletAddress: MAINNET.address, + defaultCurrencyCode, + showOnlyCurrencies, + }); + const queryParams = new URLSearchParams({ + url: `https://buy.moonpay.com?${moonPayQueryParams}`, + context: 'extension', + }); + nock(SWAPS_API_V2_BASE_URL) + .get(`/moonpaySign/?${queryParams}`) + .reply(200, { + url: `https://buy.moonpay.com/?apiKey=${MOONPAY_API_KEY}&walletAddress=${MAINNET.address}&defaultCurrencyCode=${defaultCurrencyCode}&showOnlyCurrencies=eth%2Cusdt%2Cusdc%2Cdai&signature=laefTlgkESEc2hv8AZEH9F25VjLEJUADY27D6MccE54%3D`, + }); + const moonPayUrl = await getBuyUrl({ ...MAINNET, service: 'moonpay' }); + expect(moonPayUrl).toStrictEqual( + `https://buy.moonpay.com/?apiKey=${MOONPAY_API_KEY}&walletAddress=${MAINNET.address}&defaultCurrencyCode=${defaultCurrencyCode}&showOnlyCurrencies=eth%2Cusdt%2Cusdc%2Cdai&signature=laefTlgkESEc2hv8AZEH9F25VjLEJUADY27D6MccE54%3D`, + ); + nock.cleanAll(); + }); + + it('returns an empty string if generating a MoonPay url fails', async () => { + const moonPayUrl = await getBuyUrl({ ...MAINNET, service: 'moonpay' }); + expect(moonPayUrl).toStrictEqual(''); + }); }); diff --git a/app/scripts/lib/createOnboardingMiddleware.js b/app/scripts/lib/createOnboardingMiddleware.js index 5fa244c07..fd41655f3 100644 --- a/app/scripts/lib/createOnboardingMiddleware.js +++ b/app/scripts/lib/createOnboardingMiddleware.js @@ -1,5 +1,5 @@ import log from 'loglevel'; -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; /** * Returns a middleware that intercepts `wallet_registerOnboarding` messages @@ -17,7 +17,7 @@ export default function createOnboardingMiddleware({ next(); return; } - if (req.tabId && req.tabId !== extension.tabs.TAB_ID_NONE) { + if (req.tabId && req.tabId !== browser.tabs.TAB_ID_NONE) { await registerOnboarding(location, req.tabId); } else { log.debug( diff --git a/app/scripts/lib/ens-ipfs/setup.js b/app/scripts/lib/ens-ipfs/setup.js index a4c199b42..2d19811c3 100644 --- a/app/scripts/lib/ens-ipfs/setup.js +++ b/app/scripts/lib/ens-ipfs/setup.js @@ -1,6 +1,7 @@ import base32Encode from 'base32-encode'; import base64 from 'base64-js'; -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; + import { SECOND } from '../../../../shared/constants/time'; import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'; import resolveEnsToIpfsContentId from './resolver'; @@ -16,7 +17,7 @@ export default function setupEnsIpfsResolver({ }) { // install listener const urlPatterns = supportedTopLevelDomains.map((tld) => `*://*.${tld}/*`); - extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, { + browser.webRequest.onErrorOccurred.addListener(webRequestDidFail, { urls: urlPatterns, types: ['main_frame'], }); @@ -25,7 +26,7 @@ export default function setupEnsIpfsResolver({ return { // uninstall listener remove() { - extension.webRequest.onErrorOccurred.removeListener(webRequestDidFail); + browser.webRequest.onErrorOccurred.removeListener(webRequestDidFail); }, }; @@ -51,7 +52,7 @@ export default function setupEnsIpfsResolver({ async function attemptResolve({ tabId, name, pathname, search, fragment }) { const ipfsGateway = getIpfsGateway(); - extension.tabs.update(tabId, { url: `loading.html` }); + browser.tabs.update(tabId, { url: `loading.html` }); let url = `https://app.ens.domains/name/${name}`; try { const { type, hash } = await resolveEnsToIpfsContentId({ @@ -101,7 +102,7 @@ export default function setupEnsIpfsResolver({ } catch (err) { console.warn(err); } finally { - extension.tabs.update(tabId, { url }); + browser.tabs.update(tabId, { url }); } } } diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js index 76faaafcc..a47cdc2ac 100644 --- a/app/scripts/lib/get-first-preferred-lang-code.js +++ b/app/scripts/lib/get-first-preferred-lang-code.js @@ -1,11 +1,6 @@ -import extension from 'extensionizer'; -import promisify from 'pify'; +import browser from 'webextension-polyfill'; import allLocales from '../../_locales/index.json'; -const getPreferredLocales = extension.i18n - ? promisify(extension.i18n.getAcceptLanguages, { errorFirst: false }) - : async () => []; - // mapping some browsers return hyphen instead underscore in locale codes (e.g. zh_TW -> zh-tw) const existingLocaleCodes = {}; allLocales.forEach((locale) => { @@ -25,7 +20,7 @@ export default async function getFirstPreferredLangCode() { let userPreferredLocaleCodes; try { - userPreferredLocaleCodes = await getPreferredLocales(); + userPreferredLocaleCodes = await browser.i18n.getAcceptLanguages(); } catch (e) { // Brave currently throws when calling getAcceptLanguages, so this handles that. userPreferredLocaleCodes = []; diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js index 8ef36479a..889e36d55 100644 --- a/app/scripts/lib/local-store.js +++ b/app/scripts/lib/local-store.js @@ -1,4 +1,4 @@ -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; import log from 'loglevel'; import { checkForError } from './util'; @@ -7,7 +7,7 @@ import { checkForError } from './util'; */ export default class ExtensionStore { constructor() { - this.isSupported = Boolean(extension.storage.local); + this.isSupported = Boolean(browser.storage.local); if (!this.isSupported) { log.error('Storage local API not available.'); } @@ -48,9 +48,9 @@ export default class ExtensionStore { * @returns {Object} the key-value map from local storage */ _get() { - const { local } = extension.storage; + const { local } = browser.storage; return new Promise((resolve, reject) => { - local.get(null, (/** @type {any} */ result) => { + local.get(null).then((/** @type {any} */ result) => { const err = checkForError(); if (err) { reject(err); @@ -69,9 +69,9 @@ export default class ExtensionStore { * @private */ _set(obj) { - const { local } = extension.storage; + const { local } = browser.storage; return new Promise((resolve, reject) => { - local.set(obj, () => { + local.set(obj).then(() => { const err = checkForError(); if (err) { reject(err); diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js index a0a22edb8..01ff3cc87 100644 --- a/app/scripts/lib/typed-message-manager.js +++ b/app/scripts/lib/typed-message-manager.js @@ -192,11 +192,13 @@ export default class TypedMessageManager extends EventEmitter { data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`, ); - assert.equal( - validation.errors.length, - 0, - 'Signing data must conform to EIP-712 schema. See https://git.io/fNtcx.', - ); + if (validation.errors.length !== 0) { + throw ethErrors.rpc.invalidParams({ + message: + 'Signing data must conform to EIP-712 schema. See https://git.io/fNtcx.', + data: validation.errors.map((v) => v.message.toString()), + }); + } let { chainId } = data.domain; if (chainId) { const activeChainId = parseInt(this._getCurrentChainId(), 16); diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js index 922447c3e..9800666af 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.js @@ -1,4 +1,5 @@ -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; + import { stripHexPrefix } from 'ethereumjs-util'; import BN from 'bn.js'; import { memoize } from 'lodash'; @@ -102,7 +103,7 @@ function BnMultiplyByFraction(targetBN, numerator, denominator) { * @returns {Error|undefined} */ function checkForError() { - const { lastError } = extension.runtime; + const { lastError } = browser.runtime; if (!lastError) { return undefined; } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 58669935c..f4f482891 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -40,6 +40,9 @@ import { CollectibleDetectionController, PermissionController, SubjectMetadataController, + ///: BEGIN:ONLY_INCLUDE_IN(flask) + RateLimitController, + ///: END:ONLY_INCLUDE_IN } from '@metamask/controllers'; import SmartTransactionsController from '@metamask/smart-transactions-controller'; ///: BEGIN:ONLY_INCLUDE_IN(flask) @@ -82,8 +85,8 @@ import { import { hexToDecimal } from '../../ui/helpers/utils/conversions.util'; import { getTokenValueParam } from '../../ui/helpers/utils/token-util'; -import { getTransactionData } from '../../ui/helpers/utils/transactions.util'; import { isEqualCaseInsensitive } from '../../shared/modules/string-utils'; +import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; import ComposableObservableStore from './lib/ComposableObservableStore'; import AccountTracker from './lib/account-tracker'; import createLoggerMiddleware from './lib/createLoggerMiddleware'; @@ -161,7 +164,7 @@ export default class MetamaskController extends EventEmitter { MILLISECOND * 200, ); this.opts = opts; - this.extension = opts.extension; + this.extension = opts.browser; this.platform = opts.platform; this.notificationManager = opts.notificationManager; const initState = opts.initState || {}; @@ -319,6 +322,10 @@ export default class MetamaskController extends EventEmitter { captureException, }); + this.on('update', (update) => { + this.metaMetricsController.handleMetaMaskStateUpdate(update); + }); + const gasFeeMessenger = this.controllerMessenger.getRestricted({ name: 'GasFeeController', }); @@ -634,6 +641,27 @@ export default class MetamaskController extends EventEmitter { state: initState.SnapController, messenger: snapControllerMessenger, }); + + this.rateLimitController = new RateLimitController({ + messenger: this.controllerMessenger.getRestricted({ + name: 'RateLimitController', + }), + implementations: { + showNativeNotification: (origin, message) => { + const subjectMetadataState = this.controllerMessenger.call( + 'SubjectMetadataController:getState', + ); + + const originMetadata = subjectMetadataState.subjectMetadata[origin]; + + this.platform._showNotification( + originMetadata?.name ?? origin, + message, + ); + return null; + }, + }, + }); ///: END:ONLY_INCLUDE_IN this.detectTokensController = new DetectTokensController({ @@ -717,6 +745,9 @@ export default class MetamaskController extends EventEmitter { ), getAccountType: this.getAccountType.bind(this), getDeviceModel: this.getDeviceModel.bind(this), + getTokenStandardAndDetails: this.assetsContractController.getTokenStandardAndDetails.bind( + this.assetsContractController, + ), }); this.txController.on('newUnapprovedTx', () => opts.showUserConfirmation()); @@ -750,7 +781,7 @@ export default class MetamaskController extends EventEmitter { from: userAddress, } = txMeta.txParams; const { chainId } = txMeta; - const transactionData = getTransactionData(data); + const transactionData = parseStandardTokenTransactionData(data); const tokenAmountOrTokenId = getTokenValueParam(transactionData); const { allCollectibles } = this.collectiblesController.state; @@ -974,7 +1005,7 @@ export default class MetamaskController extends EventEmitter { } // Lazily update the store with the current extension environment - this.extension.runtime.getPlatformInfo(({ os }) => { + this.extension.runtime.getPlatformInfo().then(({ os }) => { this.appStateController.setBrowserEnvironment( os, // This method is presently only supported by Firefox @@ -1033,6 +1064,14 @@ export default class MetamaskController extends EventEmitter { type: MESSAGE_TYPE.SNAP_CONFIRM, requestData: confirmationData, }), + showNotification: (origin, args) => + this.controllerMessenger.call( + 'RateLimitController:call', + origin, + 'showNativeNotification', + origin, + args.message, + ), updateSnapState: this.controllerMessenger.call.bind( this.controllerMessenger, 'SnapController:updateSnapState', @@ -1552,6 +1591,9 @@ export default class MetamaskController extends EventEmitter { txController, ), + updatePreviousGasParams: txController.updatePreviousGasParams.bind( + txController, + ), // messageManager signMessage: this.signMessage.bind(this), cancelMessage: this.cancelMessage.bind(this), diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 2739c7dcf..f1771c173 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -65,7 +65,7 @@ class ThreeBoxControllerMock { } } -const ExtensionizerMock = { +const browserPolyfillMock = { runtime: { id: 'fake-extension-id', onInstalled: { @@ -148,7 +148,7 @@ describe('MetaMaskController', function () { showTransactionNotification: () => undefined, getVersion: () => 'foo', }, - extension: ExtensionizerMock, + browser: browserPolyfillMock, infuraProjectId: 'foo', }); diff --git a/app/scripts/phishing-detect.js b/app/scripts/phishing-detect.js index bb6bf5b2e..68c30d501 100644 --- a/app/scripts/phishing-detect.js +++ b/app/scripts/phishing-detect.js @@ -1,6 +1,6 @@ import querystring from 'querystring'; import PortStream from 'extension-port-stream'; -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; import createRandomId from '../../shared/modules/random-id'; import { setupMultiplex } from './lib/stream-utils'; import { getEnvironmentType } from './lib/util'; @@ -21,7 +21,7 @@ function start() { global.platform = new ExtensionPlatform(); - const extensionPort = extension.runtime.connect({ + const extensionPort = browser.runtime.connect({ name: getEnvironmentType(), }); const connectionStream = new PortStream(extensionPort); diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 0626f1545..dc06d17a9 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -1,4 +1,5 @@ -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; + import { getBlockExplorerLink } from '@metamask/etherscan-link'; import { getEnvironmentType, checkForError } from '../lib/util'; import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app'; @@ -9,12 +10,12 @@ export default class ExtensionPlatform { // Public // reload() { - extension.runtime.reload(); + browser.runtime.reload(); } openTab(options) { return new Promise((resolve, reject) => { - extension.tabs.create(options, (newTab) => { + browser.tabs.create(options).then((newTab) => { const error = checkForError(); if (error) { return reject(error); @@ -26,7 +27,7 @@ export default class ExtensionPlatform { openWindow(options) { return new Promise((resolve, reject) => { - extension.windows.create(options, (newWindow) => { + browser.windows.create(options).then((newWindow) => { const error = checkForError(); if (error) { return reject(error); @@ -38,7 +39,7 @@ export default class ExtensionPlatform { focusWindow(windowId) { return new Promise((resolve, reject) => { - extension.windows.update(windowId, { focused: true }, () => { + browser.windows.update(windowId, { focused: true }).then(() => { const error = checkForError(); if (error) { return reject(error); @@ -50,7 +51,7 @@ export default class ExtensionPlatform { updateWindowPosition(windowId, left, top) { return new Promise((resolve, reject) => { - extension.windows.update(windowId, { left, top }, () => { + browser.windows.update(windowId, { left, top }).then(() => { const error = checkForError(); if (error) { return reject(error); @@ -62,7 +63,7 @@ export default class ExtensionPlatform { getLastFocusedWindow() { return new Promise((resolve, reject) => { - extension.windows.getLastFocused((windowObject) => { + browser.windows.getLastFocused().then((windowObject) => { const error = checkForError(); if (error) { return reject(error); @@ -73,8 +74,8 @@ export default class ExtensionPlatform { } closeCurrentWindow() { - return extension.windows.getCurrent((windowDetails) => { - return extension.windows.remove(windowDetails.id); + return browser.windows.getCurrent().then((windowDetails) => { + return browser.windows.remove(windowDetails.id); }); } @@ -82,7 +83,7 @@ export default class ExtensionPlatform { const { version, version_name: versionName, - } = extension.runtime.getManifest(); + } = browser.runtime.getManifest(); const versionParts = version.split('.'); if (versionName) { @@ -115,7 +116,7 @@ export default class ExtensionPlatform { queryString = null, keepWindowOpen = false, ) { - let extensionURL = extension.runtime.getURL('home.html'); + let extensionURL = browser.runtime.getURL('home.html'); if (route) { extensionURL += `#${route}`; @@ -136,9 +137,9 @@ export default class ExtensionPlatform { getPlatformInfo(cb) { try { - extension.runtime.getPlatformInfo((platform) => { - cb(null, platform); - }); + const platformInfo = browser.runtime.getPlatformInfo(); + cb(platformInfo); + return; } catch (e) { cb(e); // eslint-disable-next-line no-useless-return @@ -163,12 +164,12 @@ export default class ExtensionPlatform { } addOnRemovedListener(listener) { - extension.windows.onRemoved.addListener(listener); + browser.windows.onRemoved.addListener(listener); } getAllWindows() { return new Promise((resolve, reject) => { - extension.windows.getAll((windows) => { + browser.windows.getAll().then((windows) => { const error = checkForError(); if (error) { return reject(error); @@ -180,7 +181,7 @@ export default class ExtensionPlatform { getActiveTabs() { return new Promise((resolve, reject) => { - extension.tabs.query({ active: true }, (tabs) => { + browser.tabs.query({ active: true }).then((tabs) => { const error = checkForError(); if (error) { return reject(error); @@ -192,7 +193,7 @@ export default class ExtensionPlatform { currentTab() { return new Promise((resolve, reject) => { - extension.tabs.getCurrent((tab) => { + browser.tabs.getCurrent().then((tab) => { const err = checkForError(); if (err) { reject(err); @@ -205,7 +206,7 @@ export default class ExtensionPlatform { switchToTab(tabId) { return new Promise((resolve, reject) => { - extension.tabs.update(tabId, { highlighted: true }, (tab) => { + browser.tabs.update(tabId, { highlighted: true }).then((tab) => { const err = checkForError(); if (err) { reject(err); @@ -218,7 +219,7 @@ export default class ExtensionPlatform { closeTab(tabId) { return new Promise((resolve, reject) => { - extension.tabs.remove(tabId, () => { + browser.tabs.remove(tabId).then(() => { const err = checkForError(); if (err) { reject(err); @@ -252,23 +253,23 @@ export default class ExtensionPlatform { } _showNotification(title, message, url) { - extension.notifications.create(url, { + browser.notifications.create(url, { type: 'basic', title, - iconUrl: extension.extension.getURL('../../images/icon-64.png'), + iconUrl: browser.runtime.getURL('../../images/icon-64.png'), message, }); } _subscribeToNotificationClicked() { - if (!extension.notifications.onClicked.hasListener(this._viewOnEtherscan)) { - extension.notifications.onClicked.addListener(this._viewOnEtherscan); + if (!browser.notifications.onClicked.hasListener(this._viewOnEtherscan)) { + browser.notifications.onClicked.addListener(this._viewOnEtherscan); } } _viewOnEtherscan(url) { if (url.startsWith('https://')) { - extension.tabs.create({ url }); + browser.tabs.create({ url }); } } } diff --git a/app/scripts/platforms/extension.test.js b/app/scripts/platforms/extension.test.js index af7852b8b..de8b05cdd 100644 --- a/app/scripts/platforms/extension.test.js +++ b/app/scripts/platforms/extension.test.js @@ -1,7 +1,7 @@ -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; import ExtensionPlatform from './extension'; -jest.mock('extensionizer', () => { +jest.mock('webextension-polyfill', () => { return { runtime: { getManifest: jest.fn(), @@ -17,7 +17,7 @@ describe('extension platform', () => { describe('getVersion', () => { it('should return non-prerelease version', () => { - extension.runtime.getManifest.mockReturnValue({ version: '1.2.3' }); + browser.runtime.getManifest.mockReturnValue({ version: '1.2.3' }); const extensionPlatform = new ExtensionPlatform(); const version = extensionPlatform.getVersion(); @@ -26,7 +26,7 @@ describe('extension platform', () => { }); it('should return SemVer-formatted version for Chrome style manifest of prerelease', () => { - extension.runtime.getManifest.mockReturnValue({ + browser.runtime.getManifest.mockReturnValue({ version: '1.2.3.0', version_name: '1.2.3-beta.0', }); @@ -38,7 +38,7 @@ describe('extension platform', () => { }); it('should return SemVer-formatted version for Firefox style manifest of prerelease', () => { - extension.runtime.getManifest.mockReturnValue({ + browser.runtime.getManifest.mockReturnValue({ version: '1.2.3beta0', }); const extensionPlatform = new ExtensionPlatform(); @@ -49,7 +49,7 @@ describe('extension platform', () => { }); it('should throw error if build version is missing from Chrome style prerelease manifest', () => { - extension.runtime.getManifest.mockReturnValue({ + browser.runtime.getManifest.mockReturnValue({ version: '1.2.3', version_name: '1.2.3-beta.0', }); @@ -61,7 +61,7 @@ describe('extension platform', () => { }); it('should throw error if version name is missing from Chrome style prerelease manifest', () => { - extension.runtime.getManifest.mockReturnValue({ + browser.runtime.getManifest.mockReturnValue({ version: '1.2.3.0', }); const extensionPlatform = new ExtensionPlatform(); @@ -70,7 +70,7 @@ describe('extension platform', () => { }); it('should throw error if version includes four parts in a Firefox style manifest', () => { - extension.runtime.getManifest.mockReturnValue({ + browser.runtime.getManifest.mockReturnValue({ version: '1.2.3.4', }); const extensionPlatform = new ExtensionPlatform(); @@ -79,7 +79,7 @@ describe('extension platform', () => { }); it('should throw error if build version is missing from Firefox style prerelease manifest', () => { - extension.runtime.getManifest.mockReturnValue({ + browser.runtime.getManifest.mockReturnValue({ version: '1.2.3beta', }); const extensionPlatform = new ExtensionPlatform(); @@ -90,7 +90,7 @@ describe('extension platform', () => { }); it('should throw error if patch is missing from Firefox style prerelease manifest', () => { - extension.runtime.getManifest.mockReturnValue({ + browser.runtime.getManifest.mockReturnValue({ version: '1.2.beta0', }); const extensionPlatform = new ExtensionPlatform(); diff --git a/app/scripts/ui.js b/app/scripts/ui.js index 362567aa8..1c4a3fc1c 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -5,7 +5,7 @@ import '@formatjs/intl-relativetimeformat/polyfill'; import 'react-devtools'; import PortStream from 'extension-port-stream'; -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; import Eth from 'ethjs'; import EthQuery from 'eth-query'; @@ -31,7 +31,7 @@ async function start() { const windowType = getEnvironmentType(); // setup stream to background - const extensionPort = extension.runtime.connect({ name: windowType }); + const extensionPort = browser.runtime.connect({ name: windowType }); const connectionStream = new PortStream(extensionPort); const activeTab = await queryCurrentActiveTab(windowType); @@ -72,7 +72,7 @@ async function queryCurrentActiveTab(windowType) { return; } - extension.tabs.query({ active: true, currentWindow: true }, (tabs) => { + browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { const [activeTab] = tabs; const { id, title, url } = activeTab; const { origin, protocol } = url ? new URL(url) : {}; diff --git a/babel.config.js b/babel.config.js index efd474696..31e829dec 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,7 +1,11 @@ module.exports = function (api) { api.cache(false); return { + parserOpts: { + strictMode: true, + }, presets: [ + '@babel/preset-typescript', [ '@babel/preset-env', { diff --git a/development/build/index.js b/development/build/index.js index dd0e7b4c9..74106e7ac 100755 --- a/development/build/index.js +++ b/development/build/index.js @@ -33,15 +33,18 @@ require('@babel/plugin-proposal-optional-chaining'); require('@babel/plugin-proposal-nullish-coalescing-operator'); require('@babel/preset-env'); require('@babel/preset-react'); +require('@babel/preset-typescript'); require('@babel/core'); // ESLint-related require('@babel/eslint-parser'); require('@babel/eslint-plugin'); require('@metamask/eslint-config'); require('@metamask/eslint-config-nodejs'); +require('@typescript-eslint/parser'); require('eslint'); require('eslint-config-prettier'); require('eslint-import-resolver-node'); +require('eslint-import-resolver-typescript'); require('eslint-plugin-import'); require('eslint-plugin-jsdoc'); require('eslint-plugin-node'); diff --git a/development/build/scripts.js b/development/build/scripts.js index 063fc5a7e..d2c369c52 100644 --- a/development/build/scripts.js +++ b/development/build/scripts.js @@ -34,7 +34,7 @@ const metamaskrc = require('rc')('metamask', { INFURA_PROD_PROJECT_ID: process.env.INFURA_PROD_PROJECT_ID, ONBOARDING_V2: process.env.ONBOARDING_V2, COLLECTIBLES_V1: process.env.COLLECTIBLES_V1, - DARK_MODE_V1: process.env.DARK_MODE_V1, + TOKEN_DETECTION_V2: process.env.TOKEN_DETECTION_V2, SEGMENT_HOST: process.env.SEGMENT_HOST, SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY, SEGMENT_BETA_WRITE_KEY: process.env.SEGMENT_BETA_WRITE_KEY, @@ -614,6 +614,7 @@ function setupBundlerDefaults( }, ) { const { bundlerOpts } = buildConfiguration; + const extensions = ['.js', '.ts', '.tsx']; Object.assign(bundlerOpts, { // Source transforms @@ -621,10 +622,16 @@ function setupBundlerDefaults( // Remove code that should be excluded from builds of the current type createRemoveFencedCodeTransform(buildType, shouldLintFenceFiles), // Transpile top-level code - babelify, + [ + babelify, + // Run TypeScript files through Babel + { extensions }, + ], // Inline `fs.readFileSync` files brfs, ], + // Look for TypeScript files when walking the dependency tree + extensions, // Use entryFilepath for moduleIds, easier to determine origin file fullPaths: devMode, // For sourcemaps @@ -811,7 +818,7 @@ function getEnvironmentVariables({ buildType, devMode, testing, version }) { SWAPS_USE_DEV_APIS: process.env.SWAPS_USE_DEV_APIS === '1', ONBOARDING_V2: metamaskrc.ONBOARDING_V2 === '1', COLLECTIBLES_V1: metamaskrc.COLLECTIBLES_V1 === '1', - DARK_MODE_V1: metamaskrc.DARK_MODE_V1 === '1', + TOKEN_DETECTION_V2: metamaskrc.TOKEN_DETECTION_V2 === '1', }; } diff --git a/jsconfig.json b/jsconfig.json deleted file mode 100644 index 28d588ea9..000000000 --- a/jsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "target": "ES6", - "module": "commonjs" - }, - "include": ["ui/**/*.js", "app/**/*.js", "shared/**/*.js"] -} diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 4d5410150..7b93e1ecd 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -2248,12 +2248,6 @@ "stream-browserify": true } }, - "extensionizer": { - "globals": { - "browser": true, - "chrome": true - } - }, "faker": { "globals": { "console.error": true, @@ -5203,6 +5197,15 @@ "utf8": true } }, + "webextension-polyfill": { + "globals": { + "browser": true, + "chrome": true, + "console.error": true, + "console.warn": true, + "define": true + } + }, "webrtcsupport": { "globals": { "AudioContext": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index abe69b023..de99bb329 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -2266,12 +2266,6 @@ "stream-browserify": true } }, - "extensionizer": { - "globals": { - "browser": true, - "chrome": true - } - }, "faker": { "globals": { "console.error": true, @@ -5221,6 +5215,15 @@ "utf8": true } }, + "webextension-polyfill": { + "globals": { + "browser": true, + "chrome": true, + "console.error": true, + "console.warn": true, + "define": true + } + }, "webrtcsupport": { "globals": { "AudioContext": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 4d5410150..7b93e1ecd 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -2248,12 +2248,6 @@ "stream-browserify": true } }, - "extensionizer": { - "globals": { - "browser": true, - "chrome": true - } - }, "faker": { "globals": { "console.error": true, @@ -5203,6 +5197,15 @@ "utf8": true } }, + "webextension-polyfill": { + "globals": { + "browser": true, + "chrome": true, + "console.error": true, + "console.warn": true, + "define": true + } + }, "webrtcsupport": { "globals": { "AudioContext": true, diff --git a/lavamoat/build-system/policy-override.json b/lavamoat/build-system/policy-override.json index 282e26ff0..709d24252 100644 --- a/lavamoat/build-system/policy-override.json +++ b/lavamoat/build-system/policy-override.json @@ -3,13 +3,14 @@ "@babel/core": { "packages": { "": true, - "@babel/preset-env": true, - "@babel/preset-react": true, - "@babel/plugin-transform-runtime": true, "@babel/plugin-proposal-class-properties": true, + "@babel/plugin-proposal-nullish-coalescing-operator": true, "@babel/plugin-proposal-object-rest-spread": true, "@babel/plugin-proposal-optional-chaining": true, - "@babel/plugin-proposal-nullish-coalescing-operator": true + "@babel/plugin-transform-runtime": true, + "@babel/preset-env": true, + "@babel/preset-react": true, + "@babel/preset-typescript": true } }, "@eslint/eslintrc": { @@ -19,6 +20,8 @@ "@babel/eslint-plugin": true, "@metamask/eslint-config": true, "@metamask/eslint-config-nodejs": true, + "@metamask/eslint-config-typescript": true, + "@typescript-eslint/eslint-plugin": true, "eslint": true, "eslint-config-prettier": true, "eslint-plugin-import": true, @@ -29,20 +32,58 @@ "eslint-plugin-react-hooks": true } }, - "eslint-module-utils": { + "@typescript-eslint/eslint-plugin": { "packages": { - "eslint-import-resolver-node": true, - "@babel/eslint-parser": true + "@typescript-eslint/experimental-utils": true, + "@typescript-eslint/scope-manager": true, + "debug": true, + "eslint": true, + "ignore": true, + "regexpp": true, + "semver": true, + "tsutils": true, + "typescript": true } }, - "node-sass": { - "native": true + "@typescript-eslint/experimental-utils": { + "builtin": { + "path": true + }, + "packages": { + "@typescript-eslint/scope-manager": true, + "@typescript-eslint/types": true, + "eslint": true, + "eslint-scope": true, + "eslint-utils": true + } + }, + "@typescript-eslint/scope-manager": { + "packages": { + "@typescript-eslint/types": true, + "@typescript-eslint/visitor-keys": true + } + }, + "@typescript-eslint/visitor-keys": { + "packages": { + "eslint-visitor-keys": true + } + }, + "eslint-module-utils": { + "packages": { + "@babel/eslint-parser": true, + "@typescript-eslint/parser": true, + "eslint-import-resolver-node": true, + "eslint-import-resolver-typescript": true + } }, "module-deps": { "packages": { "loose-envify": true } }, + "node-sass": { + "native": true + }, "sass": { "env": "unfrozen", "builtin": { @@ -51,6 +92,17 @@ "globals": { "Buffer": true } + }, + "tsutils": { + "packages": { + "typescript": true, + "tslib": true + } + }, + "typescript": { + "globals": { + "globalThis": true + } } } } diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index e95acc4d6..03fe49344 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -97,6 +97,7 @@ "packages": { "@babel/core": true, "@babel/helper-annotate-as-pure": true, + "@babel/helper-environment-visitor": true, "@babel/helper-function-name": true, "@babel/helper-member-expression-to-functions": true, "@babel/helper-optimise-call-expression": true, @@ -129,6 +130,11 @@ "resolve": true } }, + "@babel/helper-environment-visitor": { + "packages": { + "@babel/types": true + } + }, "@babel/helper-explode-assignable-expression": { "packages": { "@babel/types": true @@ -195,6 +201,7 @@ }, "@babel/helper-replace-supers": { "packages": { + "@babel/helper-environment-visitor": true, "@babel/helper-member-expression-to-functions": true, "@babel/helper-optimise-call-expression": true, "@babel/traverse": true, @@ -424,6 +431,11 @@ "@babel/helper-plugin-utils": true } }, + "@babel/plugin-syntax-typescript": { + "packages": { + "@babel/helper-plugin-utils": true + } + }, "@babel/plugin-transform-arrow-functions": { "packages": { "@babel/helper-plugin-utils": true @@ -672,6 +684,20 @@ "@babel/helper-plugin-utils": true } }, + "@babel/plugin-transform-typescript": { + "builtin": { + "assert": true + }, + "globals": { + "console.warn": true + }, + "packages": { + "@babel/core": true, + "@babel/helper-create-class-features-plugin": true, + "@babel/helper-plugin-utils": true, + "@babel/plugin-syntax-typescript": true + } + }, "@babel/plugin-transform-unicode-escapes": { "packages": { "@babel/core": true, @@ -777,6 +803,13 @@ "@babel/plugin-transform-react-pure-annotations": true } }, + "@babel/preset-typescript": { + "packages": { + "@babel/helper-plugin-utils": true, + "@babel/helper-validator-option": true, + "@babel/plugin-transform-typescript": true + } + }, "@babel/template": { "packages": { "@babel/code-frame": true, @@ -792,6 +825,7 @@ "packages": { "@babel/code-frame": true, "@babel/generator": true, + "@babel/helper-environment-visitor": true, "@babel/helper-function-name": true, "@babel/helper-hoist-variables": true, "@babel/helper-split-export-declaration": true, @@ -964,6 +998,48 @@ "unist-util-find-all-after": true } }, + "@typescript-eslint/parser": { + "packages": { + "@typescript-eslint/scope-manager": true, + "@typescript-eslint/typescript-estree": true, + "debug": true, + "typescript": true + } + }, + "@typescript-eslint/scope-manager": { + "packages": { + "@typescript-eslint/types": true, + "@typescript-eslint/visitor-keys": true + } + }, + "@typescript-eslint/typescript-estree": { + "builtin": { + "fs": true, + "path": true + }, + "globals": { + "console.log": true, + "console.warn": true, + "new": true, + "process": true, + "target": true + }, + "packages": { + "@typescript-eslint/types": true, + "@typescript-eslint/visitor-keys": true, + "debug": true, + "globby": true, + "is-glob": true, + "semver": true, + "tsutils": true, + "typescript": true + } + }, + "@typescript-eslint/visitor-keys": { + "packages": { + "eslint-visitor-keys": true + } + }, "JSONStream": { "globals": { "Buffer": true @@ -1961,6 +2037,22 @@ "resolve": true } }, + "eslint-import-resolver-typescript": { + "builtin": { + "path": true + }, + "globals": { + "console.warn": true, + "process.cwd": true + }, + "packages": { + "debug": true, + "glob": true, + "is-glob": true, + "resolve": true, + "tsconfig-paths": true + } + }, "eslint-module-utils": { "builtin": { "crypto.createHash": true, @@ -2458,12 +2550,7 @@ "builtin": { "assert": true, "events.EventEmitter": true, - "fs.lstat": true, - "fs.lstatSync": true, - "fs.readdir": true, - "fs.readdirSync": true, - "fs.stat": true, - "fs.statSync": true, + "fs": true, "path.join": true, "path.resolve": true, "util": true @@ -4860,17 +4947,41 @@ "builtin": { "fs.existsSync": true, "fs.lstatSync": true, + "fs.readFile": true, "fs.readFileSync": true, + "fs.stat": true, "fs.statSync": true, + "module._resolveFilename": true, + "module.builtinModules": true, "path.dirname": true, + "path.isAbsolute": true, "path.join": true, - "path.resolve": true + "path.resolve": true, + "path.sep": true + }, + "globals": { + "console.warn": true, + "process.argv.slice": true, + "process.cwd": true, + "process.env": true }, "packages": { "json5": true, + "minimist": true, "strip-bom": true } }, + "tslib": { + "globals": { + "define": true + } + }, + "tsutils": { + "packages": { + "tslib": true, + "typescript": true + } + }, "type-check": { "packages": { "prelude-ls": true @@ -4888,29 +4999,19 @@ "builtin": { "buffer.Buffer": true, "crypto": true, - "fs.closeSync": true, - "fs.mkdirSync": true, - "fs.openSync": true, - "fs.readFileSync": true, - "fs.readdirSync": true, - "fs.realpathSync": true, - "fs.statSync": true, - "fs.unlinkSync": true, - "fs.unwatchFile": true, - "fs.utimesSync": true, - "fs.watch": true, - "fs.watchFile": true, - "fs.writeFileSync": true, - "fs.writeSync": true, + "fs": true, "inspector": true, "os.EOL": true, "os.platform": true, "path.dirname": true, "path.join": true, - "path.resolve": true + "path.resolve": true, + "perf_hooks.PerformanceObserver": true, + "perf_hooks.performance": true }, "globals": { "Intl": true, + "PerformanceObserver": true, "TypeScript": "write", "__dirname": true, "__filename": true, @@ -4919,7 +5020,6 @@ "console.log": true, "gc": true, "globalThis": "write", - "onProfilerEvent": true, "performance": true, "process": true, "setTimeout": true, diff --git a/package.json b/package.json index a30a0d52e..83524c3d7 100644 --- a/package.json +++ b/package.json @@ -29,26 +29,29 @@ "test:unit:global": "mocha test/unit-global/*.test.js", "test:unit:mocha": "mocha './app/**/*.test.js'", "test:e2e:chrome": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js", - "test:e2e:chrome:metrics": "SELENIUM_BROWSER=chrome node test/e2e/run-e2e-test.js test/e2e/metrics.spec.js", "test:e2e:chrome:snaps": "SELENIUM_BROWSER=chrome node test/e2e/run-all.js --snaps", "test:e2e:firefox": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js", - "test:e2e:firefox:metrics": "SELENIUM_BROWSER=firefox node test/e2e/run-e2e-test.js test/e2e/metrics.spec.js", "test:e2e:firefox:snaps": "SELENIUM_BROWSER=firefox node test/e2e/run-all.js --snaps", "test:e2e:single": "node test/e2e/run-e2e-test.js", "test:coverage:mocha": "nyc --reporter=text --reporter=html yarn test:unit:mocha", "test:coverage:jest": "yarn test:unit:jest --coverage --maxWorkers=2", "ganache:start": "./development/run-ganache.sh", "sentry:publish": "node ./development/sentry-publish.js", - "lint:prettier": "prettier '**/*.json'", - "lint": "yarn lint:prettier --check '**/*.json' && eslint . --ext js,snap --cache && yarn lint:styles", - "lint:fix": "yarn lint:prettier --write '**/*.json' && eslint . --ext js --cache --fix && yarn lint:styles --fix", + "lint": "yarn lint:prettier && yarn lint:eslint && yarn lint:tsc && yarn lint:styles", + "lint:fix": "yarn lint:prettier:fix && yarn lint:eslint:fix && yarn lint:styles:fix", + "lint:prettier": "prettier '**/*.json' --check", + "lint:prettier:fix": "prettier '**/*.json' --write", "lint:changed": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint", "lint:changed:fix": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint --fix", "lint:changelog": "auto-changelog validate", "lint:changelog:rc": "auto-changelog validate --rc", + "lint:eslint": "eslint . --ext js,ts,tsx,snap --cache", + "lint:eslint:fix": "yarn lint:eslint --fix", + "lint:lockfile": "lockfile-lint --path yarn.lock --allowed-hosts npm yarn github.com codeload.github.com --empty-hostname false --allowed-schemes \"https:\" \"git+https:\"", "lint:shellcheck": "./development/shellcheck.sh", "lint:styles": "stylelint '*/**/*.scss'", - "lint:lockfile": "lockfile-lint --path yarn.lock --allowed-hosts npm yarn github.com codeload.github.com --empty-hostname false --allowed-schemes \"https:\" \"git+https:\"", + "lint:styles:fix": "yarn lint:styles --fix", + "lint:tsc": "tsc --project tsconfig.json --noEmit", "validate-source-maps": "node ./development/sourcemap-validator.js", "verify-locales": "node ./development/verify-locale-strings.js", "verify-locales:fix": "node ./development/verify-locale-strings.js --fix", @@ -112,13 +115,14 @@ "@material-ui/core": "^4.11.0", "@metamask/contract-metadata": "^1.31.0", "@metamask/controllers": "^27.0.0", - "@metamask/design-tokens": "^1.3.0", + "@metamask/design-tokens": "^1.4.2", "@metamask/eth-ledger-bridge-keyring": "^0.10.0", "@metamask/eth-token-tracker": "^4.0.0", "@metamask/etherscan-link": "^2.1.0", "@metamask/iframe-execution-environment-service": "^0.10.6", "@metamask/jazzicon": "^2.0.0", "@metamask/logo": "^3.1.1", + "@metamask/metamask-eth-abis": "^3.0.0", "@metamask/obs-store": "^5.0.0", "@metamask/post-message-stream": "^4.0.0", "@metamask/providers": "^8.1.1", @@ -169,7 +173,6 @@ "ethjs-ens": "^2.0.0", "ethjs-query": "^0.3.4", "extension-port-stream": "^2.0.0", - "extensionizer": "^1.0.1", "fast-json-patch": "^2.2.1", "fast-safe-stringify": "^2.0.7", "fuse.js": "^3.2.0", @@ -238,6 +241,7 @@ "@babel/plugin-transform-runtime": "^7.5.5", "@babel/preset-env": "^7.5.5", "@babel/preset-react": "^7.0.0", + "@babel/preset-typescript": "^7.16.7", "@babel/register": "^7.5.5", "@lavamoat/allow-scripts": "^2.0.0", "@lavamoat/lavapack": "^2.0.4", @@ -246,6 +250,7 @@ "@metamask/eslint-config-jest": "^9.0.0", "@metamask/eslint-config-mocha": "^9.0.0", "@metamask/eslint-config-nodejs": "^9.0.0", + "@metamask/eslint-config-typescript": "^9.0.1", "@metamask/forwarder": "^1.1.0", "@metamask/test-dapp": "^5.0.0", "@sentry/cli": "^1.58.0", @@ -266,7 +271,10 @@ "@testing-library/react": "^10.4.8", "@testing-library/react-hooks": "^3.2.1", "@testing-library/user-event": "^14.0.0-beta.12", + "@tsconfig/node14": "^1.0.1", "@types/react": "^16.9.53", + "@typescript-eslint/eslint-plugin": "^4.20.0", + "@typescript-eslint/parser": "^4.20.0", "addons-linter": "1.14.0", "babelify": "^10.0.0", "bify-module-groups": "^1.0.0", @@ -289,6 +297,7 @@ "eslint": "^7.23.0", "eslint-config-prettier": "^8.1.0", "eslint-import-resolver-node": "^0.3.4", + "eslint-import-resolver-typescript": "^2.5.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^24.3.4", "eslint-plugin-jsdoc": "^37.0.3", @@ -359,11 +368,13 @@ "terser": "^5.7.0", "through2": "^4.0.2", "ttest": "^2.1.1", + "typescript": "~4.4.0", "vinyl": "^2.2.1", "vinyl-buffer": "^1.0.1", "vinyl-source-stream": "^2.0.0", "vinyl-sourcemaps-apply": "^0.2.1", "watchify": "^4.0.0", + "webextension-polyfill": "^0.8.0", "webpack": "^4.41.6", "yargs": "^17.0.1", "yarn-deduplicate": "^3.1.0" diff --git a/patches/@keystonehq+bc-ur-registry+0.4.4.patch b/patches/@keystonehq+bc-ur-registry+0.4.4.patch new file mode 100644 index 000000000..eec0a3388 --- /dev/null +++ b/patches/@keystonehq+bc-ur-registry+0.4.4.patch @@ -0,0 +1,1830 @@ +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/Bytes.ts b/node_modules/@keystonehq/bc-ur-registry/src/Bytes.ts +deleted file mode 100644 +index a5f9f7d..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/Bytes.ts ++++ /dev/null +@@ -1,34 +0,0 @@ +-import { decodeToDataItem, DataItem } from './lib'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +- +-export class Bytes extends RegistryItem { +- getRegistryType = () => { +- return RegistryTypes.BYTES; +- }; +- +- constructor(private bytes: Buffer) { +- super(); +- } +- +- getData = () => this.bytes; +- +- toDataItem = () => { +- return new DataItem(this.bytes); +- }; +- +- public static fromDataItem = (dataItem: DataItem) => { +- const bytes = dataItem.getData(); +- if (!bytes) { +- throw new Error( +- `#[ur-registry][Bytes][fn.fromDataItem]: decoded [dataItem][#data] is undefined: ${dataItem}`, +- ); +- } +- return new Bytes(bytes); +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return Bytes.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/CryptoAccount.ts b/node_modules/@keystonehq/bc-ur-registry/src/CryptoAccount.ts +deleted file mode 100644 +index 753e535..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/CryptoAccount.ts ++++ /dev/null +@@ -1,57 +0,0 @@ +-import { CryptoOutput } from '.'; +-import { decodeToDataItem, DataItem } from './lib'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +- +-enum Keys { +- masterFingerprint = 1, +- outputDescriptors, +-} +- +-export class CryptoAccount extends RegistryItem { +- getRegistryType = () => { +- return RegistryTypes.CRYPTO_ACCOUNT; +- }; +- +- constructor( +- private masterFingerprint: Buffer, +- private outputDescriptors: CryptoOutput[], +- ) { +- super(); +- } +- +- public getMasterFingerprint = () => this.masterFingerprint; +- public getOutputDescriptors = () => this.outputDescriptors; +- +- public toDataItem = () => { +- const map = {}; +- if (this.masterFingerprint) { +- map[Keys.masterFingerprint] = this.masterFingerprint.readUInt32BE(0); +- } +- if (this.outputDescriptors) { +- map[Keys.outputDescriptors] = this.outputDescriptors.map((item) => +- item.toDataItem(), +- ); +- } +- return new DataItem(map); +- }; +- +- public static fromDataItem = (dataItem: DataItem) => { +- const map = dataItem.getData(); +- const masterFingerprint = Buffer.alloc(4); +- const _masterFingerprint = map[Keys.masterFingerprint]; +- if (_masterFingerprint) { +- masterFingerprint.writeUInt32BE(_masterFingerprint, 0); +- } +- const outputDescriptors = map[Keys.outputDescriptors] as DataItem[]; +- const cryptoOutputs = outputDescriptors.map((item) => +- CryptoOutput.fromDataItem(item), +- ); +- return new CryptoAccount(masterFingerprint, cryptoOutputs); +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return CryptoAccount.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/CryptoCoinInfo.ts b/node_modules/@keystonehq/bc-ur-registry/src/CryptoCoinInfo.ts +deleted file mode 100644 +index 0201682..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/CryptoCoinInfo.ts ++++ /dev/null +@@ -1,58 +0,0 @@ +-import { decodeToDataItem, DataItem } from './lib'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +- +-enum Keys { +- type = '1', +- network = '2', +-} +- +-export enum Type { +- bitcoin = 0, +-} +- +-export enum Network { +- mainnet, +- testnet, +-} +- +-export class CryptoCoinInfo extends RegistryItem { +- getRegistryType = () => { +- return RegistryTypes.CRYPTO_COIN_INFO; +- }; +- +- constructor(private type?: Type, private network?: Network) { +- super(); +- } +- +- public getType = () => { +- return this.type || Type.bitcoin; +- }; +- +- public getNetwork = () => { +- return this.network || Network.mainnet; +- }; +- +- public toDataItem = () => { +- const map = {}; +- if (this.type) { +- map[Keys.type] = this.type; +- } +- if (this.network) { +- map[Keys.network] = this.network; +- } +- return new DataItem(map); +- }; +- +- public static fromDataItem = (dataItem: DataItem) => { +- const map = dataItem.getData(); +- const type = map[Keys.type]; +- const network = map[Keys.network]; +- return new CryptoCoinInfo(type, network); +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return CryptoCoinInfo.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/CryptoECKey.ts b/node_modules/@keystonehq/bc-ur-registry/src/CryptoECKey.ts +deleted file mode 100644 +index 1e964fc..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/CryptoECKey.ts ++++ /dev/null +@@ -1,59 +0,0 @@ +-import { decodeToDataItem, DataItem } from './lib'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +- +-enum Keys { +- curve = 1, +- private, +- data, +-} +- +-export class CryptoECKey extends RegistryItem { +- private data: Buffer; +- private curve: number; +- private privateKey: boolean; +- constructor(args: { data: Buffer; curve?: number; privateKey?: boolean }) { +- super(); +- this.data = args.data; +- this.curve = args.curve; +- this.privateKey = args.privateKey; +- } +- +- public getCurve = () => this.curve || 0; +- public isPrivateKey = () => this.privateKey || false; +- public getData = () => this.data; +- +- getRegistryType = () => { +- return RegistryTypes.CRYPTO_ECKEY; +- }; +- +- toDataItem = () => { +- const map = {}; +- if (this.curve) { +- map[Keys.curve] = this.curve; +- } +- if (this.privateKey !== undefined) { +- map[Keys.private] = this.privateKey; +- } +- map[Keys.data] = this.data; +- return new DataItem(map); +- }; +- +- static fromDataItem = (dataItem: DataItem) => { +- const map = dataItem.getData(); +- const curve = map[Keys.curve]; +- const privateKey = map[Keys.private]; +- const data = map[Keys.data]; +- if (!data) { +- throw new Error( +- `#[ur-registry][CryptoECKey][fn.fromDataItem]: decoded [dataItem][#data.data] is undefined: ${dataItem}`, +- ); +- } +- return new CryptoECKey({ data, curve, privateKey }); +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return CryptoECKey.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/CryptoHDKey.ts b/node_modules/@keystonehq/bc-ur-registry/src/CryptoHDKey.ts +deleted file mode 100644 +index bbfd331..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/CryptoHDKey.ts ++++ /dev/null +@@ -1,210 +0,0 @@ +-import { encode } from 'bs58check'; +-import { CryptoCoinInfo } from './CryptoCoinInfo'; +-import { CryptoKeypath } from './CryptoKeypath'; +-import { decodeToDataItem, DataItem } from './lib'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +- +-enum Keys { +- is_master = 1, +- is_private, +- key_data, +- chain_code, +- use_info, +- origin, +- children, +- parent_fingerprint, +- name, +- note, +-} +- +-type MasterKeyProps = { +- isMaster: true; +- key: Buffer; +- chainCode: Buffer; +-}; +- +-type DeriveKeyProps = { +- isMaster: false; +- isPrivateKey?: boolean; +- key: Buffer; +- chainCode?: Buffer; +- useInfo?: CryptoCoinInfo; +- origin?: CryptoKeypath; +- children?: CryptoKeypath; +- parentFingerprint?: Buffer; +- name?: string; +- note?: string; +-}; +-export class CryptoHDKey extends RegistryItem { +- private master: boolean; +- private privateKey: boolean; +- private key: Buffer; +- private chainCode: Buffer; +- private useInfo: CryptoCoinInfo; +- private origin: CryptoKeypath; +- private children: CryptoKeypath; +- private parentFingerprint: Buffer; +- private name: string; +- private note: string; +- +- public getKey = () => this.key; +- public getChainCode = () => this.chainCode; +- public isMaster = () => this.master; +- public isPrivateKey = () => !!this.privateKey; +- public getUseInfo = () => this.useInfo; +- public getOrigin = () => this.origin; +- public getChildren = () => this.children; +- public getParentFingerprint = () => this.parentFingerprint; +- public getName = () => this.name; +- public getNote = () => this.note; +- public getBip32Key = () => { +- let version: Buffer; +- let depth: number; +- let index: number; +- let parentFingerprint: Buffer = Buffer.alloc(4).fill(0); +- if(this.isMaster()) { +- // version bytes defined on https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#serialization-format +- version = Buffer.from("0488ADE4", "hex") +- depth = 0; +- index = 0; +- } else { +- depth = this.getOrigin().getComponents().length || this.getOrigin().getDepth(); +- const paths = this.getOrigin().getComponents(); +- const lastPath = paths[paths.length - 1]; +- if(lastPath) { +- index = lastPath.isHardened() ? lastPath.getIndex()! + 0x80000000 : lastPath.getIndex()!; +- parentFingerprint = this.getParentFingerprint(); +- } +- if(this.isPrivateKey()) { +- version = Buffer.from('0488ADE4', 'hex'); +- } else { +- version = Buffer.from('0488B21E', 'hex'); +- } +- } +- const depthBuffer = Buffer.alloc(1); +- depthBuffer.writeUInt8(depth, 0); +- const indexBuffer = Buffer.alloc(4); +- indexBuffer.writeUInt32BE(index, 0); +- const chainCode = this.getChainCode(); +- const key = this.getKey(); +- return encode(Buffer.concat([version, depthBuffer, parentFingerprint, indexBuffer, chainCode, key])); +- } +- +- public getRegistryType = () => { +- return RegistryTypes.CRYPTO_HDKEY; +- }; +- +- constructor(args: DeriveKeyProps | MasterKeyProps) { +- super(); +- if (args.isMaster) { +- this.setupMasterKey(args); +- } else { +- this.setupDeriveKey(args as DeriveKeyProps); +- } +- } +- +- private setupMasterKey = (args: MasterKeyProps) => { +- this.master = true; +- this.key = args.key; +- this.chainCode = args.chainCode; +- }; +- +- private setupDeriveKey = (args: DeriveKeyProps) => { +- this.master = false; +- this.privateKey = args.isPrivateKey; +- this.key = args.key; +- this.chainCode = args.chainCode; +- this.useInfo = args.useInfo; +- this.origin = args.origin; +- this.children = args.children; +- this.parentFingerprint = args.parentFingerprint; +- this.name = args.name; +- this.note = args.note; +- }; +- +- public toDataItem = () => { +- const map = {}; +- if (this.master) { +- map[Keys.is_master] = true; +- map[Keys.key_data] = this.key; +- map[Keys.chain_code] = this.chainCode; +- } else { +- if (this.privateKey !== undefined) { +- map[Keys.is_private] = this.privateKey; +- } +- map[Keys.key_data] = this.key; +- if (this.chainCode) { +- map[Keys.chain_code] = this.chainCode; +- } +- if (this.useInfo) { +- const useInfo = this.useInfo.toDataItem(); +- useInfo.setTag(this.useInfo.getRegistryType().getTag()); +- map[Keys.use_info] = useInfo; +- } +- if (this.origin) { +- const origin = this.origin.toDataItem(); +- origin.setTag(this.origin.getRegistryType().getTag()); +- map[Keys.origin] = origin; +- } +- if (this.children) { +- const children = this.children.toDataItem(); +- children.setTag(this.children.getRegistryType().getTag()); +- map[Keys.children] = children; +- } +- if (this.parentFingerprint) { +- map[Keys.parent_fingerprint] = this.parentFingerprint.readUInt32BE(0); +- } +- if (this.name !== undefined) { +- map[Keys.name] = this.name; +- } +- if (this.note !== undefined) { +- map[Keys.note] = this.note; +- } +- } +- return new DataItem(map); +- }; +- +- public static fromDataItem = (dataItem: DataItem) => { +- const map = dataItem.getData(); +- const isMaster = !!map[Keys.is_master]; +- const isPrivateKey = map[Keys.is_private]; +- const key = map[Keys.key_data]; +- const chainCode = map[Keys.chain_code]; +- const useInfo = map[Keys.use_info] +- ? CryptoCoinInfo.fromDataItem(map[Keys.use_info]) +- : undefined; +- const origin = map[Keys.origin] +- ? CryptoKeypath.fromDataItem(map[Keys.origin]) +- : undefined; +- const children = map[Keys.children] +- ? CryptoKeypath.fromDataItem(map[Keys.children]) +- : undefined; +- let _parentFingerprint = map[Keys.parent_fingerprint]; +- let parentFingerprint: Buffer; +- if (_parentFingerprint) { +- parentFingerprint = Buffer.alloc(4); +- parentFingerprint.writeUInt32BE(_parentFingerprint, 0); +- } +- const name = map[Keys.name]; +- const note = map[Keys.note]; +- +- return new CryptoHDKey({ +- isMaster, +- isPrivateKey, +- key, +- chainCode, +- useInfo, +- origin, +- children, +- parentFingerprint, +- name, +- note, +- }); +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return CryptoHDKey.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/CryptoKeypath.ts b/node_modules/@keystonehq/bc-ur-registry/src/CryptoKeypath.ts +deleted file mode 100644 +index 4babe91..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/CryptoKeypath.ts ++++ /dev/null +@@ -1,95 +0,0 @@ +-import { decodeToDataItem, DataItem } from './lib'; +-import { PathComponent } from './PathComponent'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +- +-enum Keys { +- components = 1, +- source_fingerprint, +- depth, +-} +- +-export class CryptoKeypath extends RegistryItem { +- getRegistryType = () => { +- return RegistryTypes.CRYPTO_KEYPATH; +- }; +- +- constructor( +- private components: PathComponent[] = [], +- private sourceFingerprint?: Buffer, +- private depth?: number, +- ) { +- super(); +- } +- +- public getPath = () => { +- if (this.components.length === 0) { +- return undefined; +- } +- +- const components = this.components.map((component) => { +- return `${component.isWildcard() ? '*' : component.getIndex()}${ +- component.isHardened() ? "'" : '' +- }`; +- }); +- return components.join('/'); +- }; +- +- public getComponents = () => this.components; +- public getSourceFingerprint = () => this.sourceFingerprint; +- public getDepth = () => this.depth; +- +- toDataItem = () => { +- const map: Record = {}; +- const components = []; +- this.components && +- this.components.forEach((component) => { +- if (component.isWildcard()) { +- components.push([]); +- } else { +- components.push(component.getIndex()); +- } +- components.push(component.isHardened() ? true : false); +- }); +- map[Keys.components] = components; +- if (this.sourceFingerprint) { +- map[Keys.source_fingerprint] = this.sourceFingerprint.readUInt32BE(0); +- } +- if (this.depth !== undefined) { +- map[Keys.depth] = this.depth; +- } +- return new DataItem(map); +- }; +- +- static fromDataItem = (dataItem: DataItem) => { +- const map: Record = dataItem.getData(); +- const pathComponents: PathComponent[] = []; +- const components = map[Keys.components] as any[]; +- if (components) { +- for (let i = 0; i < components.length; i += 2) { +- const isHardened = components[i + 1]; +- const path = components[i]; +- if (typeof path === 'number') { +- pathComponents.push( +- new PathComponent({ index: path, hardened: isHardened }), +- ); +- } else { +- pathComponents.push(new PathComponent({ hardened: isHardened })); +- } +- } +- } +- const _sourceFingerprint = map[Keys.source_fingerprint]; +- let sourceFingerprint: Buffer; +- if (_sourceFingerprint) { +- sourceFingerprint = Buffer.alloc(4); +- sourceFingerprint.writeUInt32BE(_sourceFingerprint, 0); +- } +- const depth = map[Keys.depth]; +- return new CryptoKeypath(pathComponents, sourceFingerprint, depth); +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return CryptoKeypath.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/CryptoOutput.ts b/node_modules/@keystonehq/bc-ur-registry/src/CryptoOutput.ts +deleted file mode 100644 +index cd3009c..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/CryptoOutput.ts ++++ /dev/null +@@ -1,114 +0,0 @@ +-import { CryptoECKey } from './CryptoECKey'; +-import { CryptoHDKey } from './CryptoHDKey'; +-import { decodeToDataItem, DataItem } from './lib'; +-import { MultiKey } from './MultiKey'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +-import { ScriptExpression, ScriptExpressions } from './ScriptExpression'; +- +-export class CryptoOutput extends RegistryItem { +- public getRegistryType = () => { +- return RegistryTypes.CRYPTO_OUTPUT; +- }; +- +- constructor( +- private scriptExpressions: ScriptExpression[], +- private cryptoKey: CryptoHDKey | CryptoECKey | MultiKey, +- ) { +- super(); +- } +- +- public getCryptoKey = () => this.cryptoKey; +- public getHDKey = () => { +- if (this.cryptoKey instanceof CryptoHDKey) { +- return this.cryptoKey as CryptoHDKey; +- } else { +- return undefined; +- } +- }; +- public getECKey = () => { +- if (this.cryptoKey instanceof CryptoECKey) { +- return this.cryptoKey as CryptoECKey; +- } else { +- return undefined; +- } +- }; +- +- public getMultiKey = () => { +- if (this.cryptoKey instanceof MultiKey) { +- return this.cryptoKey as MultiKey; +- } else { +- return undefined; +- } +- }; +- +- public getScriptExpressions = () => this.scriptExpressions; +- +- toDataItem = () => { +- let dataItem = this.cryptoKey.toDataItem(); +- if ( +- this.cryptoKey instanceof CryptoECKey || +- this.cryptoKey instanceof CryptoHDKey +- ) { +- dataItem.setTag(this.cryptoKey.getRegistryType().getTag()); +- } +- +- const clonedSe = [...this.scriptExpressions]; +- +- clonedSe.reverse().forEach((se) => { +- const tagValue = se.getTag(); +- if (dataItem.getTag() === undefined) { +- dataItem.setTag(tagValue); +- } else { +- dataItem = new DataItem(dataItem, tagValue); +- } +- }); +- +- return dataItem; +- }; +- +- public static fromDataItem = (dataItem: DataItem) => { +- const scriptExpressions: ScriptExpression[] = []; +- let _dataItem = dataItem; +- while (true) { +- let _tag = _dataItem.getTag() || undefined; +- const se = ScriptExpression.fromTag(_tag); +- if (se) { +- scriptExpressions.push(se); +- if (_dataItem.getData() instanceof DataItem) { +- _dataItem = _dataItem.getData(); +- _tag = _dataItem.getTag(); +- } else { +- break; +- } +- } else { +- break; +- } +- } +- const seLength = scriptExpressions.length; +- const isMultiKey = +- seLength > 0 && +- (scriptExpressions[seLength - 1].getExpression() === +- ScriptExpressions.MULTISIG.getExpression() || +- scriptExpressions[seLength - 1].getExpression() === +- ScriptExpressions.SORTED_MULTISIG.getExpression()); +- //TODO: judge is multi key by scriptExpressions +- if (isMultiKey) { +- const multiKey = MultiKey.fromDataItem(_dataItem); +- return new CryptoOutput(scriptExpressions, multiKey); +- } +- +- if (_dataItem.getTag() === RegistryTypes.CRYPTO_HDKEY.getTag()) { +- const cryptoHDKey = CryptoHDKey.fromDataItem(_dataItem); +- return new CryptoOutput(scriptExpressions, cryptoHDKey); +- } else { +- const cryptoECKey = CryptoECKey.fromDataItem(_dataItem); +- return new CryptoOutput(scriptExpressions, cryptoECKey); +- } +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return CryptoOutput.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/CryptoPSBT.ts b/node_modules/@keystonehq/bc-ur-registry/src/CryptoPSBT.ts +deleted file mode 100644 +index 626b647..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/CryptoPSBT.ts ++++ /dev/null +@@ -1,32 +0,0 @@ +-import { decodeToDataItem, DataItem } from './lib'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryTypes } from './RegistryType'; +- +-export class CryptoPSBT extends RegistryItem { +- getRegistryType = () => RegistryTypes.CRYPTO_PSBT; +- +- constructor(private psbt: Buffer) { +- super(); +- } +- +- public getPSBT = () => this.psbt; +- +- public toDataItem = () => { +- return new DataItem(this.psbt); +- }; +- +- public static fromDataItem = (dataItem: DataItem) => { +- const psbt = dataItem.getData(); +- if (!psbt) { +- throw new Error( +- `#[ur-registry][CryptoPSBT][fn.fromDataItem]: decoded [dataItem][#data] is undefined: ${dataItem}`, +- ); +- } +- return new CryptoPSBT(psbt); +- }; +- +- public static fromCBOR = (_cborPayload: Buffer) => { +- const dataItem = decodeToDataItem(_cborPayload); +- return CryptoPSBT.fromDataItem(dataItem); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/Decoder/index.ts b/node_modules/@keystonehq/bc-ur-registry/src/Decoder/index.ts +deleted file mode 100644 +index 0460694..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/Decoder/index.ts ++++ /dev/null +@@ -1,40 +0,0 @@ +-import { URDecoder } from '@ngraveio/bc-ur'; +-import { +- Bytes, +- CryptoAccount, +- CryptoCoinInfo, +- CryptoECKey, +- CryptoHDKey, +- CryptoKeypath, +- CryptoOutput, +- CryptoPSBT, +-} from '..'; +-import { RegistryTypes } from '../RegistryType'; +- +-export class URRegistryDecoder extends URDecoder { +- public resultRegistryType = () => { +- const ur = this.resultUR(); +- switch (ur.type) { +- case RegistryTypes.BYTES.getType(): +- return Bytes.fromCBOR(ur.cbor); +- case RegistryTypes.CRYPTO_HDKEY.getType(): +- return CryptoHDKey.fromCBOR(ur.cbor); +- case RegistryTypes.CRYPTO_KEYPATH.getType(): +- return CryptoKeypath.fromCBOR(ur.cbor); +- case RegistryTypes.CRYPTO_COIN_INFO.getType(): +- return CryptoCoinInfo.fromCBOR(ur.cbor); +- case RegistryTypes.CRYPTO_ECKEY.getType(): +- return CryptoECKey.fromCBOR(ur.cbor); +- case RegistryTypes.CRYPTO_OUTPUT.getType(): +- return CryptoOutput.fromCBOR(ur.cbor); +- case RegistryTypes.CRYPTO_PSBT.getType(): +- return CryptoPSBT.fromCBOR(ur.cbor); +- case RegistryTypes.CRYPTO_ACCOUNT.getType(): +- return CryptoAccount.fromCBOR(ur.cbor); +- default: +- throw new Error( +- `#[ur-registry][Decoder][fn.resultRegistryType]: registry type ${ur.type} is not supported now`, +- ); +- } +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/MultiKey.ts b/node_modules/@keystonehq/bc-ur-registry/src/MultiKey.ts +deleted file mode 100644 +index 0522fbd..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/MultiKey.ts ++++ /dev/null +@@ -1,54 +0,0 @@ +-import { CryptoECKey } from './CryptoECKey'; +-import { CryptoHDKey } from './CryptoHDKey'; +-import { DataItem } from './lib/DataItem'; +-import { RegistryItem } from './RegistryItem'; +-import { RegistryType, RegistryTypes } from './RegistryType'; +- +-enum Keys { +- threshold = 1, +- keys, +-} +- +-export class MultiKey extends RegistryItem { +- getRegistryType: () => RegistryType; +- +- constructor( +- private threshold: number, +- private ecKeys: CryptoECKey[], +- private hdKeys: CryptoHDKey[], +- ) { +- super(); +- } +- +- getThreshold = () => this.threshold; +- getEcKeys = () => this.ecKeys as CryptoECKey[]; +- getHdKeys = () => this.hdKeys as CryptoHDKey[]; +- +- toDataItem = () => { +- const map = {}; +- map[Keys.threshold] = this.threshold; +- const keys: DataItem[] = [...this.ecKeys, ...this.hdKeys].map((k) => { +- const dataItem = k.toDataItem(); +- dataItem.setTag(k.getRegistryType().getTag()); +- return dataItem; +- }); +- map[Keys.keys] = keys; +- return new DataItem(map); +- }; +- +- static fromDataItem = (dataItem: DataItem) => { +- const map = dataItem.getData(); +- const threshold = map[Keys.threshold]; +- const keys = map[Keys.keys] as DataItem[]; +- const ecKeys = []; +- const hdKeys = []; +- keys.forEach((k) => { +- if (k.getTag() === RegistryTypes.CRYPTO_HDKEY.getTag()) { +- hdKeys.push(CryptoHDKey.fromDataItem(k)); +- } else if (k.getTag() === RegistryTypes.CRYPTO_ECKEY.getTag()) { +- ecKeys.push(CryptoECKey.fromDataItem(k)); +- } +- }); +- return new MultiKey(threshold, ecKeys, hdKeys); +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/PathComponent.ts b/node_modules/@keystonehq/bc-ur-registry/src/PathComponent.ts +deleted file mode 100644 +index d41cb06..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/PathComponent.ts ++++ /dev/null +@@ -1,28 +0,0 @@ +-export class PathComponent { +- public static readonly HARDENED_BIT = 0x80000000; +- +- private index?: number; +- private wildcard: boolean; +- private hardened: boolean; +- +- constructor(args: { index?: number; hardened: boolean }) { +- this.index = args.index; +- this.hardened = args.hardened; +- +- if (this.index !== undefined) { +- this.wildcard = false; +- } else { +- this.wildcard = true; +- } +- +- if (this.index && (this.index & PathComponent.HARDENED_BIT) !== 0) { +- throw new Error( +- `#[ur-registry][PathComponent][fn.constructor]: Invalid index ${this.index} - most significant bit cannot be set`, +- ); +- } +- } +- +- public getIndex = () => this.index; +- public isWildcard = () => this.wildcard; +- public isHardened = () => this.hardened; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/RegistryItem.ts b/node_modules/@keystonehq/bc-ur-registry/src/RegistryItem.ts +deleted file mode 100644 +index 99139f7..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/RegistryItem.ts ++++ /dev/null +@@ -1,35 +0,0 @@ +-import { UR, UREncoder } from '@ngraveio/bc-ur'; +-import { encodeDataItem, DataItem } from './lib'; +-import { RegistryType } from './RegistryType'; +- +-export abstract class RegistryItem { +- abstract getRegistryType: () => RegistryType; +- abstract toDataItem: () => DataItem; +- public toCBOR = () => { +- if (this.toDataItem() === undefined) { +- throw new Error( +- `#[ur-registry][RegistryItem][fn.toCBOR]: registry ${this.getRegistryType()}'s method toDataItem returns undefined`, +- ); +- } +- return encodeDataItem(this.toDataItem()); +- }; +- +- public toUR = () => { +- return new UR(this.toCBOR(), this.getRegistryType().getType()); +- }; +- +- public toUREncoder = ( +- maxFragmentLength?: number, +- firstSeqNum?: number, +- minFragmentLength?: number, +- ) => { +- const ur = this.toUR(); +- const urEncoder = new UREncoder( +- ur, +- maxFragmentLength, +- firstSeqNum, +- minFragmentLength, +- ); +- return urEncoder; +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/RegistryType.ts b/node_modules/@keystonehq/bc-ur-registry/src/RegistryType.ts +deleted file mode 100644 +index 64637bc..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/RegistryType.ts ++++ /dev/null +@@ -1,20 +0,0 @@ +-// cbor registry types: https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-006-urtypes.md +-// Map +- +-export class RegistryType { +- constructor(private type: string, private tag?: number) {} +- getTag = () => this.tag; +- getType = () => this.type; +-} +- +-export const RegistryTypes = { +- UUID: new RegistryType('uuid', 37), +- BYTES: new RegistryType('bytes', undefined), +- CRYPTO_HDKEY: new RegistryType('crypto-hdkey', 303), +- CRYPTO_KEYPATH: new RegistryType('crypto-keypath', 304), +- CRYPTO_COIN_INFO: new RegistryType('crypto-coin-info', 305), +- CRYPTO_ECKEY: new RegistryType('crypto-eckey', 306), +- CRYPTO_OUTPUT: new RegistryType('crypto-output', 308), +- CRYPTO_PSBT: new RegistryType('crypto-psbt', 310), +- CRYPTO_ACCOUNT: new RegistryType('crypto-account', 311), +-}; +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/ScriptExpression.ts b/node_modules/@keystonehq/bc-ur-registry/src/ScriptExpression.ts +deleted file mode 100644 +index fdd3f05..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/ScriptExpression.ts ++++ /dev/null +@@ -1,26 +0,0 @@ +-export class ScriptExpression { +- constructor(private tag: number, private expression: string) {} +- +- public getTag = () => this.tag; +- public getExpression = () => this.expression; +- +- public static fromTag = (tag: number) => { +- const se = Object.values(ScriptExpressions).find( +- (se) => se.getTag() === tag, +- ); +- return se; +- }; +-} +- +-export const ScriptExpressions = { +- SCRIPT_HASH: new ScriptExpression(400, 'sh'), +- WITNESS_SCRIPT_HASH: new ScriptExpression(401, 'wsh'), +- PUBLIC_KEY: new ScriptExpression(402, 'pk'), +- PUBLIC_KEY_HASH: new ScriptExpression(403, 'pkh'), +- WITNESS_PUBLIC_KEY_HASH: new ScriptExpression(404, 'wpkh'), +- COMBO: new ScriptExpression(405, 'combo'), +- MULTISIG: new ScriptExpression(406, 'multi'), +- SORTED_MULTISIG: new ScriptExpression(407, 'sorted'), +- ADDRESS: new ScriptExpression(307, 'addr'), +- RAW_SCRIPT: new ScriptExpression(408, 'raw'), +-}; +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/index.ts b/node_modules/@keystonehq/bc-ur-registry/src/index.ts +deleted file mode 100644 +index 172a1e5..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/index.ts ++++ /dev/null +@@ -1,89 +0,0 @@ +-import './patchCBOR'; +- +-import { CryptoHDKey } from './CryptoHDKey'; +-import { CryptoKeypath } from './CryptoKeypath'; +-import { +- CryptoCoinInfo, +- Type as CryptoCoinInfoType, +- Network as CryptoCoinInfoNetwork, +-} from './CryptoCoinInfo'; +-import { CryptoECKey } from './CryptoECKey'; +-import { Bytes } from './Bytes'; +-import { CryptoOutput } from './CryptoOutput'; +-import { CryptoPSBT } from './CryptoPSBT'; +-import { CryptoAccount } from './CryptoAccount'; +-import { URRegistryDecoder } from './Decoder'; +- +-import { MultiKey } from './MultiKey'; +- +-import { ScriptExpressions } from './ScriptExpression'; +-import { PathComponent } from './PathComponent'; +- +-import { RegistryTypes, RegistryType } from './RegistryType'; +- +-import { +- addReader, +- addSemanticDecode, +- addSemanticEncode, +- addWriter, +- decodeToDataItem, +- encodeDataItem, +-} from './lib'; +- +-export { DataItem } from './lib'; +-export { RegistryItem } from './RegistryItem'; +- +-import { patchTags } from './utils'; +- +-const URlib = { +- URRegistryDecoder, +- Bytes, +- CryptoAccount, +- CryptoHDKey, +- CryptoKeypath, +- CryptoCoinInfo, +- CryptoCoinInfoType, +- CryptoCoinInfoNetwork, +- CryptoECKey, +- CryptoOutput, +- CryptoPSBT, +- MultiKey, +- ScriptExpressions, +- PathComponent, +-}; +- +-const cbor = { +- addReader, +- addSemanticDecode, +- addSemanticEncode, +- addWriter, +- patchTags, +-}; +- +-const extend = { +- RegistryTypes, +- RegistryType, +- decodeToDataItem, +- encodeDataItem, +- cbor, +-}; +- +-export { +- URRegistryDecoder, +- Bytes, +- CryptoAccount, +- CryptoHDKey, +- CryptoKeypath, +- CryptoCoinInfo, +- CryptoCoinInfoType, +- CryptoCoinInfoNetwork, +- CryptoECKey, +- CryptoOutput, +- CryptoPSBT, +- MultiKey, +- ScriptExpressions, +- PathComponent, +- extend, +-}; +- +-export default URlib; +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/lib/DataItem.ts b/node_modules/@keystonehq/bc-ur-registry/src/lib/DataItem.ts +deleted file mode 100644 +index 9727f7e..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/lib/DataItem.ts ++++ /dev/null +@@ -1,25 +0,0 @@ +-export class DataItem { +- private tag?: number; +- private data: any; +- +- constructor(data: any, tag?: number) { +- this.data = data; +- this.tag = tag; +- } +- +- public setTag = (tag?: number) => { +- this.tag = tag; +- }; +- +- public clearTag = () => { +- this.tag = undefined; +- }; +- +- public getTag = () => { +- return this.tag; +- }; +- +- public getData = () => { +- return this.data; +- }; +-} +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/lib/cbor-sync.js b/node_modules/@keystonehq/bc-ur-registry/src/lib/cbor-sync.js +deleted file mode 100644 +index 63e5b3a..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/lib/cbor-sync.js ++++ /dev/null +@@ -1,695 +0,0 @@ +-(function (global, factory) { +- if (typeof define === 'function' && define.amd) { +- define([], factory); +- } else if (typeof module !== 'undefined' && module.exports) { +- module.exports = factory(); +- } else { +- global.CBOR = factory(); +- } +-})(this, function () { +- const { DataItem } = require('./DataItem'); +- var CBOR = (function () { +- function BinaryHex(hex) { +- this.$hex = hex; +- } +- BinaryHex.prototype = { +- length: function () { +- return this.$hex.length / 2; +- }, +- toString: function (format) { +- if (!format || format === 'hex' || format === 16) return this.$hex; +- if (format === 'utf-8') { +- var encoded = ''; +- for (var i = 0; i < this.$hex.length; i += 2) { +- encoded += '%' + this.$hex.substring(i, i + 2); +- } +- return decodeURIComponent(encoded); +- } +- if (format === 'latin') { +- var encoded = []; +- for (var i = 0; i < this.$hex.length; i += 2) { +- encoded.push(parseInt(this.$hex.substring(i, i + 2), 16)); +- } +- return String.fromCharCode.apply(String, encoded); +- } +- throw new Error('Unrecognised format: ' + format); +- }, +- }; +- BinaryHex.fromLatinString = function (latinString) { +- var hex = ''; +- for (var i = 0; i < latinString.length; i++) { +- var pair = latinString.charCodeAt(i).toString(16); +- if (pair.length === 1) pair = '0' + pair; +- hex += pair; +- } +- return new BinaryHex(hex); +- }; +- BinaryHex.fromUtf8String = function (utf8String) { +- var encoded = encodeURIComponent(utf8String); +- var hex = ''; +- for (var i = 0; i < encoded.length; i++) { +- if (encoded.charAt(i) === '%') { +- hex += encoded.substring(i + 1, i + 3); +- i += 2; +- } else { +- var hexPair = encoded.charCodeAt(i).toString(16); +- if (hexPair.length < 2) hexPair = '0' + hexPair; +- hex += hexPair; +- } +- } +- return new BinaryHex(hex); +- }; +- +- var semanticEncoders = []; +- var semanticDecoders = {}; +- +- var notImplemented = function (label) { +- return function () { +- throw new Error(label + ' not implemented'); +- }; +- }; +- +- function Reader() {} +- Reader.prototype = { +- peekByte: notImplemented('peekByte'), +- readByte: notImplemented('readByte'), +- readChunk: notImplemented('readChunk'), +- readFloat16: function () { +- var half = this.readUint16(); +- var exponent = (half & 0x7fff) >> 10; +- var mantissa = half & 0x3ff; +- var negative = half & 0x8000; +- if (exponent === 0x1f) { +- if (mantissa === 0) { +- return negative ? -Infinity : Infinity; +- } +- return NaN; +- } +- var magnitude = exponent +- ? Math.pow(2, exponent - 25) * (1024 + mantissa) +- : Math.pow(2, -24) * mantissa; +- return negative ? -magnitude : magnitude; +- }, +- readFloat32: function () { +- var intValue = this.readUint32(); +- var exponent = (intValue & 0x7fffffff) >> 23; +- var mantissa = intValue & 0x7fffff; +- var negative = intValue & 0x80000000; +- if (exponent === 0xff) { +- if (mantissa === 0) { +- return negative ? -Infinity : Infinity; +- } +- return NaN; +- } +- var magnitude = exponent +- ? Math.pow(2, exponent - 23 - 127) * (8388608 + mantissa) +- : Math.pow(2, -23 - 126) * mantissa; +- return negative ? -magnitude : magnitude; +- }, +- readFloat64: function () { +- var int1 = this.readUint32(), +- int2 = this.readUint32(); +- var exponent = (int1 >> 20) & 0x7ff; +- var mantissa = (int1 & 0xfffff) * 4294967296 + int2; +- var negative = int1 & 0x80000000; +- if (exponent === 0x7ff) { +- if (mantissa === 0) { +- return negative ? -Infinity : Infinity; +- } +- return NaN; +- } +- var magnitude = exponent +- ? Math.pow(2, exponent - 52 - 1023) * (4503599627370496 + mantissa) +- : Math.pow(2, -52 - 1022) * mantissa; +- return negative ? -magnitude : magnitude; +- }, +- readUint16: function () { +- return this.readByte() * 256 + this.readByte(); +- }, +- readUint32: function () { +- return this.readUint16() * 65536 + this.readUint16(); +- }, +- readUint64: function () { +- return this.readUint32() * 4294967296 + this.readUint32(); +- }, +- }; +- function Writer() {} +- Writer.prototype = { +- writeByte: notImplemented('writeByte'), +- result: notImplemented('result'), +- writeFloat16: notImplemented('writeFloat16'), +- writeFloat32: notImplemented('writeFloat32'), +- writeFloat64: notImplemented('writeFloat64'), +- writeUint16: function (value) { +- this.writeByte((value >> 8) & 0xff); +- this.writeByte(value & 0xff); +- }, +- writeUint32: function (value) { +- this.writeUint16((value >> 16) & 0xffff); +- this.writeUint16(value & 0xffff); +- }, +- writeUint64: function (value) { +- if (value >= 9007199254740992 || value <= -9007199254740992) { +- throw new Error( +- 'Cannot encode Uint64 of: ' + +- value + +- ' magnitude to big (floating point errors)', +- ); +- } +- this.writeUint32(Math.floor(value / 4294967296)); +- this.writeUint32(value % 4294967296); +- }, +- writeString: notImplemented('writeString'), +- canWriteBinary: function (chunk) { +- return false; +- }, +- writeBinary: notImplemented('writeChunk'), +- }; +- +- function readHeaderRaw(reader) { +- var firstByte = reader.readByte(); +- var majorType = firstByte >> 5, +- value = firstByte & 0x1f; +- return { type: majorType, value: value }; +- } +- +- function valueFromHeader(header, reader) { +- var value = header.value; +- if (value < 24) { +- return value; +- } else if (value == 24) { +- return reader.readByte(); +- } else if (value == 25) { +- return reader.readUint16(); +- } else if (value == 26) { +- return reader.readUint32(); +- } else if (value == 27) { +- return reader.readUint64(); +- } else if (value == 31) { +- // special value for non-terminating arrays/objects +- return null; +- } +- notImplemented('Additional info: ' + value)(); +- } +- +- function writeHeaderRaw(type, value, writer) { +- writer.writeByte((type << 5) | value); +- } +- +- function writeHeader(type, value, writer) { +- var firstByte = type << 5; +- if (value < 24) { +- writer.writeByte(firstByte | value); +- } else if (value < 256) { +- writer.writeByte(firstByte | 24); +- writer.writeByte(value); +- } else if (value < 65536) { +- writer.writeByte(firstByte | 25); +- writer.writeUint16(value); +- } else if (value < 4294967296) { +- writer.writeByte(firstByte | 26); +- writer.writeUint32(value); +- } else { +- writer.writeByte(firstByte | 27); +- writer.writeUint64(value); +- } +- } +- +- var stopCode = new Error(); // Just a unique object, that won't compare strictly equal to anything else +- +- function decodeReader(reader) { +- var header = readHeaderRaw(reader); +- switch (header.type) { +- case 0: +- return valueFromHeader(header, reader); +- case 1: +- return -1 - valueFromHeader(header, reader); +- case 2: +- return reader.readChunk(valueFromHeader(header, reader)); +- case 3: +- var buffer = reader.readChunk(valueFromHeader(header, reader)); +- return buffer.toString('utf-8'); +- case 4: +- case 5: +- var arrayLength = valueFromHeader(header, reader); +- var result = []; +- if (arrayLength !== null) { +- if (header.type === 5) { +- arrayLength *= 2; +- } +- for (var i = 0; i < arrayLength; i++) { +- result[i] = decodeReader(reader); +- } +- } else { +- var item; +- while ((item = decodeReader(reader)) !== stopCode) { +- result.push(item); +- } +- } +- if (header.type === 5) { +- var objResult = {}; +- for (var i = 0; i < result.length; i += 2) { +- objResult[result[i]] = result[i + 1]; +- } +- return objResult; +- } else { +- return result; +- } +- case 6: +- var tag = valueFromHeader(header, reader); +- var decoder = semanticDecoders[tag]; +- var result = decodeReader(reader); +- return decoder ? decoder(result) : result; +- case 7: +- if (header.value === 25) { +- return reader.readFloat16(); +- } else if (header.value === 26) { +- return reader.readFloat32(); +- } else if (header.value === 27) { +- return reader.readFloat64(); +- } +- switch (valueFromHeader(header, reader)) { +- case 20: +- return false; +- case 21: +- return true; +- case 22: +- return null; +- case 23: +- return undefined; +- case null: +- return stopCode; +- default: +- throw new Error('Unknown fixed value: ' + header.value); +- } +- default: +- throw new Error('Unsupported header: ' + JSON.stringify(header)); +- } +- throw new Error('not implemented yet'); +- } +- +- function encodeWriter(data, writer) { +- for (var i = 0; i < semanticEncoders.length; i++) { +- var replacement = semanticEncoders[i].fn(data); +- if (replacement !== undefined) { +- writeHeader(6, semanticEncoders[i].tag, writer); +- return encodeWriter(replacement, writer); +- } +- } +- +- if (data && typeof data.toCBOR === 'function') { +- data = data.toCBOR(); +- } +- +- if (data === false) { +- writeHeader(7, 20, writer); +- } else if (data === true) { +- writeHeader(7, 21, writer); +- } else if (data === null) { +- writeHeader(7, 22, writer); +- } else if (data === undefined) { +- writeHeader(7, 23, writer); +- } else if (typeof data === 'number') { +- if ( +- Math.floor(data) === data && +- data < 9007199254740992 && +- data > -9007199254740992 +- ) { +- // Integer +- if (data < 0) { +- writeHeader(1, -1 - data, writer); +- } else { +- writeHeader(0, data, writer); +- } +- } else { +- writeHeaderRaw(7, 27, writer); +- writer.writeFloat64(data); +- } +- } else if (typeof data === 'string') { +- writer.writeString(data, function (length) { +- writeHeader(3, length, writer); +- }); +- } else if (writer.canWriteBinary(data)) { +- writer.writeBinary(data, function (length) { +- writeHeader(2, length, writer); +- }); +- } else if (typeof data === 'object') { +- if (api.config.useToJSON && typeof data.toJSON === 'function') { +- data = data.toJSON(); +- } +- if (Array.isArray(data)) { +- writeHeader(4, data.length, writer); +- for (var i = 0; i < data.length; i++) { +- encodeWriter(data[i], writer); +- } +- } else { +- var keys = Object.keys(data); +- writeHeader(5, keys.length, writer); +- for (var i = 0; i < keys.length; i++) { +- const number = parseInt(keys[i]); +- if (isNaN(number)) { +- encodeWriter(keys[i], writer); +- encodeWriter(data[keys[i]], writer); +- } else { +- encodeWriter(number, writer); +- encodeWriter(data[keys[i]], writer); +- } +- } +- } +- } else { +- throw new Error('CBOR encoding not supported: ' + data); +- } +- } +- +- var readerFunctions = []; +- var writerFunctions = []; +- +- var api = { +- config: { +- useToJSON: true, +- }, +- addWriter: function (format, writerFunction) { +- if (typeof format === 'string') { +- writerFunctions.push(function (f) { +- if (format === f) return writerFunction(f); +- }); +- } else { +- writerFunctions.push(format); +- } +- }, +- addReader: function (format, readerFunction) { +- if (typeof format === 'string') { +- readerFunctions.push(function (data, f) { +- if (format === f) return readerFunction(data, f); +- }); +- } else { +- readerFunctions.push(format); +- } +- }, +- encode: function (data, format) { +- for (var i = 0; i < writerFunctions.length; i++) { +- var func = writerFunctions[i]; +- var writer = func(format); +- if (writer) { +- encodeWriter(data, writer); +- return writer.result(); +- } +- } +- throw new Error('Unsupported output format: ' + format); +- }, +- // DataItem: {getData: () => any} +- encodeDataItem: function (data, format) { +- for (var i = 0; i < writerFunctions.length; i++) { +- var func = writerFunctions[i]; +- var writer = func(format); +- if (writer) { +- if (data.getTag() !== undefined) { +- encodeWriter(data, writer); +- return writer.result(); +- } else { +- encodeWriter(data.getData(), writer); +- return writer.result(); +- } +- } +- } +- throw new Error('Unsupported output format: ' + format); +- }, +- decode: function (data, format) { +- for (var i = 0; i < readerFunctions.length; i++) { +- var func = readerFunctions[i]; +- var reader = func(data, format); +- if (reader) { +- return decodeReader(reader); +- } +- } +- throw new Error('Unsupported input format: ' + format); +- }, +- decodeToDataItem: function (data, format) { +- for (var i = 0; i < readerFunctions.length; i++) { +- var func = readerFunctions[i]; +- var reader = func(data, format); +- if (reader) { +- const result = decodeReader(reader); +- if (result instanceof DataItem) { +- return result; +- } else { +- return new DataItem(result); +- } +- } +- } +- throw new Error('Unsupported input format: ' + format); +- }, +- addSemanticEncode: function (tag, fn) { +- if (typeof tag !== 'number' || tag % 1 !== 0 || tag < 0) { +- throw new Error('Tag must be a positive integer'); +- } +- semanticEncoders.push({ tag: tag, fn: fn }); +- return this; +- }, +- addSemanticDecode: function (tag, fn) { +- if (typeof tag !== 'number' || tag % 1 !== 0 || tag < 0) { +- throw new Error('Tag must be a positive integer'); +- } +- semanticDecoders[tag] = fn; +- return this; +- }, +- Reader: Reader, +- Writer: Writer, +- }; +- +- /** Node.js Buffers **/ +- function BufferReader(buffer) { +- this.buffer = buffer; +- this.pos = 0; +- } +- BufferReader.prototype = Object.create(Reader.prototype); +- BufferReader.prototype.peekByte = function () { +- return this.buffer[this.pos]; +- }; +- BufferReader.prototype.readByte = function () { +- return this.buffer[this.pos++]; +- }; +- BufferReader.prototype.readUint16 = function () { +- var result = this.buffer.readUInt16BE(this.pos); +- this.pos += 2; +- return result; +- }; +- BufferReader.prototype.readUint32 = function () { +- var result = this.buffer.readUInt32BE(this.pos); +- this.pos += 4; +- return result; +- }; +- BufferReader.prototype.readFloat32 = function () { +- var result = this.buffer.readFloatBE(this.pos); +- this.pos += 4; +- return result; +- }; +- BufferReader.prototype.readFloat64 = function () { +- var result = this.buffer.readDoubleBE(this.pos); +- this.pos += 8; +- return result; +- }; +- BufferReader.prototype.readChunk = function (length) { +- var result = Buffer.alloc(length); +- this.buffer.copy(result, 0, this.pos, (this.pos += length)); +- return result; +- }; +- +- function BufferWriter(stringFormat) { +- this.byteLength = 0; +- this.defaultBufferLength = 16384; // 16k +- this.latestBuffer = Buffer.alloc(this.defaultBufferLength); +- this.latestBufferOffset = 0; +- this.completeBuffers = []; +- this.stringFormat = stringFormat; +- } +- BufferWriter.prototype = Object.create(Writer.prototype); +- BufferWriter.prototype.writeByte = function (value) { +- this.latestBuffer[this.latestBufferOffset++] = value; +- if (this.latestBufferOffset >= this.latestBuffer.length) { +- this.completeBuffers.push(this.latestBuffer); +- this.latestBuffer = Buffer.alloc(this.defaultBufferLength); +- this.latestBufferOffset = 0; +- } +- this.byteLength++; +- }; +- BufferWriter.prototype.writeFloat32 = function (value) { +- var buffer = Buffer.alloc(4); +- buffer.writeFloatBE(value, 0); +- this.writeBuffer(buffer); +- }; +- BufferWriter.prototype.writeFloat64 = function (value) { +- var buffer = Buffer.alloc(8); +- buffer.writeDoubleBE(value, 0); +- this.writeBuffer(buffer); +- }; +- BufferWriter.prototype.writeString = function (string, lengthFunc) { +- var buffer = Buffer.from(string, 'utf-8'); +- lengthFunc(buffer.length); +- this.writeBuffer(buffer); +- }; +- BufferWriter.prototype.canWriteBinary = function (data) { +- return data instanceof Buffer; +- }; +- BufferWriter.prototype.writeBinary = function (buffer, lengthFunc) { +- lengthFunc(buffer.length); +- this.writeBuffer(buffer); +- }; +- BufferWriter.prototype.writeBuffer = function (chunk) { +- if (!(chunk instanceof Buffer)) +- throw new TypeError('BufferWriter only accepts Buffers'); +- if (!this.latestBufferOffset) { +- this.completeBuffers.push(chunk); +- } else if ( +- this.latestBuffer.length - this.latestBufferOffset >= +- chunk.length +- ) { +- chunk.copy(this.latestBuffer, this.latestBufferOffset); +- this.latestBufferOffset += chunk.length; +- if (this.latestBufferOffset >= this.latestBuffer.length) { +- this.completeBuffers.push(this.latestBuffer); +- this.latestBuffer = Buffer.alloc(this.defaultBufferLength); +- this.latestBufferOffset = 0; +- } +- } else { +- this.completeBuffers.push( +- this.latestBuffer.slice(0, this.latestBufferOffset), +- ); +- this.completeBuffers.push(chunk); +- this.latestBuffer = Buffer.alloc(this.defaultBufferLength); +- this.latestBufferOffset = 0; +- } +- this.byteLength += chunk.length; +- }; +- BufferWriter.prototype.result = function () { +- // Copies them all into a single Buffer +- var result = Buffer.alloc(this.byteLength); +- var offset = 0; +- for (var i = 0; i < this.completeBuffers.length; i++) { +- var buffer = this.completeBuffers[i]; +- buffer.copy(result, offset, 0, buffer.length); +- offset += buffer.length; +- } +- if (this.latestBufferOffset) { +- this.latestBuffer.copy(result, offset, 0, this.latestBufferOffset); +- } +- +- if (this.stringFormat) return result.toString(this.stringFormat); +- return result; +- }; +- +- if (typeof Buffer === 'function') { +- api.addReader(function (data, format) { +- if (data instanceof Buffer) { +- return new BufferReader(data); +- } +- if (format === 'hex' || format === 'base64') { +- var buffer = Buffer.from(data, format); +- return new BufferReader(buffer); +- } +- }); +- api.addWriter(function (format) { +- if (!format || format === 'buffer') { +- return new BufferWriter(); +- } else if (format === 'hex' || format === 'base64') { +- return new BufferWriter(format); +- } +- }); +- } +- +- /** Hex-encoding (and Latin1) for browser **/ +- function HexReader(hex) { +- this.hex = hex; +- this.pos = 0; +- } +- HexReader.prototype = Object.create(Reader.prototype); +- HexReader.prototype.peekByte = function () { +- var pair = this.hex.substring(this.pos, 2); +- return parseInt(pair, 16); +- }; +- HexReader.prototype.readByte = function () { +- var pair = this.hex.substring(this.pos, this.pos + 2); +- this.pos += 2; +- return parseInt(pair, 16); +- }; +- HexReader.prototype.readChunk = function (length) { +- var hex = this.hex.substring(this.pos, this.pos + length * 2); +- this.pos += length * 2; +- if (typeof Buffer === 'function') return Buffer.from(hex, 'hex'); +- return new BinaryHex(hex); +- }; +- +- function HexWriter(finalFormat) { +- this.$hex = ''; +- this.finalFormat = finalFormat || 'hex'; +- } +- HexWriter.prototype = Object.create(Writer.prototype); +- HexWriter.prototype.writeByte = function (value) { +- if (value < 0 || value > 255) +- throw new Error('Byte value out of range: ' + value); +- var hex = value.toString(16); +- if (hex.length == 1) { +- hex = '0' + hex; +- } +- this.$hex += hex; +- }; +- HexWriter.prototype.canWriteBinary = function (chunk) { +- return ( +- chunk instanceof BinaryHex || +- (typeof Buffer === 'function' && chunk instanceof Buffer) +- ); +- }; +- HexWriter.prototype.writeBinary = function (chunk, lengthFunction) { +- if (chunk instanceof BinaryHex) { +- lengthFunction(chunk.length()); +- this.$hex += chunk.$hex; +- } else if (typeof Buffer === 'function' && chunk instanceof Buffer) { +- lengthFunction(chunk.length); +- this.$hex += chunk.toString('hex'); +- } else { +- throw new TypeError('HexWriter only accepts BinaryHex or Buffers'); +- } +- }; +- HexWriter.prototype.result = function () { +- if (this.finalFormat === 'buffer' && typeof Buffer === 'function') { +- return Buffer.from(this.$hex, 'hex'); +- } +- return new BinaryHex(this.$hex).toString(this.finalFormat); +- }; +- HexWriter.prototype.writeString = function (string, lengthFunction) { +- var buffer = BinaryHex.fromUtf8String(string); +- lengthFunction(buffer.length()); +- this.$hex += buffer.$hex; +- }; +- +- api.addReader(function (data, format) { +- if (data instanceof BinaryHex || data.$hex) { +- return new HexReader(data.$hex); +- } +- if (format === 'hex') { +- return new HexReader(data); +- } +- }); +- api.addWriter(function (format) { +- if (format === 'hex') { +- return new HexWriter(); +- } +- }); +- +- return api; +- })(); +- +- CBOR.addSemanticEncode(0, function (data) { +- if (data instanceof Date) { +- return data.toISOString(); +- } +- }) +- .addSemanticDecode(0, function (isoString) { +- return new Date(isoString); +- }) +- .addSemanticDecode(1, function (isoString) { +- return new Date(isoString); +- }); +- +- return CBOR; +-}); +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/lib/index.ts b/node_modules/@keystonehq/bc-ur-registry/src/lib/index.ts +deleted file mode 100644 +index deb0156..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/lib/index.ts ++++ /dev/null +@@ -1,9 +0,0 @@ +-export { +- encodeDataItem, +- decodeToDataItem, +- addSemanticDecode, +- addSemanticEncode, +- addReader, +- addWriter, +-} from './cbor-sync'; +-export { DataItem } from './DataItem'; +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/patchCBOR.ts b/node_modules/@keystonehq/bc-ur-registry/src/patchCBOR.ts +deleted file mode 100644 +index b9909a7..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/patchCBOR.ts ++++ /dev/null +@@ -1,11 +0,0 @@ +-import { patchTags } from './utils'; +-import { RegistryTypes } from './RegistryType'; +-import { ScriptExpressions } from './ScriptExpression'; +- +-const registryTags = Object.values(RegistryTypes) +- .filter((r) => !!r.getTag()) +- .map((r) => r.getTag()); +-const scriptExpressionTags = Object.values(ScriptExpressions).map((se) => +- se.getTag(), +-); +-patchTags(registryTags.concat(scriptExpressionTags)); +diff --git a/node_modules/@keystonehq/bc-ur-registry/src/utils.ts b/node_modules/@keystonehq/bc-ur-registry/src/utils.ts +deleted file mode 100644 +index ee39b78..0000000 +--- a/node_modules/@keystonehq/bc-ur-registry/src/utils.ts ++++ /dev/null +@@ -1,19 +0,0 @@ +-import { addSemanticDecode, addSemanticEncode, DataItem } from './lib'; +- +-const alreadyPatchedTag = []; +-export const patchTags = (tags: number[]) => { +- tags.forEach((tag) => { +- if (alreadyPatchedTag.find((i) => i === tag)) return; +- addSemanticEncode(tag, (data: any) => { +- if (data instanceof DataItem) { +- if (data.getTag() === tag) { +- return data.getData(); +- } +- } +- }); +- addSemanticDecode(tag, (data: any) => { +- return new DataItem(data, tag); +- }); +- alreadyPatchedTag.push(tag); +- }); +-}; diff --git a/patches/await-semaphore+0.1.3.patch b/patches/await-semaphore+0.1.3.patch new file mode 100644 index 000000000..f130d85de --- /dev/null +++ b/patches/await-semaphore+0.1.3.patch @@ -0,0 +1,68 @@ +diff --git a/node_modules/await-semaphore/index.ts b/node_modules/await-semaphore/index.ts +deleted file mode 100644 +index 69ce92a..0000000 +--- a/node_modules/await-semaphore/index.ts ++++ /dev/null +@@ -1,62 +0,0 @@ +-export class Semaphore { +- private tasks: (() => void)[] = []; +- count: number; +- +- constructor(count: number) { +- this.count = count; +- } +- +- private sched() { +- if (this.count > 0 && this.tasks.length > 0) { +- this.count--; +- let next = this.tasks.shift(); +- if (next === undefined) { +- throw "Unexpected undefined value in tasks list"; +- } +- +- next(); +- } +- } +- +- public acquire() { +- return new Promise<() => void>((res, rej) => { +- var task = () => { +- var released = false; +- res(() => { +- if (!released) { +- released = true; +- this.count++; +- this.sched(); +- } +- }); +- }; +- this.tasks.push(task); +- if (process && process.nextTick) { +- process.nextTick(this.sched.bind(this)); +- } else { +- setImmediate(this.sched.bind(this)); +- } +- }); +- } +- +- public use(f: () => Promise) { +- return this.acquire() +- .then(release => { +- return f() +- .then((res) => { +- release(); +- return res; +- }) +- .catch((err) => { +- release(); +- throw err; +- }); +- }); +- } +-} +- +-export class Mutex extends Semaphore { +- constructor() { +- super(1); +- } +-} diff --git a/patches/eslint-import-resolver-typescript+2.5.0.patch b/patches/eslint-import-resolver-typescript+2.5.0.patch new file mode 100644 index 000000000..163ff663e --- /dev/null +++ b/patches/eslint-import-resolver-typescript+2.5.0.patch @@ -0,0 +1,29 @@ +diff --git a/node_modules/eslint-import-resolver-typescript/lib/cjs.js b/node_modules/eslint-import-resolver-typescript/lib/cjs.js +index 5aeddb5..1fe0cbf 100644 +--- a/node_modules/eslint-import-resolver-typescript/lib/cjs.js ++++ b/node_modules/eslint-import-resolver-typescript/lib/cjs.js +@@ -49,13 +49,19 @@ function __spreadArray(to, from) { + + var IMPORTER_NAME = 'eslint-import-resolver-typescript'; + var log = debug__default['default'](IMPORTER_NAME); +-var defaultExtensions = __spreadArray(__spreadArray([ ++/** ++ * In the original version of this package, `Object.keys(require.extensions)` ++ * gets added to `defaultExtensions`. `require.extensions` resolves to undefined ++ * when this file is run through LavaMoat, and ESLint emits a warning that ++ * `require.extensions` is deprecated anyway. So we hardcode this list here. ++ */ ++var defaultExtensions = [ + '.ts', + '.tsx', +- '.d.ts' +-], Object.keys(require.extensions)), [ +- '.jsx', +-]); ++ '.d.ts', ++ '.js', ++ '.jsx' ++]; + var interfaceVersion = 2; + /** + * @param {string} source the module to resolve; i.e './some-module' diff --git a/patches/typescript+4.4.4.patch b/patches/typescript+4.4.4.patch new file mode 100644 index 000000000..e86cadadb --- /dev/null +++ b/patches/typescript+4.4.4.patch @@ -0,0 +1,250 @@ +diff --git a/node_modules/typescript/lib/typescript.js b/node_modules/typescript/lib/typescript.js +index 323de6f..367063a 100644 +--- a/node_modules/typescript/lib/typescript.js ++++ b/node_modules/typescript/lib/typescript.js +@@ -24,11 +24,58 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { + return to.concat(ar || Array.prototype.slice.call(from)); + }; + var __assign = (this && this.__assign) || function () { +- __assign = Object.assign || function(t) { ++ __assign = function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; +- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) +- t[p] = s[p]; ++ for (var p in s) { ++ if (Object.prototype.hasOwnProperty.call(s, p)) { ++ /** ++ * In the original version of this package, this was: ++ * ++ * t[p] = s[p] ++ * ++ * Unfortunately LavaMoat trips up on this, so we have to change ++ * it. ++ * ++ * Internally LavaMoat uses `lockdown` (part of SES, which is ++ * part of Endo) to freeze modifications to "intrinsics" — core ++ * things like `Object.prototype`, `Function.prototype`, etc. ++ * This will cause code which is responsible for said ++ * modifications to fail at runtime, because it makes the ++ * properties of these intrinsics non-writable. ++ * ++ * The reason we have to change *this* code is that later on, ++ * this `__assign` function is used to merge two objects, and ++ * one of those objects contains a `constructor` property. As we ++ * know, `constructor` is a special property, as it's a property ++ * on `Object.prototype` that stores the constructor used to ++ * create that object. But when used in this context, there is ++ * nothing inherently special about it – it's just a property on ++ * an object we're setting. Unfortunately, that's not how it's ++ * being treated. Because `lockdown` freezes `Object.prototype`, ++ * `Object.prototype.constructor` is non-writable, and due to a ++ * "mistake" in the ES5 spec [1], that means `constructor` on ++ * *any* object is non-writable too. So an error is thrown when ++ * this code is executed. ++ * ++ * There is a way to get around this, which is to configure ++ * `lockdown` with the option `overrideTaming: 'severe'`. ++ * The mechanics of this option, as well as more information ++ * about the "mistake" this option solves, are explained here ++ * [2]. Unfortunately, we cannot enable this option because ++ * LavaMoat is the one running `lockdown` here [3]. So to work ++ * around this, we use `Object.defineProperty` to define the ++ * property we want. As this does not use property assignment ++ * (`object[key] = value`) but rather defines the property more ++ * directly, this bypasses the "override mistake". ++ * ++ * [1]: https://web.archive.org/web/20141230041441/http://wiki.ecmascript.org/doku.php?id=strawman:fixing_override_mistake ++ * [2]: https://github.com/endojs/endo/blob/864f086f87e1e7ef78a401a7550ff0aeb664bba0/packages/ses/src/enable-property-overrides.js#L28 ++ * [3]: https://github.com/LavaMoat/LavaMoat/blob/7c15bf8ba34ba1a9ceb3ffe591b1b2bfb084bead/packages/core/src/kernelTemplate.js#L32-L43 ++ */ ++ Object.defineProperty(t, p, Object.getOwnPropertyDescriptor(s, p)) ++ } ++ } + } + return t; + }; +@@ -9820,87 +9867,94 @@ var ts; + } + ts.tokenIsIdentifierOrKeywordOrGreaterThan = tokenIsIdentifierOrKeywordOrGreaterThan; + /** @internal */ +- ts.textToKeywordObj = (_a = { +- abstract: 126 /* AbstractKeyword */, +- any: 129 /* AnyKeyword */, +- as: 127 /* AsKeyword */, +- asserts: 128 /* AssertsKeyword */, +- bigint: 156 /* BigIntKeyword */, +- boolean: 132 /* BooleanKeyword */, +- break: 81 /* BreakKeyword */, +- case: 82 /* CaseKeyword */, +- catch: 83 /* CatchKeyword */, +- class: 84 /* ClassKeyword */, +- continue: 86 /* ContinueKeyword */, +- const: 85 /* ConstKeyword */ +- }, +- _a["" + "constructor"] = 133 /* ConstructorKeyword */, +- _a.debugger = 87 /* DebuggerKeyword */, +- _a.declare = 134 /* DeclareKeyword */, +- _a.default = 88 /* DefaultKeyword */, +- _a.delete = 89 /* DeleteKeyword */, +- _a.do = 90 /* DoKeyword */, +- _a.else = 91 /* ElseKeyword */, +- _a.enum = 92 /* EnumKeyword */, +- _a.export = 93 /* ExportKeyword */, +- _a.extends = 94 /* ExtendsKeyword */, +- _a.false = 95 /* FalseKeyword */, +- _a.finally = 96 /* FinallyKeyword */, +- _a.for = 97 /* ForKeyword */, +- _a.from = 154 /* FromKeyword */, +- _a.function = 98 /* FunctionKeyword */, +- _a.get = 135 /* GetKeyword */, +- _a.if = 99 /* IfKeyword */, +- _a.implements = 117 /* ImplementsKeyword */, +- _a.import = 100 /* ImportKeyword */, +- _a.in = 101 /* InKeyword */, +- _a.infer = 136 /* InferKeyword */, +- _a.instanceof = 102 /* InstanceOfKeyword */, +- _a.interface = 118 /* InterfaceKeyword */, +- _a.intrinsic = 137 /* IntrinsicKeyword */, +- _a.is = 138 /* IsKeyword */, +- _a.keyof = 139 /* KeyOfKeyword */, +- _a.let = 119 /* LetKeyword */, +- _a.module = 140 /* ModuleKeyword */, +- _a.namespace = 141 /* NamespaceKeyword */, +- _a.never = 142 /* NeverKeyword */, +- _a.new = 103 /* NewKeyword */, +- _a.null = 104 /* NullKeyword */, +- _a.number = 145 /* NumberKeyword */, +- _a.object = 146 /* ObjectKeyword */, +- _a.package = 120 /* PackageKeyword */, +- _a.private = 121 /* PrivateKeyword */, +- _a.protected = 122 /* ProtectedKeyword */, +- _a.public = 123 /* PublicKeyword */, +- _a.override = 157 /* OverrideKeyword */, +- _a.readonly = 143 /* ReadonlyKeyword */, +- _a.require = 144 /* RequireKeyword */, +- _a.global = 155 /* GlobalKeyword */, +- _a.return = 105 /* ReturnKeyword */, +- _a.set = 147 /* SetKeyword */, +- _a.static = 124 /* StaticKeyword */, +- _a.string = 148 /* StringKeyword */, +- _a.super = 106 /* SuperKeyword */, +- _a.switch = 107 /* SwitchKeyword */, +- _a.symbol = 149 /* SymbolKeyword */, +- _a.this = 108 /* ThisKeyword */, +- _a.throw = 109 /* ThrowKeyword */, +- _a.true = 110 /* TrueKeyword */, +- _a.try = 111 /* TryKeyword */, +- _a.type = 150 /* TypeKeyword */, +- _a.typeof = 112 /* TypeOfKeyword */, +- _a.undefined = 151 /* UndefinedKeyword */, +- _a.unique = 152 /* UniqueKeyword */, +- _a.unknown = 153 /* UnknownKeyword */, +- _a.var = 113 /* VarKeyword */, +- _a.void = 114 /* VoidKeyword */, +- _a.while = 115 /* WhileKeyword */, +- _a.with = 116 /* WithKeyword */, +- _a.yield = 125 /* YieldKeyword */, +- _a.async = 130 /* AsyncKeyword */, +- _a.await = 131 /* AwaitKeyword */, +- _a.of = 158 /* OfKeyword */, +- _a); ++ /** ++ * In the original version of this package, this object was built by ++ * initializing one object and then adding more properties to that object. ++ * This ends up throwing an error when this code is executed due to ++ * the same issue as explained at the top of this file: essentially, ++ * the `constructor` property of any object cannot be set due to the ++ * "override mistake". The fix for this is to just build one big object. ++ */ ++ ts.textToKeywordObj = { ++ abstract: 126 /* AbstractKeyword */, ++ any: 129 /* AnyKeyword */, ++ as: 127 /* AsKeyword */, ++ asserts: 128 /* AssertsKeyword */, ++ bigint: 156 /* BigIntKeyword */, ++ boolean: 132 /* BooleanKeyword */, ++ break: 81 /* BreakKeyword */, ++ case: 82 /* CaseKeyword */, ++ catch: 83 /* CatchKeyword */, ++ class: 84 /* ClassKeyword */, ++ continue: 86 /* ContinueKeyword */, ++ const: 85 /* ConstKeyword */, ++ ["constructor"]: 133 /* ConstructorKeyword */, ++ debugger: 87 /* DebuggerKeyword */, ++ declare: 134 /* DeclareKeyword */, ++ default: 88 /* DefaultKeyword */, ++ delete: 89 /* DeleteKeyword */, ++ do: 90 /* DoKeyword */, ++ else: 91 /* ElseKeyword */, ++ enum: 92 /* EnumKeyword */, ++ export: 93 /* ExportKeyword */, ++ extends: 94 /* ExtendsKeyword */, ++ false: 95 /* FalseKeyword */, ++ finally: 96 /* FinallyKeyword */, ++ for: 97 /* ForKeyword */, ++ from: 154 /* FromKeyword */, ++ function: 98 /* FunctionKeyword */, ++ get: 135 /* GetKeyword */, ++ if: 99 /* IfKeyword */, ++ implements: 117 /* ImplementsKeyword */, ++ import: 100 /* ImportKeyword */, ++ in: 101 /* InKeyword */, ++ infer: 136 /* InferKeyword */, ++ instanceof: 102 /* InstanceOfKeyword */, ++ interface: 118 /* InterfaceKeyword */, ++ intrinsic: 137 /* IntrinsicKeyword */, ++ is: 138 /* IsKeyword */, ++ keyof: 139 /* KeyOfKeyword */, ++ let: 119 /* LetKeyword */, ++ module: 140 /* ModuleKeyword */, ++ namespace: 141 /* NamespaceKeyword */, ++ never: 142 /* NeverKeyword */, ++ new: 103 /* NewKeyword */, ++ null: 104 /* NullKeyword */, ++ number: 145 /* NumberKeyword */, ++ object: 146 /* ObjectKeyword */, ++ package: 120 /* PackageKeyword */, ++ private: 121 /* PrivateKeyword */, ++ protected: 122 /* ProtectedKeyword */, ++ public: 123 /* PublicKeyword */, ++ override: 157 /* OverrideKeyword */, ++ readonly: 143 /* ReadonlyKeyword */, ++ require: 144 /* RequireKeyword */, ++ global: 155 /* GlobalKeyword */, ++ return: 105 /* ReturnKeyword */, ++ set: 147 /* SetKeyword */, ++ static: 124 /* StaticKeyword */, ++ string: 148 /* StringKeyword */, ++ super: 106 /* SuperKeyword */, ++ switch: 107 /* SwitchKeyword */, ++ symbol: 149 /* SymbolKeyword */, ++ this: 108 /* ThisKeyword */, ++ throw: 109 /* ThrowKeyword */, ++ true: 110 /* TrueKeyword */, ++ try: 111 /* TryKeyword */, ++ type: 150 /* TypeKeyword */, ++ typeof: 112 /* TypeOfKeyword */, ++ undefined: 151 /* UndefinedKeyword */, ++ unique: 152 /* UniqueKeyword */, ++ unknown: 153 /* UnknownKeyword */, ++ var: 113 /* VarKeyword */, ++ void: 114 /* VoidKeyword */, ++ while: 115 /* WhileKeyword */, ++ with: 116 /* WithKeyword */, ++ yield: 125 /* YieldKeyword */, ++ async: 130 /* AsyncKeyword */, ++ await: 131 /* AwaitKeyword */, ++ of: 158 /* OfKeyword */ ++ }; + var textToKeyword = new ts.Map(ts.getEntries(ts.textToKeywordObj)); + var textToToken = new ts.Map(ts.getEntries(__assign(__assign({}, ts.textToKeywordObj), { "{": 18 /* OpenBraceToken */, "}": 19 /* CloseBraceToken */, "(": 20 /* OpenParenToken */, ")": 21 /* CloseParenToken */, "[": 22 /* OpenBracketToken */, "]": 23 /* CloseBracketToken */, ".": 24 /* DotToken */, "...": 25 /* DotDotDotToken */, ";": 26 /* SemicolonToken */, ",": 27 /* CommaToken */, "<": 29 /* LessThanToken */, ">": 31 /* GreaterThanToken */, "<=": 32 /* LessThanEqualsToken */, ">=": 33 /* GreaterThanEqualsToken */, "==": 34 /* EqualsEqualsToken */, "!=": 35 /* ExclamationEqualsToken */, "===": 36 /* EqualsEqualsEqualsToken */, "!==": 37 /* ExclamationEqualsEqualsToken */, "=>": 38 /* EqualsGreaterThanToken */, "+": 39 /* PlusToken */, "-": 40 /* MinusToken */, "**": 42 /* AsteriskAsteriskToken */, "*": 41 /* AsteriskToken */, "/": 43 /* SlashToken */, "%": 44 /* PercentToken */, "++": 45 /* PlusPlusToken */, "--": 46 /* MinusMinusToken */, "<<": 47 /* LessThanLessThanToken */, ">": 48 /* GreaterThanGreaterThanToken */, ">>>": 49 /* GreaterThanGreaterThanGreaterThanToken */, "&": 50 /* AmpersandToken */, "|": 51 /* BarToken */, "^": 52 /* CaretToken */, "!": 53 /* ExclamationToken */, "~": 54 /* TildeToken */, "&&": 55 /* AmpersandAmpersandToken */, "||": 56 /* BarBarToken */, "?": 57 /* QuestionToken */, "??": 60 /* QuestionQuestionToken */, "?.": 28 /* QuestionDotToken */, ":": 58 /* ColonToken */, "=": 63 /* EqualsToken */, "+=": 64 /* PlusEqualsToken */, "-=": 65 /* MinusEqualsToken */, "*=": 66 /* AsteriskEqualsToken */, "**=": 67 /* AsteriskAsteriskEqualsToken */, "/=": 68 /* SlashEqualsToken */, "%=": 69 /* PercentEqualsToken */, "<<=": 70 /* LessThanLessThanEqualsToken */, ">>=": 71 /* GreaterThanGreaterThanEqualsToken */, ">>>=": 72 /* GreaterThanGreaterThanGreaterThanEqualsToken */, "&=": 73 /* AmpersandEqualsToken */, "|=": 74 /* BarEqualsToken */, "^=": 78 /* CaretEqualsToken */, "||=": 75 /* BarBarEqualsToken */, "&&=": 76 /* AmpersandAmpersandEqualsToken */, "??=": 77 /* QuestionQuestionEqualsToken */, "@": 59 /* AtToken */, "#": 62 /* HashToken */, "`": 61 /* BacktickToken */ }))); + /* +@@ -159858,6 +159912,7 @@ var ts; + delete Object.prototype.__magic__; + } + catch (error) { ++ throw error; + // In IE8, Object.defineProperty only works on DOM objects. + // If we hit this code path, assume `window` exists. + //@ts-ignore diff --git a/shared/constants/metametrics.js b/shared/constants/metametrics.js index 7f5076ffd..6639fb250 100644 --- a/shared/constants/metametrics.js +++ b/shared/constants/metametrics.js @@ -154,6 +154,30 @@ * the page view */ +/** + * @typedef {Object} Traits + * @property {string} [LEDGER_CONNECTION_TYPE] - when ledger live connnection + * type is changed we identify the ledger_connection_type trait + * @property {string} [NETWORKS_ADDED] - when user modifies networks we + * identify the networks_added trait + * @property {string} [NUMBER_OF_ACCOUNTS] - when identities change, we + * identify the new number_of_accounts trait + * @property {string} [THREE_BOX_ENABLED] - when 3box feature is toggled we + * identify the 3box_enabled trait + */ + +/** + * + * @type {Traits} + */ + +export const TRAITS = { + LEDGER_CONNECTION_TYPE: 'ledger_connection_type', + THREE_BOX_ENABLED: 'three_box_enabled', + NUMBER_OF_ACCOUNTS: 'number_of_accounts', + NETWORKS_ADDED: 'networks_added', +}; + // Mixpanel converts the zero address value to a truly anonymous event, which // speeds up reporting export const METAMETRICS_ANONYMOUS_ID = '0x0000000000000000'; diff --git a/shared/constants/network.js b/shared/constants/network.js index 5cf95da90..844533a0c 100644 --- a/shared/constants/network.js +++ b/shared/constants/network.js @@ -185,11 +185,16 @@ export const IPFS_DEFAULT_GATEWAY_URL = 'dweb.link'; // The first item in transakCurrencies must be the // default crypto currency for the network const BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME = 'ethereum'; + export const BUYABLE_CHAINS_MAP = { [MAINNET_CHAIN_ID]: { nativeCurrency: ETH_SYMBOL, network: BUYABLE_CHAIN_ETHEREUM_NETWORK_NAME, transakCurrencies: [ETH_SYMBOL, 'USDT', 'USDC', 'DAI'], + moonPay: { + defaultCurrencyCode: 'eth', + showOnlyCurrencies: 'eth,usdt,usdc,dai', + }, }, [ROPSTEN_CHAIN_ID]: { nativeCurrency: ETH_SYMBOL, @@ -211,16 +216,28 @@ export const BUYABLE_CHAINS_MAP = { nativeCurrency: BNB_SYMBOL, network: 'bsc', transakCurrencies: [BNB_SYMBOL, 'BUSD'], + moonPay: { + defaultCurrencyCode: 'bnb_bsc', + showOnlyCurrencies: 'bnb_bsc,busd_bsc', + }, }, [POLYGON_CHAIN_ID]: { nativeCurrency: MATIC_SYMBOL, network: 'polygon', transakCurrencies: [MATIC_SYMBOL, 'USDT', 'USDC', 'DAI'], + moonPay: { + defaultCurrencyCode: 'matic_polygon', + showOnlyCurrencies: 'matic_polygon,usdc_polygon', + }, }, [AVALANCHE_CHAIN_ID]: { nativeCurrency: AVALANCHE_SYMBOL, network: 'avaxcchain', transakCurrencies: [AVALANCHE_SYMBOL], + moonPay: { + defaultCurrencyCode: 'avax_cchain', + showOnlyCurrencies: 'avax_cchain', + }, }, [FANTOM_CHAIN_ID]: { nativeCurrency: FANTOM_SYMBOL, @@ -231,5 +248,9 @@ export const BUYABLE_CHAINS_MAP = { nativeCurrency: CELO_SYMBOL, network: 'celo', transakCurrencies: [CELO_SYMBOL], + moonPay: { + defaultCurrencyCode: 'celo', + showOnlyCurrencies: 'celo', + }, }, }; diff --git a/shared/constants/permissions.js b/shared/constants/permissions.js index 982f20e7b..c1e4e69e7 100644 --- a/shared/constants/permissions.js +++ b/shared/constants/permissions.js @@ -6,6 +6,7 @@ export const RestrictedMethods = Object.freeze({ eth_accounts: 'eth_accounts', ///: BEGIN:ONLY_INCLUDE_IN(flask) snap_confirm: 'snap_confirm', + snap_notify: 'snap_notify', snap_manageState: 'snap_manageState', 'snap_getBip44Entropy_*': 'snap_getBip44Entropy_*', 'wallet_snap_*': 'wallet_snap_*', @@ -23,5 +24,5 @@ export const EndowmentPermissions = Object.freeze({ }); // Methods / permissions in external packages that we are temporarily excluding. -export const ExcludedSnapPermissions = new Set(['snap_notify']); +export const ExcludedSnapPermissions = new Set([]); ///: END:ONLY_INCLUDE_IN diff --git a/shared/constants/swaps.js b/shared/constants/swaps.js index 2b035bd12..c9b183f7a 100644 --- a/shared/constants/swaps.js +++ b/shared/constants/swaps.js @@ -101,7 +101,7 @@ export const WAVAX_CONTRACT_ADDRESS = const SWAPS_TESTNET_CHAIN_ID = '0x539'; -export const SWAPS_API_V2_BASE_URL = 'https://api2.metaswap.codefi.network'; +export const SWAPS_API_V2_BASE_URL = 'https://swap.metaswap.codefi.network'; export const SWAPS_DEV_API_V2_BASE_URL = 'https://swap.metaswap-dev.codefi.network'; export const GAS_API_BASE_URL = 'https://gas-api.metaswap.codefi.network'; diff --git a/shared/constants/transaction.js b/shared/constants/transaction.js index 8ac9defc1..1e461f491 100644 --- a/shared/constants/transaction.js +++ b/shared/constants/transaction.js @@ -48,6 +48,7 @@ export const TRANSACTION_TYPES = { RETRY: 'retry', TOKEN_METHOD_TRANSFER: 'transfer', TOKEN_METHOD_TRANSFER_FROM: 'transferfrom', + TOKEN_METHOD_SAFE_TRANSFER_FROM: 'safetransferfrom', TOKEN_METHOD_APPROVE: 'approve', INCOMING: 'incoming', SIMPLE_SEND: 'simpleSend', @@ -305,3 +306,18 @@ export const TRANSACTION_EVENTS = { REJECTED: 'Transaction Rejected', SUBMITTED: 'Transaction Submitted', }; + +/** + * The types of assets that a user can send + * 1. NATIVE - The native asset for the current network, such as ETH + * 2. TOKEN - An ERC20 token. + * 3. COLLECTIBLE - An ERC721 or ERC1155 token. + * 4. UNKNOWN - A transaction interacting with a contract that isn't a token + * method interaction will be marked as dealing with an unknown asset type. + */ +export const ASSET_TYPES = { + NATIVE: 'NATIVE', + TOKEN: 'TOKEN', + COLLECTIBLE: 'COLLECTIBLE', + UNKNOWN: 'UNKNOWN', +}; diff --git a/shared/modules/transaction.utils.js b/shared/modules/transaction.utils.js index 8995b6c02..08adb46e3 100644 --- a/shared/modules/transaction.utils.js +++ b/shared/modules/transaction.utils.js @@ -1,8 +1,9 @@ import { isHexString } from 'ethereumjs-util'; import { ethers } from 'ethers'; -import abi from 'human-standard-token-abi'; +import { abiERC721, abiERC20, abiERC1155 } from '@metamask/metamask-eth-abis'; import log from 'loglevel'; -import { TRANSACTION_TYPES } from '../constants/transaction'; +import { TOKEN_STANDARDS } from '../../ui/helpers/constants/common'; +import { ASSET_TYPES, TRANSACTION_TYPES } from '../constants/transaction'; import { readAddressAsContract } from './contract-utils'; import { isEqualCaseInsensitive } from './string-utils'; @@ -18,7 +19,22 @@ import { isEqualCaseInsensitive } from './string-utils'; * code */ -const hstInterface = new ethers.utils.Interface(abi); +/** + * @typedef EthersContractCall + * @type object + * @property {any[]} args - The args/params to the function call. + * An array-like object with numerical and string indices. + * @property {string} name - The name of the function. + * @property {string} signature - The function signature. + * @property {string} sighash - The function signature hash. + * @property {EthersBigNumber} value - The ETH value associated with the call. + * @property {FunctionFragment} functionFragment - The Ethers function fragment + * representation of the function. + */ + +const erc20Interface = new ethers.utils.Interface(abiERC20); +const erc721Interface = new ethers.utils.Interface(abiERC721); +const erc1155Interface = new ethers.utils.Interface(abiERC1155); export function transactionMatchesNetwork(transaction, chainId, networkId) { if (typeof transaction.chainId !== 'undefined') { @@ -82,6 +98,36 @@ export function txParamsAreDappSuggested(transaction) { ); } +/** + * Attempts to decode transaction data using ABIs for three different token standards: ERC20, ERC721, ERC1155. + * The data will decode correctly if the transaction is an interaction with a contract that matches one of these + * contract standards + * + * @param data - encoded transaction data + * @returns {EthersContractCall | undefined} + */ +export function parseStandardTokenTransactionData(data) { + try { + return erc20Interface.parseTransaction({ data }); + } catch { + // ignore and next try to parse with erc721 ABI + } + + try { + return erc721Interface.parseTransaction({ data }); + } catch { + // ignore and next try to parse with erc1155 ABI + } + + try { + return erc1155Interface.parseTransaction({ data }); + } catch { + // ignore and return undefined + } + + return undefined; +} + /** * Determines the type of the transaction by analyzing the txParams. * This method will return one of the types defined in shared/constants/transactions @@ -97,7 +143,7 @@ export async function determineTransactionType(txParams, query) { const { data, to } = txParams; let name; try { - name = data && hstInterface.parseTransaction({ data }).name; + ({ name } = data && parseStandardTokenTransactionData(data)); } catch (error) { log.debug('Failed to parse transaction data.', error, data); } @@ -106,6 +152,7 @@ export async function determineTransactionType(txParams, query) { TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM, ].find((methodName) => isEqualCaseInsensitive(methodName, name)); let result; @@ -131,3 +178,86 @@ export async function determineTransactionType(txParams, query) { return { type: result, getCodeResponse: contractCode }; } + +const INFERRABLE_TRANSACTION_TYPES = [ + TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, + TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, + TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + TRANSACTION_TYPES.CONTRACT_INTERACTION, + TRANSACTION_TYPES.SIMPLE_SEND, +]; + +/** + * Given a transaction meta object, determine the asset type that the + * transaction is dealing with, as well as the standard for the token if it + * is a token transaction. + * + * @param {import('../constants/transaction').TransactionMeta} txMeta - + * transaction meta object + * @param {EthQuery} query - EthQuery instance + * @param {Function} getTokenStandardAndDetails - function to get token + * standards and details. + * @returns {{ assetType: string, tokenStandard: string}} + */ +export async function determineTransactionAssetType( + txMeta, + query, + getTokenStandardAndDetails, +) { + // If the transaction type is already one of the inferrable types, then we do + // not need to re-establish the type. + let inferrableType = txMeta.type; + if (INFERRABLE_TRANSACTION_TYPES.includes(txMeta.type) === false) { + // Because we will deal with all types of transactions (including swaps) + // we want to get an inferrable type of transaction that isn't special cased + // that way we can narrow the number of logic gates required. + const result = await determineTransactionType(txMeta.txParams, query); + inferrableType = result.type; + } + + // If the inferred type of the transaction is one of those that are part of + // the token contract standards, we can use the getTokenStandardAndDetails + // method to get the asset type. + const isTokenMethod = [ + TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, + TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, + TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + ].find((methodName) => methodName === inferrableType); + + if ( + isTokenMethod || + // We can also check any contract interaction type to see if the to address + // is a token contract. If it isn't, then the method will throw and we can + // fall through to the other checks. + inferrableType === TRANSACTION_TYPES.CONTRACT_INTERACTION + ) { + try { + // We don't need a balance check, so the second parameter to + // getTokenStandardAndDetails is omitted. + const details = await getTokenStandardAndDetails(txMeta.txParams.to); + if (details.standard) { + return { + assetType: + details.standard === TOKEN_STANDARDS.ERC20 + ? ASSET_TYPES.TOKEN + : ASSET_TYPES.COLLECTIBLE, + tokenStandard: details.standard, + }; + } + } catch { + // noop, We expect errors here but we don't need to report them or do + // anything in response. + } + } + + // If the transaction is interacting with a contract but isn't a token method + // we use the 'UNKNOWN' value to show that it isn't a transaction sending any + // particular asset. + if (inferrableType === TRANSACTION_TYPES.CONTRACT_INTERACTION) { + return { + assetType: ASSET_TYPES.UNKNOWN, + tokenStandard: TOKEN_STANDARDS.NONE, + }; + } + return { assetType: ASSET_TYPES.NATIVE, tokenStandard: TOKEN_STANDARDS.NONE }; +} diff --git a/shared/modules/transaction.utils.test.js b/shared/modules/transaction.utils.test.js index f6d816160..fba8c7827 100644 --- a/shared/modules/transaction.utils.test.js +++ b/shared/modules/transaction.utils.test.js @@ -5,9 +5,28 @@ import { determineTransactionType, isEIP1559Transaction, isLegacyTransaction, + parseStandardTokenTransactionData, } from './transaction.utils'; describe('Transaction.utils', function () { + describe('parseStandardTokenTransactionData', () => { + it('should return token data', () => { + const tokenData = parseStandardTokenTransactionData( + '0xa9059cbb00000000000000000000000050a9d56c2b8ba9a5c7f2c08c3d26e0499f23a7060000000000000000000000000000000000000000000000000000000000004e20', + ); + expect(tokenData).toStrictEqual(expect.anything()); + const { name, args } = tokenData; + expect(name).toStrictEqual(TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER); + const to = args._to; + const value = args._value.toString(); + expect(to).toStrictEqual('0x50A9D56C2B8BA9A5c7f2C08C3d26E0499F23a706'); + expect(value).toStrictEqual('20000'); + }); + + it('should not throw errors when called without arguments', () => { + expect(() => parseStandardTokenTransactionData()).not.toThrow(); + }); + }); describe('isEIP1559Transaction', function () { it('should return true if both maxFeePerGas and maxPriorityFeePerGas are hex strings', () => { expect( diff --git a/shared/notifications/index.js b/shared/notifications/index.js index 38ae68c0a..52e0662a4 100644 --- a/shared/notifications/index.js +++ b/shared/notifications/index.js @@ -46,6 +46,18 @@ export const UI_NOTIFICATIONS = { width: '80%', }, }, + 10: { + id: 10, + date: '2022-04-18', + image: { + src: 'images/token-detection.svg', + width: '100%', + }, + }, + 11: { + id: 11, + date: '2022-04-18', + }, }; export const getTranslatedUINoficiations = (t, locale) => { @@ -132,5 +144,26 @@ export const getTranslatedUINoficiations = (t, locale) => { new Date(UI_NOTIFICATIONS[9].date), ), }, + 10: { + ...UI_NOTIFICATIONS[10], + title: t('notifications10Title'), + description: [ + t('notifications10DescriptionOne'), + t('notifications10DescriptionTwo'), + t('notifications10DescriptionThree'), + ], + actionText: t('notifications10ActionText'), + date: new Intl.DateTimeFormat(formattedLocale).format( + new Date(UI_NOTIFICATIONS[10].date), + ), + }, + 11: { + ...UI_NOTIFICATIONS[11], + title: t('notifications11Title'), + description: t('notifications11Description'), + date: new Intl.DateTimeFormat(formattedLocale).format( + new Date(UI_NOTIFICATIONS[11].date), + ), + }, }; }; diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index 523cafdb0..a6f7b3c7b 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -13,7 +13,7 @@ const tinyDelayMs = 200; const regularDelayMs = tinyDelayMs * 2; const largeDelayMs = regularDelayMs * 2; const veryLargeDelayMs = largeDelayMs * 2; -const dappPort = 8080; +const dappBasePort = 8080; const convertToHexValue = (val) => `0x${new BigNumber(val, 10).toString(16)}`; @@ -23,6 +23,7 @@ async function withFixtures(options, testSuite) { fixtures, ganacheOptions, driverOptions, + dappOptions, title, failOnConsoleError = true, dappPath = undefined, @@ -35,7 +36,8 @@ async function withFixtures(options, testSuite) { const https = await mockttp.generateCACertificate(); const mockServer = mockttp.getLocal({ https, cors: true }); let secondaryGanacheServer; - let dappServer; + let numberOfDapps = dapp ? 1 : 0; + const dappServer = []; let webDriver; let failed = false; @@ -54,26 +56,31 @@ async function withFixtures(options, testSuite) { await fixtureServer.start(); await fixtureServer.loadState(path.join(__dirname, 'fixtures', fixtures)); if (dapp) { - let dappDirectory; - if (dappPath) { - dappDirectory = path.resolve(__dirname, dappPath); - } else { - dappDirectory = path.resolve( - __dirname, - '..', - '..', - 'node_modules', - '@metamask', - 'test-dapp', - 'dist', - ); + if (dappOptions?.numberOfDapps) { + numberOfDapps = dappOptions.numberOfDapps; + } + for (let i = 0; i < numberOfDapps; i++) { + let dappDirectory; + if (dappPath) { + dappDirectory = path.resolve(__dirname, dappPath); + } else { + dappDirectory = path.resolve( + __dirname, + '..', + '..', + 'node_modules', + '@metamask', + 'test-dapp', + 'dist', + ); + } + dappServer.push(createStaticServer(dappDirectory)); + dappServer[i].listen(`${dappBasePort + i}`); + await new Promise((resolve, reject) => { + dappServer[i].on('listening', resolve); + dappServer[i].on('error', reject); + }); } - dappServer = createStaticServer(dappDirectory); - dappServer.listen(dappPort); - await new Promise((resolve, reject) => { - dappServer.on('listening', resolve); - dappServer.on('error', reject); - }); } await setupMocking(mockServer, testSpecificMock); await mockServer.start(8000); @@ -125,15 +132,19 @@ async function withFixtures(options, testSuite) { if (webDriver) { await webDriver.quit(); } - if (dappServer && dappServer.listening) { - await new Promise((resolve, reject) => { - dappServer.close((error) => { - if (error) { - return reject(error); - } - return resolve(); - }); - }); + if (dapp) { + for (let i = 0; i < numberOfDapps; i++) { + if (dappServer[i] && dappServer[i].listening) { + await new Promise((resolve, reject) => { + dappServer[i].close((error) => { + if (error) { + return reject(error); + } + return resolve(); + }); + }); + } + } } await mockServer.stop(); } @@ -164,7 +175,7 @@ const getWindowHandles = async (driver, handlesCount) => { }; const connectDappWithExtensionPopup = async (driver) => { - await driver.openNewPage(`http://127.0.0.1:${dappPort}/`); + await driver.openNewPage(`http://127.0.0.1:${dappBasePort}/`); await driver.delay(regularDelayMs); await driver.clickElement({ text: 'Connect', tag: 'button' }); await driver.delay(regularDelayMs); diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js index f80d737f8..1733aa6f9 100644 --- a/test/e2e/metamask-ui.spec.js +++ b/test/e2e/metamask-ui.spec.js @@ -278,6 +278,7 @@ describe('MetaMask', function () { await gasPriceInput.fill('20'); await driver.delay(veryLargeDelayMs); await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(veryLargeDelayMs); await driver.clickElement({ text: 'Confirm', tag: 'button' }); await driver.delay(regularDelayMs); @@ -393,6 +394,7 @@ describe('MetaMask', function () { await gasPriceInput.fill('100'); await driver.delay(veryLargeDelayMs); await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(veryLargeDelayMs); }); it('submits the transaction', async function () { @@ -469,6 +471,7 @@ describe('MetaMask', function () { await gasPriceInput.fill('10'); await driver.delay(veryLargeDelayMs); await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(veryLargeDelayMs); await driver.findElement({ tag: 'span', text: '0.0006' }); }); @@ -595,6 +598,7 @@ describe('MetaMask', function () { await driver.delay(veryLargeDelayMs); await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(veryLargeDelayMs); const gasFeeInEth = await driver.findElement( '.confirm-approve-content__transaction-details-content__secondary-fee', @@ -619,6 +623,7 @@ describe('MetaMask', function () { await driver.delay(regularDelayMs); await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(veryLargeDelayMs); // wait for permission modal to be removed from DOM. await permissionModal.waitForElementState('hidden'); diff --git a/test/e2e/tests/add-account.spec.js b/test/e2e/tests/add-account.spec.js index 2557e5b3f..f4950199b 100644 --- a/test/e2e/tests/add-account.spec.js +++ b/test/e2e/tests/add-account.spec.js @@ -135,7 +135,6 @@ describe('Add account', function () { '[data-testid="import-srp__srp-word-0"]', testSeedPhrase, ); - await driver.delay(regularDelayMs); await driver.fill('#password', 'correct horse battery staple'); await driver.fill('#confirm-password', 'correct horse battery staple'); diff --git a/test/e2e/tests/contract-interactions.spec.js b/test/e2e/tests/contract-interactions.spec.js index 1e4ddecfa..e8edd9107 100644 --- a/test/e2e/tests/contract-interactions.spec.js +++ b/test/e2e/tests/contract-interactions.spec.js @@ -6,10 +6,6 @@ const { } = require('../helpers'); describe('Deploy contract and call contract methods', function () { - let windowHandles; - let extension; - let popup; - let dapp; const ganacheOptions = { accounts: [ { @@ -37,13 +33,13 @@ describe('Deploy contract and call contract methods', function () { await driver.clickElement({ text: 'Connect', tag: 'button' }); await driver.waitUntilXWindowHandles(3); await driver.delay(5000); - windowHandles = await driver.getAllWindowHandles(); - extension = windowHandles[0]; - dapp = await driver.switchToWindowWithTitle( + let windowHandles = await driver.getAllWindowHandles(); + const extension = windowHandles[0]; + const dapp = await driver.switchToWindowWithTitle( 'E2E Test Dapp', windowHandles, ); - popup = windowHandles.find( + const popup = windowHandles.find( (handle) => handle !== extension && handle !== dapp, ); await driver.switchToWindow(popup); diff --git a/test/e2e/tests/dapp-interactions.spec.js b/test/e2e/tests/dapp-interactions.spec.js new file mode 100644 index 000000000..af9f0af89 --- /dev/null +++ b/test/e2e/tests/dapp-interactions.spec.js @@ -0,0 +1,126 @@ +const { strict: assert } = require('assert'); +const { + convertToHexValue, + withFixtures, + connectDappWithExtensionPopup, +} = require('../helpers'); + +describe('Dapp interactions', function () { + let windowHandles; + let extension; + let popup; + const ganacheOptions = { + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + it('should trigger the add chain confirmation despite MetaMask being locked', async function () { + await withFixtures( + { + dapp: true, + fixtures: 'imported-account', + ganacheOptions, + title: this.test.title, + }, + async ({ driver }) => { + await driver.navigate(); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + + // Connect to Dapp0 + await connectDappWithExtensionPopup(driver, 0); + windowHandles = await driver.getAllWindowHandles(); + extension = windowHandles[0]; + + // Lock Account + await driver.switchToWindow(extension); + await driver.clickElement('.account-menu__icon'); + await driver.clickElement({ text: 'Lock', tag: 'button' }); + + // Trigger Notification + await driver.switchToWindowWithTitle('E2E Test Dapp', windowHandles); + await driver.clickElement('#addEthereumChain'); + await driver.switchToWindowWithTitle( + 'MetaMask Notification', + windowHandles, + ); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + const notification = await driver.isElementPresent({ + text: 'Allow this site to add a network?', + tag: 'h3', + }); + + assert.ok(notification, 'Dapp action does not appear in Metamask'); + }, + ); + }); + + it('should connect a second Dapp despite Metamask being locked', async function () { + await withFixtures( + { + dapp: true, + fixtures: 'imported-account', + ganacheOptions, + dappOptions: { numberOfDapps: 2 }, + title: this.test.title, + }, + async ({ driver }) => { + await driver.navigate(); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + + // Connect to Dapp0 + await connectDappWithExtensionPopup(driver, 0); + windowHandles = await driver.getAllWindowHandles(); + extension = windowHandles[0]; + + // Lock Account + await driver.switchToWindow(extension); + await driver.clickElement('.account-menu__icon'); + await driver.clickElement({ text: 'Lock', tag: 'button' }); + + // Connect to Dapp1 + await driver.openNewPage('http://127.0.0.1:8081/'); + await driver.clickElement({ text: 'Connect', tag: 'button' }); + + windowHandles = await driver.getAllWindowHandles(); + + popup = await driver.switchToWindowWithTitle( + 'MetaMask Notification', + windowHandles, + ); + + await driver.switchToWindow(popup); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + await driver.clickElement({ text: 'Next', tag: 'button' }); + await driver.clickElement({ text: 'Connect', tag: 'button' }); + + // Assert Connection + await driver.switchToWindow(extension); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + await driver.clickElement( + '[data-testid ="account-options-menu-button"]', + ); + await driver.clickElement({ text: 'Connected sites', tag: 'span' }); + const connectedDapp1 = await driver.isElementPresent({ + text: 'http://127.0.0.1:8080', + tag: 'span', + }); + const connectedDapp2 = await driver.isElementPresent({ + text: 'http://127.0.0.1:8081', + tag: 'span', + }); + + assert.ok(connectedDapp1, 'Account not connected to Dapp1'); + assert.ok(connectedDapp2, 'Account not connected to Dapp2'); + }, + ); + }); +}); diff --git a/test/e2e/tests/edit-gas-fee.spec.js b/test/e2e/tests/edit-gas-fee.spec.js index 6b79fcbd8..44d21889b 100644 --- a/test/e2e/tests/edit-gas-fee.spec.js +++ b/test/e2e/tests/edit-gas-fee.spec.js @@ -158,6 +158,7 @@ describe('Editing Confirm Transaction', function () { // Submit gas fee changes await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(largeDelayMs); // has correct updated value on the confirm screen the transaction const editedTransactionAmounts = await driver.findElements( diff --git a/test/e2e/tests/failing-contract.spec.js b/test/e2e/tests/failing-contract.spec.js new file mode 100644 index 000000000..f95fba9e4 --- /dev/null +++ b/test/e2e/tests/failing-contract.spec.js @@ -0,0 +1,107 @@ +const { strict: assert } = require('assert'); +const { convertToHexValue, withFixtures } = require('../helpers'); + +describe('Failing contract interaction ', function () { + const ganacheOptions = { + hardfork: 'london', + accounts: [ + { + secretKey: + '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC', + balance: convertToHexValue(25000000000000000000), + }, + ], + }; + it('should display a warning when the contract interaction is expected to fail', async function () { + await withFixtures( + { + dapp: true, + fixtures: 'imported-account', + ganacheOptions, + title: this.test.title, + }, + async ({ driver }) => { + await driver.navigate(); + await driver.fill('#password', 'correct horse battery staple'); + await driver.press('#password', driver.Key.ENTER); + + // connects the dapp + await driver.openNewPage('http://127.0.0.1:8080/'); + await driver.clickElement({ text: 'Connect', tag: 'button' }); + await driver.waitUntilXWindowHandles(3); + let windowHandles = await driver.getAllWindowHandles(); + const extension = windowHandles[0]; + const dapp = await driver.switchToWindowWithTitle( + 'E2E Test Dapp', + windowHandles, + ); + const popup = windowHandles.find( + (handle) => handle !== extension && handle !== dapp, + ); + await driver.switchToWindow(popup); + await driver.clickElement({ text: 'Next', tag: 'button' }); + await driver.clickElement({ text: 'Connect', tag: 'button' }); + await driver.waitUntilXWindowHandles(2); + + // deploy contract + await driver.switchToWindow(dapp); + await driver.clickElement('#deployFailingButton'); + await driver.waitUntilXWindowHandles(3); + windowHandles = await driver.getAllWindowHandles(); + await driver.switchToWindowWithTitle( + 'MetaMask Notification', + windowHandles, + ); + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindow(extension); + await driver.clickElement('[data-testid="home__activity-tab"]'); + await driver.waitForSelector( + '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(1)', + { timeout: 10000 }, + ); + const completedTx = await driver.findElement('.list-item__title'); + const completedTxText = await completedTx.getText(); + assert.equal(completedTxText, 'Contract Deployment'); + + // calls failing contract method + await driver.switchToWindow(dapp); + await driver.clickElement('#sendFailingButton'); + await driver.waitUntilXWindowHandles(3); + windowHandles = await driver.getAllWindowHandles(); + await driver.switchToWindowWithTitle( + 'MetaMask Notification', + windowHandles, + ); + + // display warning when transaction is expected to fail + const warningText = + 'This transaction is expected to fail. Trying to execute it is expected to be expensive but fail, and is not recommended.'; + const warning = await driver.findElement( + '.actionable-message__message', + ); + const confirmButton = await driver.findElement( + '[data-testid="page-container-footer-next"]', + ); + assert.equal(await warning.getText(), warningText); + assert.equal(await confirmButton.isEnabled(), false); + + // dismiss warning and confirm the transaction + await driver.clickElement({ text: 'I will try anyway', tag: 'button' }); + await driver.clickElement({ text: 'Confirm', tag: 'button' }); + await driver.waitUntilXWindowHandles(2); + await driver.switchToWindow(extension); + await driver.waitForSelector( + '.transaction-list__completed-transactions .transaction-list-item:nth-of-type(2)', + { timeout: 10000 }, + ); + + // display the transaction status + const transactionStatus = await driver.findElement( + '.transaction-list-item:nth-of-type(1) .transaction-status', + ); + assert.equal(await transactionStatus.getText(), 'Failed'); + }, + ); + }); +}); diff --git a/test/e2e/tests/send-edit.spec.js b/test/e2e/tests/send-edit.spec.js index ec25342fc..5c274acc2 100644 --- a/test/e2e/tests/send-edit.spec.js +++ b/test/e2e/tests/send-edit.spec.js @@ -61,6 +61,7 @@ describe('Editing Confirm Transaction', function () { await driver.delay(largeDelayMs); await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(largeDelayMs); // has correct updated value on the confirm screen the transaction const editedTransactionAmounts = await driver.findElements( @@ -170,6 +171,7 @@ describe('Editing Confirm Transaction', function () { // Submit gas fee changes await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(largeDelayMs); // has correct updated value on the confirm screen the transaction const editedTransactionAmounts = await driver.findElements( diff --git a/test/e2e/tests/send-eth.spec.js b/test/e2e/tests/send-eth.spec.js index f17f116d9..b59f2cf4e 100644 --- a/test/e2e/tests/send-eth.spec.js +++ b/test/e2e/tests/send-eth.spec.js @@ -190,7 +190,7 @@ describe('Send ETH from dapp using advanced gas controls', function () { await driver.clickElement({ text: 'Settings', tag: 'div' }); await driver.clickElement({ text: 'Advanced', tag: 'div' }); await driver.clickElement( - '[data-testid="advanced-setting-show-testnet-conversion"] .settings-page__content-item-col > div > div', + '[data-testid="advanced-setting-show-testnet-conversion"] .settings-page__content-item-col > label > div', ); const advancedGasTitle = await driver.findElement({ text: 'Advanced gas controls', @@ -198,7 +198,7 @@ describe('Send ETH from dapp using advanced gas controls', function () { }); await driver.scrollToElement(advancedGasTitle); await driver.clickElement( - '[data-testid="advanced-setting-advanced-gas-inline"] .settings-page__content-item-col > div > div', + '[data-testid="advanced-setting-advanced-gas-inline"] .settings-page__content-item-col > label > div', ); windowHandles = await driver.getAllWindowHandles(); extension = windowHandles[0]; @@ -245,6 +245,7 @@ describe('Send ETH from dapp using advanced gas controls', function () { await gasPriceInput.fill('100'); await driver.delay(1000); await driver.clickElement({ text: 'Save', tag: 'button' }); + await driver.delay(1000); await driver.clickElement({ text: 'Confirm', tag: 'button' }); await driver.waitUntilXWindowHandles(2); await driver.switchToWindow(extension); diff --git a/test/e2e/webdriver/chrome.js b/test/e2e/webdriver/chrome.js index 05ce2b65d..13ff00f62 100644 --- a/test/e2e/webdriver/chrome.js +++ b/test/e2e/webdriver/chrome.js @@ -18,6 +18,7 @@ class ChromeDriver { if (responsive) { args.push('--auto-open-devtools-for-tabs'); } + args.push('--log-level=3'); const options = new chrome.Options().addArguments(args); options.setProxy(proxy.manual({ https: HTTPS_PROXY_HOST })); options.setAcceptInsecureCerts(true); diff --git a/test/e2e/webdriver/driver.js b/test/e2e/webdriver/driver.js index 8f23f3cdc..068478741 100644 --- a/test/e2e/webdriver/driver.js +++ b/test/e2e/webdriver/driver.js @@ -274,7 +274,9 @@ class Driver { await this.executeScript( `navigator.clipboard.writeText("${contentToPaste}")`, ); - await this.fill(element, Key.chord(Key.CONTROL, 'v')); + const modifierKey = + process.platform === 'darwin' ? Key.COMMAND : Key.CONTROL; + await this.fill(element, Key.chord(modifierKey, 'v')); } // Navigation diff --git a/test/e2e/webdriver/index.js b/test/e2e/webdriver/index.js index fee966d8f..9753c03fe 100644 --- a/test/e2e/webdriver/index.js +++ b/test/e2e/webdriver/index.js @@ -48,7 +48,7 @@ async function setupFetchMocking(driver) { return { json: async () => clone(mockResponses.gasPricesBasic) }; } else if (url.match(/chromeextensionmm/u)) { return { json: async () => clone(mockResponses.metametrics) }; - } else if (url.match(/^https:\/\/(api2\.metaswap\.codefi\.network)/u)) { + } else if (url.match(/^https:\/\/(swap\.metaswap\.codefi\.network)/u)) { if (url.match(/featureFlags$/u)) { return { json: async () => clone(mockResponses.swaps.featureFlags) }; } diff --git a/test/helpers/setup-helper.js b/test/helpers/setup-helper.js index 9d4b7d892..75dfb8ea7 100644 --- a/test/helpers/setup-helper.js +++ b/test/helpers/setup-helper.js @@ -6,6 +6,8 @@ import { JSDOM } from 'jsdom'; process.env.IN_TEST = true; +global.chrome = { runtime: { id: 'testid' } }; + nock.disableNetConnect(); nock.enableNetConnect('localhost'); diff --git a/test/jest/constants.js b/test/jest/constants.js index 8015a8a65..4017903d8 100644 --- a/test/jest/constants.js +++ b/test/jest/constants.js @@ -1,2 +1,2 @@ -export const METASWAP_BASE_URL = 'https://api2.metaswap.codefi.network'; +export const METASWAP_BASE_URL = 'https://swap.metaswap.codefi.network'; export const GAS_API_URL = 'https://gas-api.metaswap.codefi.network'; diff --git a/test/lib/render-helpers.js b/test/lib/render-helpers.js index 9fc7aad16..27bc73b7c 100644 --- a/test/lib/render-helpers.js +++ b/test/lib/render-helpers.js @@ -31,12 +31,14 @@ export function mountWithRouter(component, store = {}, pathname = '/') { router, t: (str) => str, metricsEvent: () => undefined, + trackEvent: () => undefined, store, }, childContextTypes: { router: PropTypes.object, t: PropTypes.func, metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, store: PropTypes.object, }, }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..34389d567 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "inlineSources": true, + "isolatedModules": true, + "jsx": "react", + "lib": ["dom", "es2020"], + "moduleResolution": "node", + "noEmitOnError": true, + "outDir": "tsout", + "rootDir": ".", + "sourceMap": true, + "strict": true + }, + "exclude": [ + "**/*.test.js", + "**/*.test.ts", + "**/*.test.tsx", + ".storybook/**/*", + "builds/**/*", + "dist/**/*", + "node_modules/**" + ], + "extends": "@tsconfig/node14/tsconfig.json" +} diff --git a/ui/__mocks__/webextension-polyfill.js b/ui/__mocks__/webextension-polyfill.js new file mode 100644 index 000000000..54e90aa25 --- /dev/null +++ b/ui/__mocks__/webextension-polyfill.js @@ -0,0 +1,3 @@ +module.exports = { + runtime: {}, +}; diff --git a/ui/components/app/account-menu/account-menu.component.js b/ui/components/app/account-menu/account-menu.component.js index 4c3ff209c..5f542d99d 100644 --- a/ui/components/app/account-menu/account-menu.component.js +++ b/ui/components/app/account-menu/account-menu.component.js @@ -25,6 +25,13 @@ import { } from '../../../helpers/constants/routes'; import TextField from '../../ui/text-field'; import SearchIcon from '../../ui/search-icon'; +import IconCheck from '../../ui/icon/icon-check'; +import IconSpeechBubbles from '../../ui/icon/icon-speech-bubbles'; +import IconConnect from '../../ui/icon/icon-connect'; +import IconCog from '../../ui/icon/icon-cog'; +import IconPlus from '../../ui/icon/icon-plus'; +import IconImport from '../../ui/icon/icon-import'; + import Button from '../../ui/button'; import KeyRingLabel from './keyring-label'; @@ -61,7 +68,7 @@ AccountMenuItem.propTypes = { export default class AccountMenu extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -189,11 +196,12 @@ export default class AccountMenu extends Component {
{ - this.context.metricsEvent({ - eventOpts: { - category: 'Navigation', + this.context.trackEvent({ + category: 'Navigation', + event: 'Switched Account', + properties: { action: 'Main Menu', - name: 'Switched Account', + legacy_event: true, }, }); showAccountDetail(identity.address); @@ -202,7 +210,7 @@ export default class AccountMenu extends Component { >
{isSelected ? ( -
+ ) : null}
@@ -271,18 +279,13 @@ export default class AccountMenu extends Component { className="account-menu__scroll-button" onClick={this.handleScrollDown} > - {this.context.t('scrollDown')} +
); } render() { - const { t, metricsEvent } = this.context; + const { t, trackEvent } = this.context; const { shouldShowAccountsSearch, isAccountMenuOpen, @@ -309,6 +312,7 @@ export default class AccountMenu extends Component { {t('myAccounts')}
); }; diff --git a/ui/components/app/app-loading-spinner/index.scss b/ui/components/app/app-loading-spinner/index.scss index 271b40c9d..9e2ea504d 100644 --- a/ui/components/app/app-loading-spinner/index.scss +++ b/ui/components/app/app-loading-spinner/index.scss @@ -1,5 +1,5 @@ .app-loading-spinner { - background-color: rgba(255, 255, 255, 0.75); + background-color: var(--color-overlay-alternative); display: flex; align-items: center; justify-content: center; diff --git a/ui/components/app/asset-list-item/asset-list-item.js b/ui/components/app/asset-list-item/asset-list-item.js index a571ee42b..8b743ac7a 100644 --- a/ui/components/app/asset-list-item/asset-list-item.js +++ b/ui/components/app/asset-list-item/asset-list-item.js @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useContext } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useDispatch } from 'react-redux'; @@ -9,11 +9,12 @@ import Tooltip from '../../ui/tooltip'; import InfoIcon from '../../ui/icon/info-icon.component'; import Button from '../../ui/button'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { useMetricEvent } from '../../../hooks/useMetricEvent'; -import { ASSET_TYPES, updateSendAsset } from '../../../ducks/send'; +import { updateSendAsset } from '../../../ducks/send'; import { SEND_ROUTE } from '../../../helpers/constants/routes'; import { SEVERITIES } from '../../../helpers/constants/design-system'; import { INVALID_ASSET_TYPE } from '../../../helpers/constants/error-keys'; +import { ASSET_TYPES } from '../../../../shared/constants/transaction'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; const AssetListItem = ({ className, @@ -33,13 +34,7 @@ const AssetListItem = ({ const t = useI18nContext(); const dispatch = useDispatch(); const history = useHistory(); - const sendTokenEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Home', - name: 'Clicked Send: Token', - }, - }); + const trackEvent = useContext(MetaMetricsContext); const titleIcon = warning ? ( { e.stopPropagation(); - sendTokenEvent(); + trackEvent({ + event: 'Clicked Send: Token', + category: 'Navigation', + properties: { + action: 'Home', + legacy_event: true, + }, + }); try { await dispatch( updateSendAsset({ @@ -93,7 +95,7 @@ const AssetListItem = ({ ); }, [ tokenSymbol, - sendTokenEvent, + trackEvent, tokenAddress, tokenDecimals, history, diff --git a/ui/components/app/asset-list-item/asset-list-item.scss b/ui/components/app/asset-list-item/asset-list-item.scss index 6e8421027..3289e1d01 100644 --- a/ui/components/app/asset-list-item/asset-list-item.scss +++ b/ui/components/app/asset-list-item/asset-list-item.scss @@ -8,6 +8,7 @@ min-width: 0; min-height: 0; text-align: start; + color: var(--color-text-default); & h2 { display: flex; @@ -29,7 +30,7 @@ } &__chevron-right { - color: var(--Grey-500); + color: var(--color-icon-default); } .list-item__right-content { diff --git a/ui/components/app/asset-list/asset-list.js b/ui/components/app/asset-list/asset-list.js index c0b1347d0..75be4008d 100644 --- a/ui/components/app/asset-list/asset-list.js +++ b/ui/components/app/asset-list/asset-list.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; @@ -7,7 +7,6 @@ import TokenList from '../token-list'; import { IMPORT_TOKEN_ROUTE } from '../../../helpers/constants/routes'; import AssetListItem from '../asset-list-item'; import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'; -import { useMetricEvent } from '../../../hooks/useMetricEvent'; import { useUserPreferencedCurrency } from '../../../hooks/useUserPreferencedCurrency'; import { getCurrentAccountWithSendEtherInfo, @@ -26,6 +25,7 @@ import { JUSTIFY_CONTENT, } from '../../../helpers/constants/design-system'; import { useI18nContext } from '../../../hooks/useI18nContext'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; const AssetList = ({ onClickAsset }) => { const t = useI18nContext(); @@ -35,20 +35,7 @@ const AssetList = ({ onClickAsset }) => { ); const nativeCurrency = useSelector(getNativeCurrency); const showFiat = useSelector(getShouldShowFiat); - const selectTokenEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Token Menu', - name: 'Clicked Token', - }, - }); - const addTokenEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Token Menu', - name: 'Clicked "Add Token"', - }, - }); + const trackEvent = useContext(MetaMetricsContext); const { currency: primaryCurrency, @@ -94,7 +81,14 @@ const AssetList = ({ onClickAsset }) => { { onClickAsset(tokenAddress); - selectTokenEvent(); + trackEvent({ + event: 'Clicked Token', + category: 'Navigation', + properties: { + action: 'Token Menu', + legacy_event: true, + }, + }); }} /> @@ -111,7 +105,14 @@ const AssetList = ({ onClickAsset }) => { isMainnet={isMainnet} onClick={() => { history.push(IMPORT_TOKEN_ROUTE); - addTokenEvent(); + trackEvent({ + event: 'Clicked "Add Token"', + category: 'Navigation', + properties: { + action: 'Token Menu', + legacy_event: true, + }, + }); }} /> diff --git a/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.js b/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.js index 077b19ca6..9bcadbc00 100644 --- a/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.js +++ b/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.js @@ -84,7 +84,7 @@ const CancelSpeedupPopover = () => { } else { speedUpTransaction(); } - closeModal('cancelSpeedUpTransaction'); + closeModal(['cancelSpeedUpTransaction']); }; return ( diff --git a/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js b/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js index 89b1151d0..94a393c44 100644 --- a/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js +++ b/ui/components/app/cancel-speedup-popover/cancel-speedup-popover.test.js @@ -22,6 +22,8 @@ jest.mock('../../../store/actions', () => ({ addPollingTokenToAppState: jest.fn(), removePollingTokenFromAppState: jest.fn(), updateTransaction: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }), + updateTransactionGasFees: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }), + updatePreviousGasParams: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }), createTransactionEventFragment: jest.fn(), })); diff --git a/ui/components/app/cancel-speedup-popover/index.scss b/ui/components/app/cancel-speedup-popover/index.scss index 331e486eb..cd7886982 100644 --- a/ui/components/app/cancel-speedup-popover/index.scss +++ b/ui/components/app/cancel-speedup-popover/index.scss @@ -21,7 +21,7 @@ } &__separator { - border-bottom: 1px solid var(--ui-grey); + border-bottom: 1px solid var(--color-border-default); width: 100%; } } diff --git a/ui/components/app/collectible-details/collectible-details.js b/ui/components/app/collectible-details/collectible-details.js index be538d852..4e7c433d4 100644 --- a/ui/components/app/collectible-details/collectible-details.js +++ b/ui/components/app/collectible-details/collectible-details.js @@ -45,12 +45,13 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import CollectibleOptions from '../collectible-options/collectible-options'; import Button from '../../ui/button'; -import { ASSET_TYPES, updateSendAsset } from '../../../ducks/send'; +import { updateSendAsset } from '../../../ducks/send'; import InfoTooltip from '../../ui/info-tooltip'; import { ERC721 } from '../../../helpers/constants/common'; import { usePrevious } from '../../../hooks/usePrevious'; import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard'; import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; +import { ASSET_TYPES } from '../../../../shared/constants/transaction'; export default function CollectibleDetails({ collectible }) { const { @@ -310,7 +311,7 @@ export default function CollectibleDetails({ collectible }) { {copied ? ( t('copiedExclamation') ) : ( - + )} diff --git a/ui/components/app/collectible-details/index.scss b/ui/components/app/collectible-details/index.scss index e3e83ec9b..c368c63ff 100644 --- a/ui/components/app/collectible-details/index.scss +++ b/ui/components/app/collectible-details/index.scss @@ -66,7 +66,7 @@ $spacer-break-small: 16px; justify-content: center; background-color: transparent; cursor: pointer; - color: var(--ui-4); + color: var(--color-text-alternative); border: 0; &:active { diff --git a/ui/components/app/collectible-options/index.scss b/ui/components/app/collectible-options/index.scss index fe91042e2..a60ab0c6f 100644 --- a/ui/components/app/collectible-options/index.scss +++ b/ui/components/app/collectible-options/index.scss @@ -1,7 +1,7 @@ .collectible-options { &__button { font-size: $font-size-paragraph; - color: var(--Black-100); + color: var(--color-text-default); background-color: inherit; padding: 2px 0 2px 8px; } diff --git a/ui/components/app/collectibles-detection-notice/index.scss b/ui/components/app/collectibles-detection-notice/index.scss index c972cb393..4f7bc3d4b 100644 --- a/ui/components/app/collectibles-detection-notice/index.scss +++ b/ui/components/app/collectibles-detection-notice/index.scss @@ -11,7 +11,7 @@ content: '\00D7'; font-size: 29px; font-weight: 200; - color: var(--black); + color: var(--color-icon-default); background-color: transparent; top: 0; right: 12px; diff --git a/ui/components/app/collectibles-items/index.scss b/ui/components/app/collectibles-items/index.scss index 5844d153e..a2dc0dd58 100644 --- a/ui/components/app/collectibles-items/index.scss +++ b/ui/components/app/collectibles-items/index.scss @@ -23,8 +23,8 @@ width: 32px; height: 32px; padding: 8px; - background: var(--ui-4); - color: var(--ui-white); + background: var(--color-overlay-alternative); + color: var(--color-overlay-inverse); text-align: center; } diff --git a/ui/components/app/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js b/ui/components/app/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js index 4ee4ecbd4..fcef11674 100644 --- a/ui/components/app/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js +++ b/ui/components/app/confirm-page-container/confirm-detail-row/confirm-detail-row.component.js @@ -44,7 +44,7 @@ const ConfirmDetailRow = (props) => { type={PRIMARY} value={value} showEthLogo - ethLogoHeight="18" + ethLogoHeight={18} style={{ color: primaryValueTextColor }} hideLabel /> diff --git a/ui/components/app/confirm-page-container/confirm-detail-row/index.scss b/ui/components/app/confirm-page-container/confirm-detail-row/index.scss index 53e100f5b..9645ef29b 100644 --- a/ui/components/app/confirm-page-container/confirm-detail-row/index.scss +++ b/ui/components/app/confirm-page-container/confirm-detail-row/index.scss @@ -9,7 +9,7 @@ @include H7; font-weight: 500; - color: var(--scorpion); + color: var(--color-text-allternative); text-transform: uppercase; } @@ -26,7 +26,7 @@ } &__secondary { - color: var(--oslo-gray); + color: var(--color-icon-default); justify-content: flex-end; } @@ -35,10 +35,10 @@ text-transform: uppercase; margin-bottom: 6px; - color: var(--scorpion); + color: var(--color-icon-default); &--edit { - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; } diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js index 16f89f91e..641a13f03 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js @@ -10,7 +10,6 @@ import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../../helpers/constants/erro import Typography from '../../../ui/typography'; import { TYPOGRAPHY } from '../../../../helpers/constants/design-system'; import { TRANSACTION_TYPES } from '../../../../../shared/constants/transaction'; -import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network'; import { ConfirmPageContainerSummary, ConfirmPageContainerWarning } from '.'; @@ -57,6 +56,7 @@ export default class ConfirmPageContainerContent extends Component { showBuyModal: PropTypes.func, toAddress: PropTypes.string, transactionType: PropTypes.string, + isBuyableChain: PropTypes.bool, }; renderContent() { @@ -132,6 +132,7 @@ export default class ConfirmPageContainerContent extends Component { showBuyModal, toAddress, transactionType, + isBuyableChain, } = this.props; const primaryAction = hideUserAcknowledgedGasMissing @@ -146,8 +147,7 @@ export default class ConfirmPageContainerContent extends Component { supportsEIP1559V2 && !hasSimulationError && (errorKey || errorMessage) && - errorKey === INSUFFICIENT_FUNDS_ERROR_KEY && - currentTransaction.type === TRANSACTION_TYPES.SIMPLE_SEND; + errorKey === INSUFFICIENT_FUNDS_ERROR_KEY; return (
- {currentTransaction.chainId === MAINNET_CHAIN_ID ? ( - - {t('insufficientCurrency', [nativeCurrency, networkName])} - + {t('insufficientCurrencyBuyOrDeposit', [ + nativeCurrency, + networkName, - {t('orDeposit')} + , + ])} - } - useIcon - iconFillColor="#d73a49" - type="danger" - /> - ) : ( - - {t('insufficientCurrency', [nativeCurrency, networkName])} - {t('buyOther', [nativeCurrency])} + {t('insufficientCurrencyDeposit', [ + nativeCurrency, + networkName, + ])} - } - useIcon - iconFillColor="#d73a49" - type="danger" - /> - )} + ) + } + useIcon + iconFillColor="var(--color-error-default)" + type="danger" + />
)} diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js index 7da4ddeba..218fc99a7 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/confirm-page-container-summary.component.js @@ -38,6 +38,7 @@ const ConfirmPageContainerSummary = (props) => { TRANSACTION_TYPES.CONTRACT_INTERACTION, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM, ]; const isContractTypeTransaction = contractInitiatedTransactionType.includes( transactionType, @@ -49,7 +50,8 @@ const ConfirmPageContainerSummary = (props) => { // type of contract interaction it is passed as toAddress contractAddress = transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER || - transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM + transactionType === TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM || + transactionType === TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM ? tokenAddress : toAddress; } diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/index.scss b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/index.scss index 53fb24a22..24a3fa620 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/index.scss +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-summary/index.scss @@ -1,6 +1,6 @@ .confirm-page-container-summary { padding: 16px 24px; - background-color: #f9fafa; + background-color: var(--color-background-alternative); box-sizing: border-box; display: flex; flex-direction: column; @@ -23,9 +23,9 @@ &__action { @include H7; - color: var(--oslo-gray); + color: var(--color-text-muted); padding: 3px 8px; - border: 1px solid var(--oslo-gray); + border: 1px solid var(--color-border-default); border-radius: 4px; display: inline-flex; align-items: center; @@ -51,12 +51,12 @@ border: none; padding: 0; margin-inline-end: 4px; - color: var(--primary-1); + color: var(--color-primary-default); } } &__nonce { - color: var(--oslo-gray); + color: var(--color-text-alternative); } &__title { @@ -88,13 +88,13 @@ &__subtitle { @include H5; - color: var(--oslo-gray); + color: var(--color-text-alternative); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } &--border { - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-muted); } } diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/confirm-page-container-warning.component.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/confirm-page-container-warning.component.js index 29f7b9edd..5c4f5d632 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/confirm-page-container-warning.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/confirm-page-container-warning.component.js @@ -4,11 +4,7 @@ import PropTypes from 'prop-types'; const ConfirmPageContainerWarning = (props) => { return (
- +
{props.warning}
diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/confirm-page-container-warning.stories.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/confirm-page-container-warning.stories.js new file mode 100644 index 000000000..f36b6bf4c --- /dev/null +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/confirm-page-container-warning.stories.js @@ -0,0 +1,19 @@ +import React from 'react'; +import ConfirmPageContainerWarning from '.'; + +export default { + title: 'Components/UI/ConfirmPageContainerWarning', // title should follow the folder structure location of the component. Don't use spaces. + id: __filename, + argTypes: { + warning: { + control: 'text', + }, + }, + args: { + warning: 'This is a warning', + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/index.scss b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/index.scss index 06aa82551..e5377f96a 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/index.scss +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-warning/index.scss @@ -1,18 +1,19 @@ .confirm-page-container-warning { - background-color: #fffcdb; + background-color: var(--color-warning-muted); display: flex; align-items: center; - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-muted); padding: 12px 24px; &__icon { flex: 0 0 auto; - margin-right: 16px; + margin-right: 8px; + color: var(--color-warning-default); } &__warning { @include H7; - color: #5f5922; + color: var(--color-text-default); } } diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss b/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss index f52888db2..37d104c80 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss +++ b/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss @@ -9,7 +9,7 @@ flex-direction: column; &--with-top-border { - border-top: 1px solid var(--geyser); + border-top: 1px solid var(--color-border-muted); } &__error-container { @@ -23,7 +23,7 @@ &__data { padding: 16px; - color: var(--oslo-gray); + color: var(--color-text-alternative); & > .disclosure { margin-top: 0; @@ -33,7 +33,7 @@ &__data-box { @include H7; - background-color: #f9fafa; + background-color: var(--color-background-alternative); padding: 12px; word-wrap: break-word; overflow-y: auto; @@ -61,7 +61,7 @@ } &__gas-fee { - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-muted); .advanced-gas-inputs__gas-edit-rows { margin-bottom: 16px; @@ -73,20 +73,20 @@ font-weight: 500; text-transform: capitalize; - color: var(--black); + color: var(--color-text-default); padding-left: 5px; } &__tab { @include H7; - color: #8c8e94; + color: var(--color-text-alternative); text-transform: uppercase; margin: 0 8px; & button { font-size: unset; - color: #8c8e94; + color: var(--color-text-alternative); text-transform: uppercase; } } @@ -100,12 +100,4 @@ &__total-value { position: relative; } - - &__link { - background: transparent; - border: 0 transparent; - display: inline; - padding: 0; - font-size: $font-size-h7; - } } diff --git a/ui/components/app/confirm-page-container/confirm-page-container-header/index.scss b/ui/components/app/confirm-page-container/confirm-page-container-header/index.scss index 1ffb8707a..57c9eb90b 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container-header/index.scss +++ b/ui/components/app/confirm-page-container/confirm-page-container-header/index.scss @@ -6,7 +6,7 @@ &__row { display: flex; justify-content: space-between; - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-muted); padding: 4px 13px 4px 13px; flex: 0 0 auto; align-items: center; @@ -31,6 +31,7 @@ &__back-button { @include Paragraph; + color: var(--color-primary-default); cursor: pointer; padding-left: 5px; } diff --git a/ui/components/app/confirm-page-container/confirm-page-container-navigation/confirm-page-container-navigation.component.js b/ui/components/app/confirm-page-container/confirm-page-container-navigation/confirm-page-container-navigation.component.js index 8cc0985fb..6c7bb981b 100755 --- a/ui/components/app/confirm-page-container/confirm-page-container-navigation/confirm-page-container-navigation.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container-navigation/confirm-page-container-navigation.component.js @@ -28,20 +28,20 @@ const ConfirmPageContainerNavigation = (props) => { visibility: prevTxId ? 'initial' : 'hidden', }} > -
onNextTx(firstTx)} > - -
-
+ +
+ +
@@ -57,28 +57,20 @@ const ConfirmPageContainerNavigation = (props) => { visibility: nextTxId ? 'initial' : 'hidden', }} > -
onNextTx(nextTxId)} > - -
-
+ +
+ +
); diff --git a/ui/components/app/confirm-page-container/confirm-page-container-navigation/index.scss b/ui/components/app/confirm-page-container/confirm-page-container-navigation/index.scss index f0f065569..93ed2e962 100755 --- a/ui/components/app/confirm-page-container/confirm-page-container-navigation/index.scss +++ b/ui/components/app/confirm-page-container/confirm-page-container-navigation/index.scss @@ -3,7 +3,7 @@ justify-content: space-between; font: inherit; padding: 4px 10px 4px 10px; - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-muted); flex: 0 0 auto; &__container { @@ -13,8 +13,11 @@ &__arrow { cursor: pointer; display: flex; - padding-left: 5px; - padding-right: 5px; + padding-left: 4px; + padding-right: 4px; + background: none; + border: none; + color: var(--color-icon-default); } &__arrow:hover { @@ -44,7 +47,7 @@ &__longtext { @include H9; - color: var(--oslo-gray); + color: var(--color-text-alternative); } &__imageflip { diff --git a/ui/components/app/confirm-page-container/confirm-page-container.component.js b/ui/components/app/confirm-page-container/confirm-page-container.component.js index 641230df8..f011405b9 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container.component.js +++ b/ui/components/app/confirm-page-container/confirm-page-container.component.js @@ -4,10 +4,7 @@ import PropTypes from 'prop-types'; import { EDIT_GAS_MODES } from '../../../../shared/constants/gas'; import { GasFeeContextProvider } from '../../../contexts/gasFee'; import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; -import { - NETWORK_TO_NAME_MAP, - MAINNET_CHAIN_ID, -} from '../../../../shared/constants/network'; +import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network'; import { PageContainerFooter } from '../../ui/page-container'; import Dialog from '../../ui/dialog'; @@ -98,6 +95,7 @@ export default class ConfirmPageContainer extends Component { supportsEIP1559V2: PropTypes.bool, nativeCurrency: PropTypes.string, showBuyModal: PropTypes.func, + isBuyableChain: PropTypes.bool, }; render() { @@ -152,6 +150,7 @@ export default class ConfirmPageContainer extends Component { supportsEIP1559V2, nativeCurrency, showBuyModal, + isBuyableChain, } = this.props; const showAddToAddressDialog = @@ -258,47 +257,41 @@ export default class ConfirmPageContainer extends Component { showBuyModal={showBuyModal} toAddress={toAddress} transactionType={currentTransaction.type} + isBuyableChain={isBuyableChain} /> )} {shouldDisplayWarning && errorKey === INSUFFICIENT_FUNDS_ERROR_KEY && (
- {currentTransaction.chainId === MAINNET_CHAIN_ID ? ( - - {t('insufficientCurrency', [nativeCurrency, networkName])} - - - {t('orDeposit')} + {t('insufficientCurrencyBuyOrDeposit', [ + nativeCurrency, + networkName, + , + ])} - } - useIcon - iconFillColor="#d73a49" - type="danger" - /> - ) : ( - - {t('insufficientCurrency', [nativeCurrency, networkName])} - {t('buyOther', [nativeCurrency])} + ) : ( + + {t('insufficientCurrencyDeposit', [ + nativeCurrency, + networkName, + ])} - } - useIcon - iconFillColor="#d73a49" - type="danger" - /> - )} + ) + } + useIcon + iconFillColor="var(--color-error-default)" + type="danger" + />
)} {shouldDisplayWarning && errorKey !== INSUFFICIENT_FUNDS_ERROR_KEY && ( diff --git a/ui/components/app/confirm-page-container/confirm-page-container.container.js b/ui/components/app/confirm-page-container/confirm-page-container.container.js index d0d287aed..2ad758908 100644 --- a/ui/components/app/confirm-page-container/confirm-page-container.container.js +++ b/ui/components/app/confirm-page-container/confirm-page-container.container.js @@ -1,13 +1,18 @@ import { connect } from 'react-redux'; -import { getAccountsWithLabels, getAddressBookEntry } from '../../../selectors'; +import { + getAccountsWithLabels, + getAddressBookEntry, + getIsBuyableChain, +} from '../../../selectors'; import { showModal } from '../../../store/actions'; import ConfirmPageContainer from './confirm-page-container.component'; function mapStateToProps(state, ownProps) { const to = ownProps.toAddress; - + const isBuyableChain = getIsBuyableChain(state); const contact = getAddressBookEntry(state, to); return { + isBuyableChain, contact, toName: contact?.name || ownProps.toName, isOwnedAccount: getAccountsWithLabels(state) diff --git a/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.scss b/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.scss index 9615bd0ac..eef81fc99 100644 --- a/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.scss +++ b/ui/components/app/confirm-page-container/enableEIP1559V2-notice/index.scss @@ -14,7 +14,7 @@ content: '\00D7'; font-size: 29px; font-weight: 200; - color: var(--black); + color: var(--color-text-default); background-color: transparent; top: 0; right: 12px; diff --git a/ui/components/app/connected-accounts-list/connected-accounts-list.component.js b/ui/components/app/connected-accounts-list/connected-accounts-list.component.js index cb1fa5861..20d4cdf91 100644 --- a/ui/components/app/connected-accounts-list/connected-accounts-list.component.js +++ b/ui/components/app/connected-accounts-list/connected-accounts-list.component.js @@ -104,10 +104,7 @@ export default class ConnectedAccountsList extends PureComponent { onShowOptions={this.showAccountOptions.bind(null, address)} show={accountWithOptionsShown === address} > - + {t('disconnectThisAccount')} diff --git a/ui/components/app/connected-accounts-list/index.scss b/ui/components/app/connected-accounts-list/index.scss index 2db798c13..98339fd22 100644 --- a/ui/components/app/connected-accounts-list/index.scss +++ b/ui/components/app/connected-accounts-list/index.scss @@ -24,7 +24,7 @@ @extend %account-status-typography; display: inline; - color: var(--Grey-500); + color: var(--color-text-alternative); } &__account-status-link { @@ -34,7 +34,7 @@ &, &:hover { - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; } } @@ -46,11 +46,11 @@ align-items: center; width: 100%; padding: 16px 24px; - border-top: 1px solid var(--geyser); + border-top: 1px solid var(--color-border-muted); &--highlight { - background-color: var(--Yellow-000); - border: 1px solid var(--accent-yellow); + background-color: var(--color-warning-muted); + border: 1px solid var(--color-warning-default); box-sizing: border-box; padding: 16px; margin-bottom: 16px; @@ -69,7 +69,7 @@ &__button { font-size: $font-size-h4; background: inherit; - color: var(--Grey-500); + color: var(--color-icon-default); } } diff --git a/ui/components/app/connected-accounts-permissions/connected-accounts-permissions.js b/ui/components/app/connected-accounts-permissions/connected-accounts-permissions.js index feb781ab7..dce984c47 100644 --- a/ui/components/app/connected-accounts-permissions/connected-accounts-permissions.js +++ b/ui/components/app/connected-accounts-permissions/connected-accounts-permissions.js @@ -3,14 +3,12 @@ import PropTypes from 'prop-types'; import React, { useState } from 'react'; import CheckBox from '../../ui/check-box'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { usePermissionDescriptions } from '../../../hooks/usePermissionDescriptions'; +import { getPermissionDescription } from '../../../helpers/utils/permission'; const ConnectedAccountsPermissions = ({ permissions }) => { const t = useI18nContext(); const [expanded, setExpanded] = useState(false); - const getPermissionDescription = usePermissionDescriptions(); - const toggleExpanded = () => { setExpanded((_expanded) => !_expanded); }; @@ -56,7 +54,7 @@ const ConnectedAccountsPermissions = ({ permissions }) => { className="connected-accounts-permissions__checkbox" /> ))} diff --git a/ui/components/app/connected-accounts-permissions/index.scss b/ui/components/app/connected-accounts-permissions/index.scss index 98ddd1ec7..06a2fcad6 100644 --- a/ui/components/app/connected-accounts-permissions/index.scss +++ b/ui/components/app/connected-accounts-permissions/index.scss @@ -3,7 +3,7 @@ display: flex; flex-direction: column; - color: var(--Grey-500); + color: var(--color-text-alternative); strong { font-weight: bold; @@ -21,7 +21,7 @@ justify-content: space-between; align-items: center; cursor: pointer; - color: #24292e; + color: var(--color-text-default); button { font-size: $font-size-paragraph; diff --git a/ui/components/app/connected-sites-list/index.scss b/ui/components/app/connected-sites-list/index.scss index 21167a9ce..3a690d189 100644 --- a/ui/components/app/connected-sites-list/index.scss +++ b/ui/components/app/connected-sites-list/index.scss @@ -11,7 +11,7 @@ flex-direction: row; justify-content: space-between; align-items: center; - border-top: 1px solid #d2d8dd; + border-top: 1px solid var(--color-border-muted); padding: 16px 24px; & &-link-button { diff --git a/ui/components/app/connected-status-indicator/connected-status-indicator.js b/ui/components/app/connected-status-indicator/connected-status-indicator.js index 969cb401c..e005cb840 100644 --- a/ui/components/app/connected-status-indicator/connected-status-indicator.js +++ b/ui/components/app/connected-status-indicator/connected-status-indicator.js @@ -37,13 +37,13 @@ export default function ConnectedStatusIndicator({ onClick }) { } let indicatorType = ColorIndicator.TYPES.OUTLINE; - let indicatorColor = COLORS.UI4; + let indicatorColor = COLORS.ICON_DEFAULT; if (status === STATUS_CONNECTED) { - indicatorColor = COLORS.SUCCESS1; + indicatorColor = COLORS.SUCCESS_DEFAULT; indicatorType = ColorIndicator.TYPES.PARTIAL; } else if (status === STATUS_CONNECTED_TO_ANOTHER_ACCOUNT) { - indicatorColor = COLORS.ALERT1; + indicatorColor = COLORS.ERROR_DEFAULT; } const text = diff --git a/ui/components/app/connected-status-indicator/index.scss b/ui/components/app/connected-status-indicator/index.scss index bfd61b1b6..547086abb 100644 --- a/ui/components/app/connected-status-indicator/index.scss +++ b/ui/components/app/connected-status-indicator/index.scss @@ -7,12 +7,9 @@ padding: 8px; border-radius: 100px; - &:hover { - background-color: #f2f3f4; - } - + &:hover, &:active { - background-color: #ededed; + background-color: var(--color-background-alternative); } &__inner-circle { @@ -35,25 +32,25 @@ } &__green-circle { - border-color: #4cd964; + border-color: var(--color-success-default); } &__green-circle &__inner-circle { - background-color: #4cd964; + background-color: var(--color-success-default); } &__yellow-circle { - border-color: #ffd33d; + border-color: var(--color-warning-default); } &__grey-circle { - border-color: var(--Grey-500); + border-color: var(--color-border-default); } &__text { @include H8; - color: var(--Grey-500); + color: var(--color-text-alternative); margin-left: 6px; white-space: nowrap; } diff --git a/ui/components/app/create-new-vault/create-new-vault.js b/ui/components/app/create-new-vault/create-new-vault.js index 9fef846f4..7d442ecd2 100644 --- a/ui/components/app/create-new-vault/create-new-vault.js +++ b/ui/components/app/create-new-vault/create-new-vault.js @@ -1,7 +1,7 @@ import React, { useCallback, useContext, useState } from 'react'; import PropTypes from 'prop-types'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { MetaMetricsContext } from '../../../contexts/metametrics'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; import TextField from '../../ui/text-field'; import Button from '../../ui/button'; import CheckBox from '../../ui/check-box'; @@ -22,7 +22,7 @@ export default function CreateNewVault({ const [termsChecked, setTermsChecked] = useState(false); const t = useI18nContext(); - const metricsEvent = useContext(MetaMetricsContext); + const trackEvent = useContext(MetaMetricsContext); const onPasswordChange = useCallback( (newPassword) => { @@ -82,16 +82,17 @@ export default function CreateNewVault({ ); const toggleTermsCheck = useCallback(() => { - metricsEvent({ - eventOpts: { - category: 'Onboarding', + trackEvent({ + category: 'Onboarding', + event: 'Check ToS', + properties: { action: 'Import Seed Phrase', - name: 'Check ToS', + legacy_event: true, }, }); setTermsChecked((currentTermsChecked) => !currentTermsChecked); - }, [metricsEvent]); + }, [trackEvent]); const termsOfUse = t('acceptTermsOfUse', [ + } > {renderConversionComponent()} diff --git a/ui/components/app/currency-input/currency-input.stories.js b/ui/components/app/currency-input/currency-input.stories.js new file mode 100644 index 000000000..b68deae6e --- /dev/null +++ b/ui/components/app/currency-input/currency-input.stories.js @@ -0,0 +1,25 @@ +import React from 'react'; +import CurrencyInput from '.'; + +export default { + title: 'Components/App/CurrencyInput', + id: __filename, + argTypes: { + hexValue: { + control: 'text', + }, + featureSecondary: { + control: 'boolean', + }, + onChange: { + action: 'onChange', + }, + onPreferenceToggle: { + action: 'onPreferenceToggle', + }, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/currency-input/index.scss b/ui/components/app/currency-input/index.scss index c374e06ae..c8f7bc478 100644 --- a/ui/components/app/currency-input/index.scss +++ b/ui/components/app/currency-input/index.scss @@ -7,20 +7,12 @@ &__swap-component { flex: 0 0 auto; - height: 24px; - width: 24px; - background-image: url("images/icons/swap.svg"); - background-size: contain; - background-repeat: no-repeat; + display: flex; + align-items: center; + justify-content: center; + color: var(--color-icon-default); cursor: pointer; - opacity: 0.4; - - &:hover { - opacity: 0.3; - } - - &:active { - opacity: 0.5; - } + background: none; + border: none; } } diff --git a/ui/components/app/detected-token/detected-token-address/detected-token-address.js b/ui/components/app/detected-token/detected-token-address/detected-token-address.js new file mode 100644 index 000000000..1e385b4fa --- /dev/null +++ b/ui/components/app/detected-token/detected-token-address/detected-token-address.js @@ -0,0 +1,57 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import { I18nContext } from '../../../../contexts/i18n'; +import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard'; + +import Box from '../../../ui/box'; +import Button from '../../../ui/button'; +import Typography from '../../../ui/typography'; +import Tooltip from '../../../ui/tooltip'; + +import { + COLORS, + DISPLAY, + TYPOGRAPHY, +} from '../../../../helpers/constants/design-system'; + +import { shortenAddress } from '../../../../helpers/utils/util'; + +const DetectedTokenAddress = ({ address }) => { + const t = useContext(I18nContext); + const [copied, handleCopy] = useCopyToClipboard(); + + return ( + + + {`${t('tokenAddress')}:`} + + + {shortenAddress(address)} + + + + + + ); +}; + +DetectedTokenAddress.propTypes = { + address: PropTypes.string, +}; + +export default DetectedTokenAddress; diff --git a/ui/components/app/detected-token/detected-token-address/detected-token-address.stories.js b/ui/components/app/detected-token/detected-token-address/detected-token-address.stories.js new file mode 100644 index 000000000..4c328ebff --- /dev/null +++ b/ui/components/app/detected-token/detected-token-address/detected-token-address.stories.js @@ -0,0 +1,22 @@ +import React from 'react'; + +import DetectedTokenAddress from './detected-token-address'; + +export default { + title: 'Components/App/DetectedToken/DetectedTokenAddress', + id: __filename, + argTypes: { + address: { control: 'text' }, + }, + args: { + address: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', + }, +}; + +const Template = (args) => { + return ; +}; + +export const DefaultStory = Template.bind({}); + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/detected-token/detected-token-address/detected-token-address.test.js b/ui/components/app/detected-token/detected-token-address/detected-token-address.test.js new file mode 100644 index 000000000..b0daa552d --- /dev/null +++ b/ui/components/app/detected-token/detected-token-address/detected-token-address.test.js @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { renderWithProvider, screen } from '../../../../../test/jest'; +import configureStore from '../../../../store/store'; + +import DetectedTokenAddress from './detected-token-address'; + +describe('DetectedTokenAddress', () => { + const args = { + address: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', + }; + + it('should render the detected token address', async () => { + const store = configureStore({}); + renderWithProvider(, store); + + expect(screen.getByText('Token address:')).toBeInTheDocument(); + expect(screen.getByText('0xc01...2a6f')).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/detected-token/detected-token-address/index.scss b/ui/components/app/detected-token/detected-token-address/index.scss new file mode 100644 index 000000000..599a4d1d7 --- /dev/null +++ b/ui/components/app/detected-token/detected-token-address/index.scss @@ -0,0 +1,7 @@ +.detected-token-address { + & &__copy-link { + @include H7; + + padding: 4px 0; + } +} diff --git a/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.js b/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.js new file mode 100644 index 000000000..7cb66701a --- /dev/null +++ b/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.js @@ -0,0 +1,50 @@ +import React, { useContext, useState } from 'react'; +import PropTypes from 'prop-types'; +import { I18nContext } from '../../../../contexts/i18n'; + +import Box from '../../../ui/box'; +import Button from '../../../ui/button'; +import Typography from '../../../ui/typography/typography'; +import { + DISPLAY, + FONT_WEIGHT, + TYPOGRAPHY, +} from '../../../../helpers/constants/design-system'; + +const DetectedTokenAggregators = ({ aggregatorsList }) => { + const t = useContext(I18nContext); + const numOfHiddenAggregators = parseInt(aggregatorsList.length, 10) - 2; + const [displayMore, setDisplayMore] = useState(false); + + return ( + + + {t('fromTokenLists', [ + numOfHiddenAggregators > 0 && !displayMore ? ( + + {`${aggregatorsList.slice(0, 2).join(', ')}`} + + + ) : ( + + {`${aggregatorsList.join(', ')}.`} + + ), + ])} + + + ); +}; + +DetectedTokenAggregators.propTypes = { + aggregatorsList: PropTypes.array.isRequired, +}; + +export default DetectedTokenAggregators; diff --git a/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.stories.js b/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.stories.js new file mode 100644 index 000000000..c4c96b684 --- /dev/null +++ b/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.stories.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { DISPLAY } from '../../../../helpers/constants/design-system'; + +import Box from '../../../ui/box'; +import DetectedTokenAggregators from './detected-token-aggregators'; + +export default { + title: 'Components/App/DetectedToken/DetectedTokenAggregators', + id: __filename, + argTypes: { + aggregatorsList: { control: 'array' }, + }, + args: { + aggregatorsList1: [ + 'Aave', + 'Bancor', + 'CMC', + 'Crypto.com', + 'CoinGecko', + '1inch', + 'Paraswap', + 'PMM', + 'Synthetix', + 'Zapper', + 'Zerion', + '0x', + ], + aggregatorsList2: ['Aave', 'Bancor'], + }, +}; + +const Template = (args) => { + return ( + + + + + ); +}; + +export const DefaultStory = Template.bind({}); + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.test.js b/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.test.js new file mode 100644 index 000000000..27757fb32 --- /dev/null +++ b/ui/components/app/detected-token/detected-token-aggregators/detected-token-aggregators.test.js @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { + renderWithProvider, + screen, + fireEvent, +} from '../../../../../test/jest'; +import configureStore from '../../../../store/store'; + +import DetectedTokenAggregators from './detected-token-aggregators'; + +describe('DetectedTokenAggregators', () => { + const args = { + aggregatorsList: [ + 'Aave', + 'Bancor', + 'CMC', + 'Crypto.com', + 'CoinGecko', + '1inch', + 'Paraswap', + 'PMM', + 'Synthetix', + 'Zapper', + 'Zerion', + '0x', + ], + }; + + it('should render the detected token aggregators', async () => { + const store = configureStore({}); + renderWithProvider(, store); + + expect(screen.getByText('From token lists:')).toBeInTheDocument(); + expect(screen.getByText('Aave, Bancor')).toBeInTheDocument(); + expect(screen.getByText('+ 10 more')).toBeInTheDocument(); + fireEvent.click(screen.getByText('+ 10 more')); + expect( + screen.getByText( + 'Aave, Bancor, CMC, Crypto.com, CoinGecko, 1inch, Paraswap, PMM, Synthetix, Zapper, Zerion, 0x.', + ), + ).toBeInTheDocument(); + }); +}); diff --git a/ui/components/app/detected-token/detected-token-aggregators/index.scss b/ui/components/app/detected-token/detected-token-aggregators/index.scss new file mode 100644 index 000000000..37767a3b7 --- /dev/null +++ b/ui/components/app/detected-token/detected-token-aggregators/index.scss @@ -0,0 +1,13 @@ +.detected-token-aggregators { + .typography { + display: inline; + } + + & &__link { + @include H7; + + padding: 0; + display: inline; + margin-left: 4px; + } +} diff --git a/ui/components/app/dropdowns/dropdown.js b/ui/components/app/dropdowns/dropdown.js index 79dc5be2d..18e87d215 100644 --- a/ui/components/app/dropdowns/dropdown.js +++ b/ui/components/app/dropdowns/dropdown.js @@ -17,7 +17,7 @@ export class Dropdown extends Component { const innerStyleDefaults = { borderRadius: '4px', padding: '8px 16px', - background: 'rgba(0, 0, 0, 0.8)', + background: 'var(--color-background-default)', boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', ...innerStyle, }; @@ -35,11 +35,10 @@ export class Dropdown extends Component { {children} @@ -88,7 +87,6 @@ export class DropdownMenuItem extends Component { display: 'flex', justifyContent: 'flex-start', alignItems: 'center', - color: 'white', ...style, }} tabIndex="0" diff --git a/ui/components/app/dropdowns/network-dropdown.js b/ui/components/app/dropdowns/network-dropdown.js index c6c5e6091..832733392 100644 --- a/ui/components/app/dropdowns/network-dropdown.js +++ b/ui/components/app/dropdowns/network-dropdown.js @@ -21,6 +21,8 @@ import { ADD_NETWORK_ROUTE, ADVANCED_ROUTE, } from '../../../helpers/constants/routes'; +import IconCheck from '../../ui/icon/icon-check'; + import { Dropdown, DropdownMenuItem } from './dropdown'; // classes from nodes of the toggle element. @@ -36,7 +38,7 @@ const notToggleElementClassnames = [ const DROP_DOWN_MENU_ITEM_STYLE = { fontSize: '16px', lineHeight: '20px', - padding: '12px 0', + padding: '16px', }; function mapStateToProps(state) { @@ -77,7 +79,7 @@ function mapDispatchToProps(dispatch) { class NetworkDropdown extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -105,15 +107,14 @@ class NetworkDropdown extends Component { provider: { type: providerType }, setProviderType, } = this.props; - const { metricsEvent } = this.context; + const { trackEvent } = this.context; - metricsEvent({ - eventOpts: { - category: 'Navigation', + trackEvent({ + category: 'Navigation', + event: 'Switched Networks', + properties: { action: 'Home', - name: 'Switched Networks', - }, - customVariables: { + legacy_event: true, fromNetwork: providerType, toNetwork: newProviderType, }, @@ -122,32 +123,22 @@ class NetworkDropdown extends Component { } renderAddCustomButton() { - const style = { - width: '100%', - left: '40px', - color: 'white', - background: 'rgba(0, 0, 0, 0.75)', - borderRadius: '20px', - textTransform: 'none', - }; - return ( - +
+ +
); } @@ -159,14 +150,6 @@ class NetworkDropdown extends Component { const isCurrentRpcTarget = provider.type === NETWORK_TYPE_RPC && rpcUrl === provider.rpcUrl; - let borderColor = COLORS.UI2; - if (isCurrentRpcTarget) { - borderColor = COLORS.WHITE; - } - if (opts.isLocalHost) { - borderColor = 'localhost'; - } - return ( {isCurrentRpcTarget ? ( - + ) : (
)} {nickname || rpcUrl} @@ -257,7 +241,7 @@ class NetworkDropdown extends Component { style={DROP_DOWN_MENU_ITEM_STYLE} > {providerType === network ? ( - + ) : (
)} @@ -265,12 +249,14 @@ class NetworkDropdown extends Component { color={network} size={SIZES.LG} type={ColorIndicator.TYPES.FILLED} - borderColor={providerType === network ? COLORS.WHITE : network} /> {this.context.t(network)} @@ -321,7 +307,7 @@ class NetworkDropdown extends Component { zIndex: '55px', }} innerStyle={{ - padding: '18px 8px', + padding: '16px 0', }} >
) : null} diff --git a/ui/components/app/dropdowns/network-dropdown.test.js b/ui/components/app/dropdowns/network-dropdown.test.js index 7a0eaf3ef..1a963c9e8 100644 --- a/ui/components/app/dropdowns/network-dropdown.test.js +++ b/ui/components/app/dropdowns/network-dropdown.test.js @@ -76,7 +76,9 @@ describe('Network Dropdown', () => { let i = 1; let found = false; while (!found) { - if (_wrapper.find(ColorIndicator).at(i).prop('color') === 'ui-2') { + if ( + _wrapper.find(ColorIndicator).at(i).prop('color') === 'text-muted' + ) { i += 1; } else { found = true; @@ -94,14 +96,12 @@ describe('Network Dropdown', () => { it('checks background color for first ColorIndicator', () => { const colorIndicator = wrapper.find(ColorIndicator).at(0); expect(colorIndicator.prop('color')).toStrictEqual('mainnet'); - expect(colorIndicator.prop('borderColor')).toStrictEqual('mainnet'); }); it('checks background color for second ColorIndicator', () => { // find where test networks start in case there are custom RPCs const colorIndicator = wrapper.find(ColorIndicator).at(testNetworkIndex); expect(colorIndicator.prop('color')).toStrictEqual('ropsten'); - expect(colorIndicator.prop('borderColor')).toStrictEqual('ropsten'); }); it('checks background color for third ColorIndicator', () => { @@ -109,7 +109,6 @@ describe('Network Dropdown', () => { .find(ColorIndicator) .at(testNetworkIndex + 1); expect(colorIndicator.prop('color')).toStrictEqual('kovan'); - expect(colorIndicator.prop('borderColor')).toStrictEqual('kovan'); }); it('checks background color for fourth ColorIndicator', () => { @@ -117,7 +116,6 @@ describe('Network Dropdown', () => { .find(ColorIndicator) .at(testNetworkIndex + 2); expect(colorIndicator.prop('color')).toStrictEqual('rinkeby'); - expect(colorIndicator.prop('borderColor')).toStrictEqual('rinkeby'); }); it('checks background color for fifth ColorIndicator', () => { @@ -125,7 +123,6 @@ describe('Network Dropdown', () => { .find(ColorIndicator) .at(testNetworkIndex + 3); expect(colorIndicator.prop('color')).toStrictEqual('goerli'); - expect(colorIndicator.prop('borderColor')).toStrictEqual('goerli'); }); it('checks background color for sixth ColorIndicator', () => { @@ -133,7 +130,6 @@ describe('Network Dropdown', () => { .find(ColorIndicator) .at(testNetworkIndex + 4); expect(colorIndicator.prop('color')).toStrictEqual('localhost'); - expect(colorIndicator.prop('borderColor')).toStrictEqual('localhost'); expect( wrapper .find(DropdownMenuItem) @@ -186,7 +182,6 @@ describe('Network Dropdown', () => { it('checks background color for first ColorIndicator', () => { const colorIndicator = wrapper.find(ColorIndicator).at(0); expect(colorIndicator.prop('color')).toStrictEqual('mainnet'); - expect(colorIndicator.prop('borderColor')).toStrictEqual('mainnet'); expect(wrapper.find(DropdownMenuItem).at(0).text()).toStrictEqual( '✓mainnet', ); diff --git a/ui/components/app/edit-gas-display-education/index.scss b/ui/components/app/edit-gas-display-education/index.scss index f2d8bf308..c37683182 100644 --- a/ui/components/app/edit-gas-display-education/index.scss +++ b/ui/components/app/edit-gas-display-education/index.scss @@ -1,5 +1,5 @@ .edit-gas-display-education { a { - color: var(--primary-1); + color: var(--color-primary-default); } } diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index f7fc433c9..dd1af320a 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -35,8 +35,7 @@ import ActionableMessage from '../../ui/actionable-message/actionable-message'; import { I18nContext } from '../../../contexts/i18n'; import GasTiming from '../gas-timing'; - -import { useMetricEvent } from '../../../hooks/useMetricEvent'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; export default function EditGasDisplay({ mode = EDIT_GAS_MODES.MODIFY_IN_PLACE, @@ -133,14 +132,7 @@ export default function EditGasDisplay({ errorKey = 'gasEstimatesUnavailableWarning'; } - const clickedAdvancedOptionsMetricsEvent = useMetricEvent({ - eventOpts: { - category: 'Transactions', - action: 'Edit Screen', - name: 'Clicked "Advanced Options"', - }, - }); - + const trackEvent = useContext(MetaMetricsContext); return (
@@ -154,7 +146,7 @@ export default function EditGasDisplay({
@@ -164,7 +156,7 @@ export default function EditGasDisplay({
@@ -174,7 +166,7 @@ export default function EditGasDisplay({ @@ -286,7 +278,14 @@ export default function EditGasDisplay({ className="edit-gas-display__advanced-button" onClick={() => { setShowAdvancedForm(!showAdvancedForm); - clickedAdvancedOptionsMetricsEvent(); + trackEvent({ + event: 'Clicked "Advanced Options"', + category: 'Transactions', + properties: { + action: 'Edit Screen', + legacy_event: true, + }, + }); }} > {t('advancedOptions')}{' '} diff --git a/ui/components/app/edit-gas-display/index.scss b/ui/components/app/edit-gas-display/index.scss index 70e35c6a4..3af1e402c 100644 --- a/ui/components/app/edit-gas-display/index.scss +++ b/ui/components/app/edit-gas-display/index.scss @@ -28,8 +28,8 @@ button.edit-gas-display__dapp-acknowledgement-button { margin: 40px auto 0 auto; display: block; - color: var(--secondary-1); - border: 1px solid var(--secondary-1); + color: var(--color-secondary-default); + border: 1px solid var(--color-secondary-default); text-transform: unset; width: auto; background: transparent; @@ -43,7 +43,7 @@ display: block; margin: 0 auto; background: transparent; - color: var(--primary-1); + color: var(--color-primary-default); font-weight: bold; } @@ -58,7 +58,7 @@ display: block; margin: 0 auto; background: transparent; - color: var(--primary-1); + color: var(--color-primary-default); } } diff --git a/ui/components/app/edit-gas-fee-button/index.scss b/ui/components/app/edit-gas-fee-button/index.scss index ada2348fc..52c7cbaf4 100644 --- a/ui/components/app/edit-gas-fee-button/index.scss +++ b/ui/components/app/edit-gas-fee-button/index.scss @@ -8,7 +8,7 @@ display: flex; align-items: baseline; - color: var(--primary-1); + color: var(--color-primary-default); background: transparent; border: 0; padding-inline-end: 0; @@ -16,7 +16,7 @@ } i { - color: var(--primary-1); + color: var(--color-primary-default); margin-right: 2px; } @@ -37,11 +37,11 @@ &__tooltip { p { - color: var(--Grey-500); + color: var(--color-text-alternative); } b { - color: var(--neutral-black); + color: var(--color-text-default); display: inline-block; min-width: 60%; } diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js b/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js index d1b8bc147..e8ffe93e1 100644 --- a/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-item/edit-gas-item.js @@ -87,7 +87,7 @@ const EditGasItem = ({ priorityLevel }) => { }, }); - closeModal('editGasFee'); + closeModal(['editGasFee']); if (priorityLevel === PRIORITY_LEVELS.TEN_PERCENT_INCREASED) { updateTransactionToTenPercentIncreasedGasFee(); @@ -136,7 +136,9 @@ const EditGasItem = ({ priorityLevel }) => {
diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss b/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss index c9bcf1952..ed6fd0035 100644 --- a/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-item/index.scss @@ -1,7 +1,7 @@ .edit-gas-item { border-radius: 24px; - background: white; - color: var(--ui-4); + background: var(--color-background-default); + color: var(--color-text-alternative); cursor: pointer; font-size: 12px; display: flex; @@ -12,11 +12,11 @@ width: 100%; &:hover:not([disabled]) { - background-color: var(--primary-2); + background-color: var(--color-primary-muted); } &--selected { - background-color: var(--ui-1); + background-color: var(--color-background-alternative); } button.edit-gas-item--disabled[disabled] { @@ -27,7 +27,7 @@ &__name { display: inline-flex; align-items: center; - color: var(--ui-black); + color: var(--color-text-default); font-size: 12px; font-weight: bold; white-space: nowrap; @@ -78,7 +78,7 @@ &__time-estimate-low, &__fee-estimate-high { - color: var(--secondary-1); + color: var(--color-secondary-default); } &__time-estimate-medium, diff --git a/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/index.scss b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/index.scss index 05b8d4963..63c1b7e98 100644 --- a/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/index.scss +++ b/ui/components/app/edit-gas-fee-popover/edit-gas-tooltip/index.scss @@ -17,7 +17,7 @@ } &__dialog { - background-color: var(--Orange-500); + background-color: var(--color-secondary-default); border-radius: 30px; margin: 4px 0; text-align: center; diff --git a/ui/components/app/edit-gas-fee-popover/index.scss b/ui/components/app/edit-gas-fee-popover/index.scss index 5319938a3..9d72b840f 100644 --- a/ui/components/app/edit-gas-fee-popover/index.scss +++ b/ui/components/app/edit-gas-fee-popover/index.scss @@ -2,7 +2,7 @@ height: 500px; &__wrapper { - border-top: 1px solid var(--ui-grey); + border-top: 1px solid var(--color-border-default); height: 100%; } @@ -19,7 +19,7 @@ } &__header { - color: var(--ui-4); + color: var(--color-text-alternative); font-size: 10px; font-weight: 700; margin: 0 12px; @@ -41,7 +41,7 @@ } &__separator { - border-top: 1px solid var(--ui-grey); + border-top: 1px solid var(--color-border-default); margin: 8px 12px; } } @@ -51,6 +51,6 @@ } &__know-more a { - color: var(--primary-1); + color: var(--color-primary-default); } } diff --git a/ui/components/app/edit-gas-fee-popover/network-statistics/index.scss b/ui/components/app/edit-gas-fee-popover/network-statistics/index.scss index 3204fbf14..a0a01e411 100644 --- a/ui/components/app/edit-gas-fee-popover/network-statistics/index.scss +++ b/ui/components/app/edit-gas-fee-popover/network-statistics/index.scss @@ -2,8 +2,8 @@ margin: 24px 12px 12px; &__info { - border-top: 1px solid var(--ui-2); - border-bottom: 1px solid var(--ui-2); + border-top: 1px solid var(--color-border-muted); + border-bottom: 1px solid var(--color-border-muted); height: 56px; display: flex; align-items: center; @@ -18,7 +18,7 @@ flex: 1; &:not(:last-child) { - border-right: 1px solid var(--ui-2); + border-right: 1px solid var(--color-border-muted); } } diff --git a/ui/components/app/edit-gas-fee-popover/network-statistics/status-slider/index.scss b/ui/components/app/edit-gas-fee-popover/network-statistics/status-slider/index.scss index 47626d64c..1c295cbb9 100644 --- a/ui/components/app/edit-gas-fee-popover/network-statistics/status-slider/index.scss +++ b/ui/components/app/edit-gas-fee-popover/network-statistics/status-slider/index.scss @@ -5,7 +5,7 @@ width: 56px; &__line { - background-image: linear-gradient(to right, #037dd6, #d73a49); + background-image: linear-gradient(to right, var(--color-primary-default), var(--color-error-default)); height: 4px; width: 100%; border-radius: 100px; diff --git a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js index 211b6ed0a..9d8ca4ac1 100644 --- a/ui/components/app/edit-gas-popover/edit-gas-popover.component.js +++ b/ui/components/app/edit-gas-popover/edit-gas-popover.component.js @@ -27,9 +27,11 @@ import { createCancelTransaction, createSpeedUpTransaction, hideModal, - updateTransaction, + updateTransactionGasFees, updateCustomSwapsEIP1559GasParams, updateSwapsUserFeeLevel, + hideLoadingIndication, + showLoadingIndication, } from '../../../store/actions'; import LoadingHeartBeat from '../../ui/loading-heartbeat'; import { checkNetworkAndAccountSupports1559 } from '../../../selectors'; @@ -134,7 +136,7 @@ export default function EditGasPopover({ } }, [onClose, dispatch]); - const onSubmit = useCallback(() => { + const onSubmit = useCallback(async () => { if (!updatedTransaction || !mode) { closePopover(); } @@ -187,7 +189,14 @@ export default function EditGasPopover({ ); break; case EDIT_GAS_MODES.MODIFY_IN_PLACE: - dispatch(updateTransaction(updatedTxMeta)); + newGasSettings.userEditedGasLimit = updatedTxMeta.userEditedGasLimit; + newGasSettings.userFeeLevel = updatedTxMeta.userFeeLevel; + + dispatch(showLoadingIndication()); + await dispatch( + updateTransactionGasFees(updatedTxMeta.id, newGasSettings), + ); + dispatch(hideLoadingIndication()); break; case EDIT_GAS_MODES.SWAPS: // This popover component should only be used for the "FEE_MARKET" type in Swaps. diff --git a/ui/components/app/flask/experimental-area/index.scss b/ui/components/app/flask/experimental-area/index.scss index 292922755..44a348240 100644 --- a/ui/components/app/flask/experimental-area/index.scss +++ b/ui/components/app/flask/experimental-area/index.scss @@ -52,7 +52,7 @@ button { background-color: var(--flask-purple) !important; border: 0 !important; - color: var(--ui-1); + color: var(--color-primary-inverse); //@TODO: We don't have a generic inverse color, using this one for now width: 200px; } } diff --git a/ui/components/app/flask/snap-remove-warning/index.scss b/ui/components/app/flask/snap-remove-warning/index.scss index cd7a17016..c2a561dad 100644 --- a/ui/components/app/flask/snap-remove-warning/index.scss +++ b/ui/components/app/flask/snap-remove-warning/index.scss @@ -1,5 +1,5 @@ .snap-remove-warning { - color: var(--black); + color: var(--color-text-default); &__footer-button { height: 40px; diff --git a/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.component.js b/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.component.js index 128b8361f..9de986699 100644 --- a/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.component.js +++ b/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.component.js @@ -20,12 +20,12 @@ export default class AdvancedGasInputs extends Component { customGasLimitMessage: PropTypes.string, minimumGasLimit: PropTypes.number, customPriceIsExcessive: PropTypes.bool, - networkSupportsSettingGasPrice: PropTypes.bool, + networkSupportsSettingGasFees: PropTypes.bool, }; static defaultProps = { customPriceIsExcessive: false, - networkSupportsSettingGasPrice: true, + networkSupportsSettingGasFees: true, }; constructor(props) { @@ -202,10 +202,14 @@ export default class AdvancedGasInputs extends Component { customGasLimitMessage, minimumGasLimit, customPriceIsExcessive, - networkSupportsSettingGasPrice, + networkSupportsSettingGasFees, } = this.props; const { gasPrice, gasLimit } = this.state; + if (!networkSupportsSettingGasFees) { + return null; + } + const { errorText: gasPriceErrorText, errorType: gasPriceErrorType, @@ -252,7 +256,7 @@ export default class AdvancedGasInputs extends Component { onChange: this.onChangeGasPrice, errorComponent: gasPriceErrorComponent, errorType: gasPriceErrorType, - disabled: !networkSupportsSettingGasPrice, + disabled: !networkSupportsSettingGasFees, })} {this.renderGasInput({ label: this.context.t('gasLimit'), @@ -263,6 +267,7 @@ export default class AdvancedGasInputs extends Component { errorComponent: gasLimitErrorComponent, customMessageComponent: gasLimitCustomMessageComponent, errorType: gasLimitErrorType, + disabled: !networkSupportsSettingGasFees, })}
); diff --git a/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js b/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js index 314410aa5..eca645f55 100644 --- a/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js +++ b/ui/components/app/gas-customization/advanced-gas-inputs/advanced-gas-inputs.container.js @@ -4,7 +4,7 @@ import { decimalToHex, hexWEIToDecGWEI, } from '../../../../helpers/utils/conversions.util'; -import { getNetworkSupportsSettingGasPrice } from '../../../../selectors/selectors'; +import { getNetworkSupportsSettingGasFees } from '../../../../selectors/selectors'; import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants'; import AdvancedGasInputs from './advanced-gas-inputs.component'; @@ -22,7 +22,7 @@ function convertMinimumGasLimitForInputs(minimumGasLimit = MIN_GAS_LIMIT_DEC) { function mapStateToProps(state) { return { - networkSupportsSettingGasPrice: getNetworkSupportsSettingGasPrice(state), + networkSupportsSettingGasFees: getNetworkSupportsSettingGasFees(state), }; } diff --git a/ui/components/app/gas-customization/advanced-gas-inputs/index.scss b/ui/components/app/gas-customization/advanced-gas-inputs/index.scss index 270b7c29f..a389efc65 100644 --- a/ui/components/app/gas-customization/advanced-gas-inputs/index.scss +++ b/ui/components/app/gas-customization/advanced-gas-inputs/index.scss @@ -13,7 +13,7 @@ &__label { @include H7; - color: #313b5e; + color: var(--color-text-alternative); display: flex; justify-content: space-between; align-items: center; @@ -23,25 +23,25 @@ } .fa-info-circle { - color: var(--silver); + color: var(--color-icon-default); cursor: pointer; } .fa-info-circle:hover { - color: var(--mid-gray); + color: var(--color-icon-muted); } } &__error-text { @include H7; - color: red; + color: var(--color-error-default); } &__warning-text { @include H7; - color: orange; + color: var(--color-secondary-default); } &__input-wrapper { @@ -53,9 +53,10 @@ @include Paragraph; direction: ltr; - border: 1px solid var(--dusty-gray); + border: 1px solid var(--color-border-muted); border-radius: 4px; - color: var(--mid-gray); + background: var(--color-background-default); + color: var(--color-text-alternative); height: 24px; width: 100%; padding-left: 8px; @@ -64,11 +65,11 @@ } &__input--error { - border: 1px solid var(--red); + border: 1px solid var(--color-error-default); } &__input--warning { - border: 1px solid var(--orange); + border: 1px solid var(--color-warning-default); } &__input-arrows { @@ -81,11 +82,11 @@ right: 0; width: 17px; height: 24px; - border: 1px solid #dadada; + border: 1px solid var(--color-border-muted); border-top-right-radius: 4px; display: flex; flex-direction: column; - color: #9b9b9b; + color: var(--color-icon-muted); border-bottom-right-radius: 4px; cursor: pointer; @@ -98,12 +99,12 @@ } &__i-wrap:hover { - background: #4eade7; - color: var(--white); + background: var(--color-primary-default); + color: var(--color-primary-inverse); } i:hover { - background: #4eade7; + background: var(--color-primary-muted); } i { @@ -116,11 +117,11 @@ } &__input-arrows--error { - border: 1px solid var(--red); + border: 1px solid var(--color-error-default); } &__input-arrows--warning { - border: 1px solid var(--orange); + border: 1px solid var(--color-warning-default); } input[type="number"] { @@ -145,7 +146,7 @@ position: absolute; top: 8px; right: 10px; - color: var(--dusty-gray); + color: var(--color-text-muted); } &__custom-text { diff --git a/ui/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss b/ui/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss index d35feac31..920e831c2 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss +++ b/ui/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss @@ -10,7 +10,7 @@ &__transaction-data-summary { display: flex; flex-flow: column; - color: var(--mid-gray); + color: var(--color-text-alternative); margin-top: 12px; padding-left: 18px; padding-right: 18px; @@ -22,7 +22,7 @@ display: flex; flex-flow: row; justify-content: space-between; - color: #888ea3; + color: var(--color-text-muted); } &__container { @@ -34,7 +34,7 @@ &__fee { @include Paragraph; - color: #313a5e; + color: var(--color-text-alternative); } &__time-remaining { @@ -42,7 +42,7 @@ /*rtl:ignore*/ direction: ltr; - color: #313a5e; + color: var(--color-text-alternative); .minutes-num, .seconds-num { @@ -65,15 +65,15 @@ &__fee-chart { margin-top: 8px; height: 265px; - background: #f8f9fb; - border-bottom: 1px solid #d2d8dd; - border-top: 1px solid #d2d8dd; + background: var(--color-background-alternative); + border-bottom: 1px solid var(--color-border-muted); + border-top: 1px solid var(--color-border-muted); position: relative; &__title { @include H7; - color: #313a5e; + color: var(--color-text-alternative); margin-left: 22px; } @@ -87,7 +87,7 @@ padding-left: 20px; padding-right: 19px; width: 100%; - color: #888ea3; + color: var(--color-text-alternative); } .loading-overlay { diff --git a/ui/components/app/gas-customization/gas-modal-page-container/basic-tab-content/index.scss b/ui/components/app/gas-customization/gas-modal-page-container/basic-tab-content/index.scss index ce4b48e31..ab15ad45e 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/basic-tab-content/index.scss +++ b/ui/components/app/gas-customization/gas-modal-page-container/basic-tab-content/index.scss @@ -4,21 +4,21 @@ align-items: flex-start; padding-left: 21px; height: 324px; - background: #f5f7f8; - border-bottom: 1px solid #d2d8dd; + background: var(--color-background-alternative); + border-bottom: 1px solid var(--color-border-default); &__title { @include Paragraph; margin-top: 19px; - color: var(--black); + color: var(--color-text-default); } &__blurb { @include H7; width: 95%; - color: var(--black); + color: var(--color-text-default); margin-top: 5px; margin-bottom: 15px; } @@ -27,7 +27,7 @@ @include H7; width: 95%; - color: #979797; + color: var(--color-text-alternative); margin-top: 15px; } } diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js index 52f2b0fb4..ef78b166a 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-container.test.js @@ -60,7 +60,9 @@ jest.mock('../../../../ducks/gas/gas.duck', () => ({ })); jest.mock('../../../../ducks/send', () => { - const { ASSET_TYPES } = jest.requireActual('../../../../ducks/send'); + const { ASSET_TYPES } = jest.requireActual( + '../../../../../shared/constants/transaction', + ); return { useCustomGas: jest.fn(), updateGasLimit: jest.fn(), diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js index 9bf9ccfa9..e7a3056b4 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js @@ -14,7 +14,6 @@ import BasicTabContent from './basic-tab-content'; export default class GasModalPageContainer extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, trackEvent: PropTypes.func, }; @@ -219,11 +218,12 @@ export default class GasModalPageContainer extends Component { onClose={() => cancelAndClose()} onSubmit={() => { if (isSpeedUp) { - this.context.metricsEvent({ - eventOpts: { - category: 'Navigation', + this.context.trackEvent({ + category: 'Navigation', + event: 'Saved "Speed Up"', + properties: { action: 'Activity Log', - name: 'Saved "Speed Up"', + legacy_event: true, }, }); } diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index 3d77f1214..db2d30768 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -19,7 +19,6 @@ import { updateGasPrice, useCustomGas, getSendAsset, - ASSET_TYPES, } from '../../../../ducks/send'; import { conversionRateSelector as getConversionRate, @@ -54,9 +53,12 @@ import { isBalanceSufficient, } from '../../../../pages/send/send.utils'; import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants'; -import { TRANSACTION_STATUSES } from '../../../../../shared/constants/transaction'; +import { + ASSET_TYPES, + TRANSACTION_STATUSES, +} from '../../../../../shared/constants/transaction'; import { GAS_LIMITS } from '../../../../../shared/constants/gas'; -import { updateTransactionGasFees } from '../../../../ducks/metamask/metamask'; +import { updateGasFees } from '../../../../ducks/metamask/metamask'; import GasModalPageContainer from './gas-modal-page-container.component'; const mapStateToProps = (state, ownProps) => { @@ -207,7 +209,7 @@ const mapDispatchToProps = (dispatch) => { hideModal: () => dispatch(hideModal()), useCustomGas: () => dispatch(useCustomGas()), updateTransactionGasFees: (gasFees) => { - dispatch(updateTransactionGasFees({ ...gasFees, expectHexWei: true })); + dispatch(updateGasFees({ ...gasFees, expectHexWei: true })); }, updateCustomGasPrice, updateCustomGasLimit: (newLimit) => diff --git a/ui/components/app/gas-customization/gas-modal-page-container/index.scss b/ui/components/app/gas-customization/gas-modal-page-container/index.scss index 85b8a981c..70d759d21 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/index.scss +++ b/ui/components/app/gas-customization/gas-modal-page-container/index.scss @@ -33,7 +33,7 @@ &__header-close-text { @include H6; - color: #4eade7; + color: var(--color-primary-default); position: absolute; font-size: 0.75rem; top: 8px; @@ -46,7 +46,7 @@ &__title { @include H5; - color: var(--black); + color: var(--color-text-default); font-weight: 500; display: flex; justify-content: center; @@ -80,7 +80,7 @@ } &.tab--active button { - color: var(--primary-blue); + color: var(--color-primary-default); } } } @@ -101,11 +101,11 @@ @include H7; width: 100%; - background: var(--polar); + background: var(--color-background-alternative); padding: 15px 21px; display: flex; flex-flow: column; - color: var(--scorpion); + color: var(--color-text-alternative); &__send-info, &__transaction-info, @@ -153,8 +153,8 @@ } &__info-row--fade { - background: white; - color: var(--dusty-gray); - border-top: 1px solid var(--mischka); + background: var(--color-background-default); + color: var(--color-text-muted); + border-top: 1px solid var(--color-border-muted); } } diff --git a/ui/components/app/gas-customization/gas-price-button-group/index.scss b/ui/components/app/gas-customization/gas-price-button-group/index.scss index 0619b545e..5573d923d 100644 --- a/ui/components/app/gas-customization/gas-price-button-group/index.scss +++ b/ui/components/app/gas-customization/gas-price-button-group/index.scss @@ -15,7 +15,7 @@ &__time-estimate { margin-top: 5.5px; - color: var(--silver-chalice); + color: var(--color-text-alternative); height: 15.4px; } @@ -34,9 +34,9 @@ display: flex; padding-top: 17px; border-radius: 4px; - border-color: var(--spindle); - background: var(--white); - color: var(--scorpion); + border-color: var(--color-primary-muted); + background: var(--color-background-default); + color: var(--color-text-alternative); div { display: flex; @@ -52,13 +52,13 @@ } .button-group__button--active { - border-color: var(--primary-blue); - color: var(--scorpion); + border-color: var(--color-primary-default); + color: var(--color-text-alternative); i { &:last-child { display: flex; - color: var(--primary-blue); + color: var(--color-primary-default); margin-top: 8px; } } @@ -114,8 +114,8 @@ .button-group__button, .button-group__button--active { - background: white; - color: var(--scorpion); + background: var(--color-background-default); + color: var(--color-text-alternative); padding: 4px; div { @@ -133,13 +133,13 @@ } .button-group__button--active { - color: var(--white); - background: var(--dodger-blue); + color: var(--color-background-default); + background: var(--color-primary-muted); i { &:last-child { display: flex; - color: var(--primary-blue); + color: var(--color-primary-default); margin-top: 10px; } } @@ -185,14 +185,14 @@ font-weight: 500; margin-top: 4px; - color: var(--black); + color: var(--color-text-default); } .button-group__button, .button-group__button--active { height: 78px; - background: white; - color: #2a4055; + background: var(--color-background-default); + color: var(--color-text-default); width: 108px; height: 97px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.151579); @@ -221,11 +221,11 @@ } .button-group__button--active { - background: #f7fcff; - border-color: #2c8bdc; + background: var(--color-background-alternative); + border-color: var(--color-primary-default); &:first-child { - border-color: #2c8bdc; + border-color: var(--color-primary-default); } .button-check-wrapper { @@ -235,7 +235,7 @@ position: absolute; top: -11px; right: -10px; - background: #d5ecfa; + background: var(--color-background-alternative); display: flex; flex-flow: row; justify-content: center; @@ -244,7 +244,7 @@ i { display: flex; - color: var(--primary-blue); + color: var(--color-primary-default); font-weight: 900; } } diff --git a/ui/components/app/gas-customization/gas-slider/index.scss b/ui/components/app/gas-customization/gas-slider/index.scss index d7d1dc055..876c5664b 100644 --- a/ui/components/app/gas-customization/gas-slider/index.scss +++ b/ui/components/app/gas-customization/gas-slider/index.scss @@ -16,7 +16,7 @@ -webkit-appearance: none !important; height: 34px; width: 34px; - background-color: var(--primary-blue); + background-color: var(--color-primary-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); border-radius: 50%; position: relative; @@ -26,7 +26,7 @@ &__bar { height: 6px; width: 322px; - background: var(--alto); + background: var(--color-background-alternative); display: flex; justify-content: space-between; position: absolute; @@ -41,7 +41,7 @@ margin-left: 102px; width: 322px; z-index: 1; - background-color: var(--blizzard-blue); + background-color: var(--color-primary-muted); } &__labels { @@ -50,6 +50,6 @@ display: flex; justify-content: space-between; margin-top: -6px; - color: var(--mid-gray); + color: var(--color-text-alternative); } } diff --git a/ui/components/app/gas-details-item/gas-details-item-title/index.scss b/ui/components/app/gas-details-item/gas-details-item-title/index.scss index eab165081..8c8e4a7ad 100644 --- a/ui/components/app/gas-details-item/gas-details-item-title/index.scss +++ b/ui/components/app/gas-details-item/gas-details-item-title/index.scss @@ -3,7 +3,7 @@ font-weight: 400; font-style: italic; font-size: 12px; - color: var(--Grey-500); + color: var(--color-text-muted); line-height: inherit; } } diff --git a/ui/components/app/gas-details-item/gas-details-item.js b/ui/components/app/gas-details-item/gas-details-item.js index 8c3977f19..bb90fbe4a 100644 --- a/ui/components/app/gas-details-item/gas-details-item.js +++ b/ui/components/app/gas-details-item/gas-details-item.js @@ -36,7 +36,7 @@ const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => { } - detailTitleColor={COLORS.BLACK} + detailTitleColor={COLORS.TEXT_DEFAULT} detailText={
diff --git a/ui/components/app/gas-details-item/index.scss b/ui/components/app/gas-details-item/index.scss index 64c6fcfe3..43b2da44b 100644 --- a/ui/components/app/gas-details-item/index.scss +++ b/ui/components/app/gas-details-item/index.scss @@ -1,6 +1,6 @@ .gas-details-item { &__gas-fee-warning { - color: var(--secondary-1); + color: var(--color-secondary-default); //@TODO: revisit when warning color tokens are revisited } &__currency-container, diff --git a/ui/components/app/gas-timing/index.scss b/ui/components/app/gas-timing/index.scss index bd4e03c3e..ca8d1e989 100644 --- a/ui/components/app/gas-timing/index.scss +++ b/ui/components/app/gas-timing/index.scss @@ -1,26 +1,26 @@ .typography.gas-timing { - color: var(--ui-4); + color: var(--color-text-alternative); &--positive { - color: var(--success-3); + color: var(--color-success-default); } &--positive-V2 { - color: var(--success-3); + color: var(--color-success-default); font-weight: bold; } &--warning { - color: var(--alert-3); + color: var(--color-warning-default); } &--negative { - color: var(--error-1); + color: var(--color-error-default); font-weight: bold; } &--negative-V2 { - color: var(--secondary-1); + color: var(--color-secondary-default); font-weight: bold; } @@ -29,7 +29,7 @@ margin-inline-start: 4px; path { - fill: var(--error-1); + fill: var(--color-error-default); } } } diff --git a/ui/components/app/home-notification/index.scss b/ui/components/app/home-notification/index.scss index 8eea6800f..a8e99db34 100644 --- a/ui/components/app/home-notification/index.scss +++ b/ui/components/app/home-notification/index.scss @@ -2,8 +2,9 @@ display: flex; flex-flow: column; justify-content: space-between; - background: rgba(36, 41, 46, 0.9); - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12); + background: var(--color-background-default); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + border: 1px solid var(--color-border-muted); border-radius: 8px; min-height: 116px; padding: 16px; @@ -25,21 +26,21 @@ &__text { @include H7; - color: var(--white); + color: var(--color-text-default); } &__text-link { @include H7; - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; } .fa-info-circle { - color: #6a737d; + color: var(--color-icon-default); } - & &__checkbox-wrapper { + &__checkbox-wrapper { display: flex; flex-direction: row; align-items: center; @@ -49,18 +50,18 @@ } } - & &__checkbox { + &__checkbox { height: 13px; width: 13px; font-size: 16px; cursor: pointer; } - & &__checkbox-label { + &__checkbox-label { @include H7; - color: var(--white); - margin-left: 10px; + color: var(--color-text-default); + margin-left: 8px; margin-top: 1px; user-select: none; -moz-user-select: none; @@ -68,57 +69,20 @@ cursor: pointer; } - & &__ignore-button { - border-color: #6a737d; - box-sizing: border-box; - color: var(--white); - background-color: inherit; - height: 34px; - width: 155px; - padding: 0; - - @media screen and (max-width: $break-small) { - width: 135px; - } - - &:hover { - border-color: #6a737d; - background-color: #6a737d; - } - - &:active { - background-color: #141618; - } + &__ignore-button.button { + width: auto; + padding: 8px 16px; } - & &__accept-button { - border-color: #6a737d; - box-sizing: border-box; - color: var(--white); - background-color: inherit; - height: 34px; - width: 155px; - padding: 0; - margin-left: 4px; - - @media screen and (max-width: $break-small) { - width: 135px; - } - - &:hover { - border-color: #6a737d; - background-color: #6a737d; - } - - &:active { - background-color: #141618; - } + &__accept-button.button { + width: auto; + padding: 8px 16px; } &__buttons { display: flex; width: 100%; - padding-top: 10px; + padding-top: 8px; align-items: center; justify-content: space-between; flex-direction: row-reverse; @@ -126,6 +90,6 @@ &__tooltip-wrapper { display: flex; - margin-left: 10px; + margin-left: 8px; } } diff --git a/ui/components/app/import-token-link/import-token-link.component.js b/ui/components/app/import-token-link/import-token-link.component.js index 40e153000..4147a9256 100644 --- a/ui/components/app/import-token-link/import-token-link.component.js +++ b/ui/components/app/import-token-link/import-token-link.component.js @@ -1,22 +1,16 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; -import { useMetricEvent } from '../../../hooks/useMetricEvent'; import { useI18nContext } from '../../../hooks/useI18nContext'; import { IMPORT_TOKEN_ROUTE } from '../../../helpers/constants/routes'; import Button from '../../ui/button'; import Box from '../../ui/box/box'; import { TEXT_ALIGN } from '../../../helpers/constants/design-system'; import { detectNewTokens } from '../../../store/actions'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; export default function ImportTokenLink({ isMainnet }) { - const addTokenEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Token Menu', - name: 'Clicked "Add Token"', - }, - }); + const trackEvent = useContext(MetaMetricsContext); const t = useI18nContext(); const history = useHistory(); @@ -39,7 +33,14 @@ export default function ImportTokenLink({ isMainnet }) { type="link" onClick={() => { history.push(IMPORT_TOKEN_ROUTE); - addTokenEvent(); + trackEvent({ + event: 'Clicked "Add Token"', + category: 'Navigation', + properties: { + action: 'Token Menu', + legacy_event: true, + }, + }); }} > {isMainnet diff --git a/ui/components/app/info-box/index.scss b/ui/components/app/info-box/index.scss index 7fc763d3b..37a29a95c 100644 --- a/ui/components/app/info-box/index.scss +++ b/ui/components/app/info-box/index.scss @@ -1,17 +1,17 @@ .info-box { border-radius: 4px; - background-color: var(--alabaster); + background-color: var(--color-background-alternative); position: relative; padding: 16px; display: flex; flex-flow: column; - color: var(--mid-gray); + color: var(--color-text-alternative); &__close::after { content: '\00D7'; font-size: 29px; font-weight: 200; - color: var(--dusty-gray); + color: var(--color-icon-default); position: absolute; right: 12px; top: 0; diff --git a/ui/components/app/info-box/info-box.stories.js b/ui/components/app/info-box/info-box.stories.js new file mode 100644 index 000000000..26dc006b7 --- /dev/null +++ b/ui/components/app/info-box/info-box.stories.js @@ -0,0 +1,22 @@ +import React from 'react'; + +import InfoBox from '.'; + +export default { + title: 'Components/App/InfoBox', + id: __filename, + component: InfoBox, + argTypes: { + title: 'string', + description: 'string', + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; + +DefaultStory.args = { + title: 'Hello Ether', + description: 'This is the description', +}; diff --git a/ui/components/app/ledger-instruction-field/ledger-instruction-field.js b/ui/components/app/ledger-instruction-field/ledger-instruction-field.js index 287db2c8e..09ffafe32 100644 --- a/ui/components/app/ledger-instruction-field/ledger-instruction-field.js +++ b/ui/components/app/ledger-instruction-field/ledger-instruction-field.js @@ -35,7 +35,11 @@ import { import { getLedgerTransportType } from '../../../ducks/metamask/metamask'; import { attemptLedgerTransportCreation } from '../../../store/actions'; -const renderInstructionStep = (text, show = true, color = COLORS.PRIMARY3) => { +const renderInstructionStep = ( + text, + show = true, + color = COLORS.PRIMARY_DEFAULT, +) => { return ( show && ( diff --git a/ui/components/app/menu-bar/account-options-menu.js b/ui/components/app/menu-bar/account-options-menu.js index 5e83f8aec..9e83f3788 100644 --- a/ui/components/app/menu-bar/account-options-menu.js +++ b/ui/components/app/menu-bar/account-options-menu.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; @@ -15,12 +15,9 @@ import { getSelectedIdentity, } from '../../../selectors'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { - useMetricEvent, - useNewMetricEvent, -} from '../../../hooks/useMetricEvent'; import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../shared/constants/app'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; export default function AccountOptionsMenu({ anchorElement, onClose }) { const t = useI18nContext(); @@ -35,39 +32,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) { const addressLink = getAccountLink(address, chainId, rpcPrefs); const { blockExplorerUrl } = rpcPrefs; const blockExplorerUrlSubTitle = getURLHostName(blockExplorerUrl); - - const openFullscreenEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Account Options', - name: 'Clicked Expand View', - }, - }); - const viewAccountDetailsEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Account Options', - name: 'Viewed Account Details', - }, - }); - - const openConnectedSitesEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Account Options', - name: 'Opened Connected Sites', - }, - }); - - const blockExplorerLinkClickedEvent = useNewMetricEvent({ - category: 'Navigation', - event: 'Clicked Block Explorer Link', - properties: { - link_type: 'Account Tracker', - action: 'Account Options', - block_explorer_domain: getURLHostName(addressLink), - }, - }); + const trackEvent = useContext(MetaMetricsContext); const isRemovable = keyring.type !== 'HD Key Tree'; @@ -79,7 +44,15 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) { > { - blockExplorerLinkClickedEvent(); + trackEvent({ + event: 'Clicked Block Explorer Link', + category: 'Navigation', + properties: { + link_type: 'Account Tracker', + action: 'Account Options', + block_explorer_domain: getURLHostName(addressLink), + }, + }); global.platform.openTab({ url: addressLink, }); @@ -101,7 +74,14 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) { {getEnvironmentType() === ENVIRONMENT_TYPE_FULLSCREEN ? null : ( { - openFullscreenEvent(); + trackEvent({ + event: 'Clicked Expand View', + category: 'Navigation', + properties: { + action: 'Account Options', + legacy_event: true, + }, + }); global.platform.openExtensionInBrowser(); onClose(); }} @@ -114,7 +94,14 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) { data-testid="account-options-menu__account-details" onClick={() => { dispatch(showModal({ name: 'ACCOUNT_DETAILS' })); - viewAccountDetailsEvent(); + trackEvent({ + event: 'Viewed Account Details', + category: 'Navigation', + properties: { + action: 'Account Options', + legacy_event: true, + }, + }); onClose(); }} iconClassName="fas fa-qrcode" @@ -124,11 +111,18 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) { { - openConnectedSitesEvent(); + trackEvent({ + event: 'Opened Connected Sites', + category: 'Navigation', + properties: { + action: 'Account Options', + legacy_event: true, + }, + }); history.push(CONNECTED_ROUTE); onClose(); }} - iconClassName="account-options-menu__connected-sites" + iconClassName="fa fa-bullseye" > {t('connectedSites')} diff --git a/ui/components/app/menu-bar/accoutn-options-menu.stories.js b/ui/components/app/menu-bar/accoutn-options-menu.stories.js new file mode 100644 index 000000000..daebb7bab --- /dev/null +++ b/ui/components/app/menu-bar/accoutn-options-menu.stories.js @@ -0,0 +1,19 @@ +import React from 'react'; +import AccountOptionsMenu from '.'; + +export default { + title: 'Components/App/AccountOptionsMenu', + id: __filename, + argTypes: { + anchorElement: { + control: 'func', + }, + onClose: { + action: 'onClose', + }, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/menu-bar/index.scss b/ui/components/app/menu-bar/index.scss index 11f0b14da..cced9f864 100644 --- a/ui/components/app/menu-bar/index.scss +++ b/ui/components/app/menu-bar/index.scss @@ -3,7 +3,7 @@ grid-template-columns: 30% minmax(30%, 1fr) 30%; column-gap: 5px; padding: 0 8px; - border-bottom: 1px solid var(--Grey-100); + border-bottom: 1px solid var(--color-border-muted); height: 64px; .menu-bar__account-options { @@ -11,6 +11,7 @@ font-size: inherit; padding: 0 8px 0 5px; place-self: center end; + color: var(--color-text-default); } .selected-account { @@ -20,18 +21,9 @@ } .account-options-menu { - &__connected-sites::before { - content: ""; - background-image: url(/images/icons/connected-sites.svg); - background-size: contain; - background-repeat: no-repeat; - background-position: center; - padding: 8px; - } - &__explorer-origin { @include H7; - color: var(--Grey-500); + color: var(--color-text-alternative); } } diff --git a/ui/components/app/menu-bar/menu-bar.js b/ui/components/app/menu-bar/menu-bar.js index b5e3fdb7c..9d8af1066 100644 --- a/ui/components/app/menu-bar/menu-bar.js +++ b/ui/components/app/menu-bar/menu-bar.js @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; -import extension from 'extensionizer'; +import React, { useState, useContext } from 'react'; +import browser from 'webextension-polyfill'; import { useHistory } from 'react-router-dom'; import { useSelector } from 'react-redux'; import SelectedAccount from '../selected-account'; @@ -8,19 +8,13 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util'; import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app'; import { CONNECTED_ACCOUNTS_ROUTE } from '../../../helpers/constants/routes'; import { useI18nContext } from '../../../hooks/useI18nContext'; -import { useMetricEvent } from '../../../hooks/useMetricEvent'; import { getOriginOfCurrentTab } from '../../../selectors'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; import AccountOptionsMenu from './account-options-menu'; export default function MenuBar() { const t = useI18nContext(); - const openAccountOptionsEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Home', - name: 'Opened Account Options', - }, - }); + const trackEvent = useContext(MetaMetricsContext); const history = useHistory(); const [ accountOptionsButtonElement, @@ -32,7 +26,7 @@ export default function MenuBar() { const showStatus = getEnvironmentType() === ENVIRONMENT_TYPE_POPUP && origin && - origin !== extension.runtime.id; + origin !== browser.runtime.id; return (
@@ -50,7 +44,14 @@ export default function MenuBar() { ref={setAccountOptionsButtonElement} title={t('accountOptions')} onClick={() => { - openAccountOptionsEvent(); + trackEvent({ + event: 'Opened Account Options', + category: 'Navigation', + properties: { + action: 'Home', + legacy_event: true, + }, + }); setAccountOptionsMenuOpen(true); }} /> diff --git a/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js b/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js index 9abcb9707..511a6bb56 100644 --- a/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js +++ b/ui/components/app/metamask-template-renderer/metamask-template-renderer.stories.js @@ -13,7 +13,7 @@ const SECTIONS = { props: { margin: 4, padding: 8, - borderColor: COLORS.PRIMARY1, + borderColor: COLORS.PRIMARY_DEFAULT, borderWidth: 2, }, children: [ @@ -22,7 +22,7 @@ const SECTIONS = { key: 'A Test String', children: 'A Test String', props: { - color: COLORS.UI3, + color: COLORS.TEXT_MUTED, variant: TYPOGRAPHY.H2, }, }, @@ -31,7 +31,7 @@ const SECTIONS = { key: 'Some more text', children: 'Some more text as a paragraph', props: { - color: COLORS.UI4, + color: COLORS.TEXT_ALTERNATIVE, variant: TYPOGRAPHY.Paragraph, }, }, diff --git a/ui/components/app/modal/index.scss b/ui/components/app/modal/index.scss index 10355b42f..858409a92 100644 --- a/ui/components/app/modal/index.scss +++ b/ui/components/app/modal/index.scss @@ -3,7 +3,7 @@ .modal-container { width: 100%; height: 100%; - background-color: #fff; + background-color: var(--color-background-default); display: flex; flex-flow: column; border-radius: 8px; @@ -28,14 +28,14 @@ display: flex; padding: 12px; justify-content: center; - border-bottom: 1px solid #d2d8dd; + border-bottom: 1px solid var(--color-border-muted); flex: 0 0 auto; } &__header-close::after { content: '\00D7'; font-size: 40px; - color: var(--dusty-gray); + color: var(--color-icon-default); position: absolute; top: -5px; right: 10px; @@ -46,7 +46,7 @@ display: flex; flex-flow: row; justify-content: center; - border-top: 1px solid #d2d8dd; + border-top: 1px solid var(--color-border-muted); padding: 16px; flex: 0 0 auto; diff --git a/ui/components/app/modals/account-details-modal/index.scss b/ui/components/app/modals/account-details-modal/index.scss index 0ccb9851b..03e577354 100644 --- a/ui/components/app/modals/account-details-modal/index.scss +++ b/ui/components/app/modals/account-details-modal/index.scss @@ -15,7 +15,7 @@ width: 100%; height: 1px; margin: 16px 0 8px 0; - background-color: var(--alto); + background-color: var(--color-border-muted); } & .qr-header { @@ -27,13 +27,4 @@ & .qr-wrapper { margin-top: 5px; } - - & .ellip-address-wrapper { - display: flex; - justify-content: center; - border: 1px solid var(--alto); - padding: 5px 10px; - margin-top: 7px; - width: 286px; - } } diff --git a/ui/components/app/modals/account-modal-container/index.scss b/ui/components/app/modals/account-modal-container/index.scss index 630cb4535..9e621e7d2 100644 --- a/ui/components/app/modals/account-modal-container/index.scss +++ b/ui/components/app/modals/account-modal-container/index.scss @@ -7,12 +7,12 @@ align-items: center; position: relative; padding: 5px 0 31px 0; - border: 1px solid var(--silver); + border: 1px solid var(--color-border-default); border-radius: 4px; } &__back { - color: var(--dusty-gray); + color: var(--color-icon-default); position: absolute; top: 13px; left: 17px; @@ -31,7 +31,7 @@ @include H1; background-color: transparent; - color: var(--ui-black); + color: var(--color-text-default); position: absolute; cursor: pointer; top: -10px; diff --git a/ui/components/app/modals/add-to-addressbook-modal/add-to-addressbook-modal.component.js b/ui/components/app/modals/add-to-addressbook-modal/add-to-addressbook-modal.component.js deleted file mode 100644 index 70661e3c7..000000000 --- a/ui/components/app/modals/add-to-addressbook-modal/add-to-addressbook-modal.component.js +++ /dev/null @@ -1,75 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Button from '../../../ui/button/button.component'; - -export default class AddToAddressBookModal extends Component { - static contextTypes = { - t: PropTypes.func, - }; - - static propTypes = { - hideModal: PropTypes.func.isRequired, - addToAddressBook: PropTypes.func.isRequired, - recipient: PropTypes.string.isRequired, - }; - - state = { - alias: '', - }; - - onSave = async () => { - const { recipient, addToAddressBook, hideModal } = this.props; - await addToAddressBook(recipient, this.state.alias); - hideModal(); - }; - - onChange = (e) => { - this.setState({ - alias: e.target.value, - }); - }; - - onKeyPress = async (e) => { - if (e.key === 'Enter' && this.state.alias) { - this.onSave(); - } - }; - - render() { - const { t } = this.context; - - return ( -
-
-
- {t('addToAddressBook')} -
-
- {t('enterAnAlias')} -
- -
-
- - -
-
- ); - } -} diff --git a/ui/components/app/modals/add-to-addressbook-modal/add-to-addressbook-modal.container.js b/ui/components/app/modals/add-to-addressbook-modal/add-to-addressbook-modal.container.js deleted file mode 100644 index 8d220a02a..000000000 --- a/ui/components/app/modals/add-to-addressbook-modal/add-to-addressbook-modal.container.js +++ /dev/null @@ -1,22 +0,0 @@ -import { connect } from 'react-redux'; -import * as actions from '../../../../store/actions'; -import AddToAddressBookModal from './add-to-addressbook-modal.component'; - -function mapStateToProps(state) { - return { - ...(state.appState.modal.modalState.props || {}), - }; -} - -function mapDispatchToProps(dispatch) { - return { - hideModal: () => dispatch(actions.hideModal()), - addToAddressBook: (recipient, nickname) => - dispatch(actions.addToAddressBook(recipient, nickname)), - }; -} - -export default connect( - mapStateToProps, - mapDispatchToProps, -)(AddToAddressBookModal); diff --git a/ui/components/app/modals/add-to-addressbook-modal/index.js b/ui/components/app/modals/add-to-addressbook-modal/index.js deleted file mode 100644 index d35d21b10..000000000 --- a/ui/components/app/modals/add-to-addressbook-modal/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './add-to-addressbook-modal.container'; diff --git a/ui/components/app/modals/add-to-addressbook-modal/index.scss b/ui/components/app/modals/add-to-addressbook-modal/index.scss deleted file mode 100644 index 53a7bba1d..000000000 --- a/ui/components/app/modals/add-to-addressbook-modal/index.scss +++ /dev/null @@ -1,48 +0,0 @@ -.add-to-address-book-modal { - display: flex; - flex-flow: column nowrap; - background-color: var(--white); - border-radius: 10px; - box-shadow: 0 5px 16px rgba($black, 0.25); - - &__content { - @extend %col-nowrap; - - padding: 1.5rem; - border-bottom: 1px solid var(--Grey-100); - - &__header { - @include H3; - } - } - - &__input-label { - color: var(--Grey-600); - margin-top: 1.25rem; - } - - &__input { - @include H4; - - background: var(--white); - border: 1px solid var(--Grey-100); - box-sizing: border-box; - border-radius: 8px; - padding: 0.625rem 0.75rem; - margin-top: 0.75rem; - - &::placeholder { - color: var(--Grey-300); - } - } - - &__footer { - @extend %row-nowrap; - - padding: 1rem; - - button + button { - margin-left: 1rem; - } - } -} diff --git a/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/index.scss b/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/index.scss index d9b2d2734..2184ffd56 100644 --- a/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/index.scss +++ b/ui/components/app/modals/cancel-transaction/cancel-transaction-gas-fee/index.scss @@ -1,5 +1,5 @@ .cancel-transaction-gas-fee { - background: #f1f4f9; + background: var(--color-background-alternative); padding: 16px; display: flex; flex-direction: column; diff --git a/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js index 891e40f87..a4628cec7 100644 --- a/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js +++ b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.component.js @@ -53,7 +53,6 @@ export default class ConfirmRemoveAccount extends Component {
diff --git a/ui/components/app/modals/confirm-remove-account/confirm-remove-account.stories.js b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.stories.js new file mode 100644 index 000000000..7bd6779aa --- /dev/null +++ b/ui/components/app/modals/confirm-remove-account/confirm-remove-account.stories.js @@ -0,0 +1,42 @@ +import React from 'react'; +import ConfirmRemoveAccount from '.'; + +export default { + title: 'Components/App/Modals/ConfirmRemoveAccount', + id: __filename, + component: ConfirmRemoveAccount, + argTypes: { + hideModal: { + action: 'hideModal', + }, + removeAccount: { + action: 'removeAccount', + }, + identity: { + control: 'object', + }, + chainId: { + control: 'text', + }, + rpcPrefs: { + control: 'object', + }, + }, + args: { + identity: { + control: 'object', + }, + chainId: 'chainId', + rpcPrefs: { + control: 'object', + }, + }, +}; + +const Template = (args) => { + return ; +}; + +export const DefaultStory = Template.bind({}); + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/modals/confirm-remove-account/index.scss b/ui/components/app/modals/confirm-remove-account/index.scss index e30673843..939198dee 100644 --- a/ui/components/app/modals/confirm-remove-account/index.scss +++ b/ui/components/app/modals/confirm-remove-account/index.scss @@ -6,7 +6,7 @@ } &__account { - border: 1px solid #b7b7b7; + border: 1px solid var(--color-border-default); border-radius: 4px; padding: 10px; display: flex; @@ -36,16 +36,11 @@ @include H8; display: block; - color: #9b9b9b; + color: var(--color-text-muted); } &__link { margin-top: 14px; - - img { - width: 15px; - height: 15px; - } } @media screen and (max-width: $break-small) { @@ -56,6 +51,6 @@ } &__link { - color: #2f9ae0; + color: var(--color-primary-default); } } diff --git a/ui/components/app/modals/customize-nonce/index.scss b/ui/components/app/modals/customize-nonce/index.scss index ae74a7844..fb7a5656b 100644 --- a/ui/components/app/modals/customize-nonce/index.scss +++ b/ui/components/app/modals/customize-nonce/index.scss @@ -17,7 +17,7 @@ &__close { @include H4; - color: var(--ui-black); + color: var(--color-icon-default); background: none; flex: 0; align-self: flex-start; diff --git a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js b/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js index 518e3b90a..90f707f75 100644 --- a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js +++ b/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.component.js @@ -5,11 +5,15 @@ import { BUYABLE_CHAINS_MAP, } from '../../../../../shared/constants/network'; import Button from '../../../ui/button'; +import LogoMoonPay from '../../../ui/logo/logo-moonpay'; +import LogoWyre from '../../../ui/logo/logo-wyre'; +import LogoTransak from '../../../ui/logo/logo-transak'; +import LogoDepositEth from '../../../ui/logo/logo-deposit-eth'; export default class DepositEtherModal extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func.isRequired, + trackEvent: PropTypes.func.isRequired, }; static propTypes = { @@ -17,8 +21,10 @@ export default class DepositEtherModal extends Component { isTestnet: PropTypes.bool.isRequired, isMainnet: PropTypes.bool.isRequired, isBuyableTransakChain: PropTypes.bool.isRequired, + isBuyableMoonPayChain: PropTypes.bool.isRequired, toWyre: PropTypes.func.isRequired, toTransak: PropTypes.func.isRequired, + toMoonPay: PropTypes.func.isRequired, address: PropTypes.string.isRequired, toFaucet: PropTypes.func.isRequired, hideWarning: PropTypes.func.isRequired, @@ -93,11 +99,13 @@ export default class DepositEtherModal extends Component { chainId, toWyre, toTransak, + toMoonPay, address, toFaucet, isTestnet, isMainnet, isBuyableTransakChain, + isBuyableMoonPayChain, } = this.props; const { t } = this.context; const networkName = NETWORK_TO_NAME_MAP[chainId]; @@ -123,15 +131,42 @@ export default class DepositEtherModal extends Component {
{this.renderRow({ - logo: ( -
- ), + logo: , + title: t('buyCryptoWithTransak', [symbol]), + text: t('buyCryptoWithTransakDescription', [symbol]), + buttonLabel: t('continueToTransak'), + onButtonClick: () => { + this.context.trackEvent({ + category: 'Accounts', + event: 'Click buy Ether via Transak', + properties: { + action: 'Deposit Ether', + legacy_event: true, + }, + }); + toTransak(address, chainId); + }, + hide: !isBuyableTransakChain, + })} + {this.renderRow({ + logo: , + title: t('buyCryptoWithMoonPay', [symbol]), + text: t('buyCryptoWithMoonPayDescription', [symbol]), + buttonLabel: t('continueToMoonPay'), + onButtonClick: () => { + this.context.metricsEvent({ + eventOpts: { + category: 'Accounts', + action: 'Deposit tokens', + name: 'Click buy tokens via MoonPay', + }, + }); + toMoonPay(address, chainId); + }, + hide: !isBuyableMoonPayChain, + })} + {this.renderRow({ + logo: , title: t('buyWithWyre'), text: t('buyWithWyreDescription'), buttonLabel: t('continueToWyre'), @@ -149,40 +184,7 @@ export default class DepositEtherModal extends Component { })} {this.renderRow({ logo: ( -
- ), - title: t('buyCryptoWithTransak', [symbol]), - text: t('buyCryptoWithTransakDescription', [symbol]), - buttonLabel: t('continueToTransak'), - onButtonClick: () => { - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', - action: 'Deposit Ether', - name: 'Click buy Ether via Transak', - }, - }); - toTransak(address, chainId); - }, - hide: !isBuyableTransakChain, - })} - {this.renderRow({ - logo: ( - + ), title: t('directDepositCrypto', [symbol]), text: t('directDepositCryptoExplainer', [symbol]), diff --git a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js b/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js index c6eba37b6..227e63047 100644 --- a/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js +++ b/ui/components/app/modals/deposit-ether-modal/deposit-ether-modal.container.js @@ -11,6 +11,7 @@ import { getCurrentChainId, getSelectedAddress, getIsBuyableTransakChain, + getIsBuyableMoonPayChain, } from '../../../../selectors/selectors'; import DepositEtherModal from './deposit-ether-modal.component'; @@ -21,6 +22,7 @@ function mapStateToProps(state) { isMainnet: getIsMainnet(state), address: getSelectedAddress(state), isBuyableTransakChain: getIsBuyableTransakChain(state), + isBuyableMoonPayChain: getIsBuyableMoonPayChain(state), }; } @@ -32,6 +34,9 @@ function mapDispatchToProps(dispatch) { toTransak: (address, chainId) => { dispatch(buyEth({ service: 'transak', address, chainId })); }, + toMoonPay: (address, chainId) => { + dispatch(buyEth({ service: 'moonpay', address, chainId })); + }, hideModal: () => { dispatch(hideModal()); }, diff --git a/ui/components/app/modals/deposit-ether-modal/index.scss b/ui/components/app/modals/deposit-ether-modal/index.scss index 212c4a679..007e0b581 100644 --- a/ui/components/app/modals/deposit-ether-modal/index.scss +++ b/ui/components/app/modals/deposit-ether-modal/index.scss @@ -4,40 +4,6 @@ flex-flow: column; height: 100%; - &__header { - width: 100%; - border-radius: 8px 8px 0 0; - background-color: var(--mid-gray); - display: flex; - position: relative; - padding: 25px; - flex-flow: column; - align-items: flex-start; - - &__title { - @include H3; - - color: var(--white); - } - - &__description { - @include Paragraph; - - color: var(--white); - margin-top: 10px; - } - - &__close::after { - content: '\00D7'; - font-size: 2em; - color: var(--white); - position: absolute; - top: 20.8px; - right: 28px; - cursor: pointer; - } - } - &__buy-rows { width: 100%; padding: 0 30px; @@ -52,7 +18,7 @@ } &__logo { - height: 60px; + max-height: 40px; background-repeat: no-repeat; background-size: contain; background-position: center; @@ -60,10 +26,18 @@ display: flex; justify-content: center; align-items: center; + + &--lg { + max-height: 60px; + } + + @media screen and (min-width: $break-large) { + height: 60px; + } } &__buy-row { - border-bottom: 1px solid var(--alto); + border-bottom: 1px solid var(--color-border-default); display: flex; justify-content: space-between; align-items: center; @@ -105,7 +79,7 @@ } &__description { - color: var(--cape-cod); + color: var(--color-text-alternative); padding-bottom: 20px; align-self: flex-start; diff --git a/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js b/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js index 373d2c1d3..73f81ed0c 100644 --- a/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js +++ b/ui/components/app/modals/edit-approval-permission/edit-approval-permission.component.js @@ -54,8 +54,8 @@ export default class EditApprovalPermission extends PureComponent {
{t('editPermission')}
-
hideModal()} />
diff --git a/ui/components/app/modals/edit-approval-permission/index.scss b/ui/components/app/modals/edit-approval-permission/index.scss index d604b3ec3..e6794d131 100644 --- a/ui/components/app/modals/edit-approval-permission/index.scss +++ b/ui/components/app/modals/edit-approval-permission/index.scss @@ -7,7 +7,7 @@ justify-content: center; align-items: center; position: relative; - border-bottom: 1px solid #d2d8dd; + border-bottom: 1px solid var(--color-border-muted); } &__header { @@ -16,10 +16,8 @@ &__close { position: absolute; right: 24px; - background-image: url("/images/close-gray.svg"); - width: 0.75rem; - height: 0.75rem; cursor: pointer; + display: block; } } @@ -38,7 +36,7 @@ @include H6; font-weight: normal; - color: #24292e; + color: var(--color-text-default); } &__account { @@ -52,7 +50,7 @@ } &__balance { - color: #6a737d; + color: var(--color-text-alternative); margin-left: 8px; } } @@ -64,14 +62,14 @@ @include H6; font-weight: bold; - color: #24292e; + color: var(--color-text-default); } &__description { @include H7; font-weight: normal; - color: #6a737d; + color: var(--color-text-alternative); margin-top: 8px; } @@ -95,17 +93,17 @@ @include H6; font-weight: normal; - color: #474b4d; + color: var(--color-text-alternative); } &__option-label--selected { - color: var(--primary-blue); + color: var(--color-primary-default); } &__option-description { @include H7; - color: #6a737d; + color: var(--color-text-alternative); margin-top: 8px; margin-bottom: 6px; } @@ -113,7 +111,7 @@ &__option-value { @include H4; - color: #24292e; + color: var(--color-text-default); } &__radio-button { @@ -130,19 +128,19 @@ &__radio-button-outline--selected { width: 18px; height: 18px; - background: #dadcdd; + background: var(--color-border-default); border-radius: 9px; position: absolute; } &__radio-button-outline--selected { - background: var(--primary-blue); + background: var(--color-primary-default); } &__radio-button-fill { width: 14px; height: 14px; - background: white; + background: var(--color-background-default); border-radius: 7px; position: absolute; } @@ -150,7 +148,7 @@ &__radio-button-dot { width: 8px; height: 8px; - background: var(--primary-blue); + background: var(--color-primary-default); border-radius: 4px; position: absolute; } diff --git a/ui/components/app/modals/export-private-key-modal/index.scss b/ui/components/app/modals/export-private-key-modal/index.scss index d4debed95..27b7c55f2 100644 --- a/ui/components/app/modals/export-private-key-modal/index.scss +++ b/ui/components/app/modals/export-private-key-modal/index.scss @@ -10,7 +10,7 @@ width: 100%; height: 1px; margin: 19px 0 8px 0; - background-color: var(--alto); + background-color: var(--color-border-default); } &__account-name { @@ -28,12 +28,12 @@ &__password--error { @include H6; - color: var(--scorpion); + color: var(--color-text-muted); margin-bottom: 10px; } &__password--error { - color: var(--crimson); + color: var(--color-error-default); margin-bottom: 0; } @@ -43,19 +43,23 @@ padding: 10px 0 13px 17px; width: 291px; height: 44px; + background: var(--color-background-default); + color: var(--color-text-default); + border: 1px solid var(--color-border-default); } &__password::-webkit-input-placeholder { - color: var(--dusty-gray); + color: var(--color-text-muted); } &__password--warning { @include H7; border-radius: 8px; - background-color: #fff6f6; + background-color: var(--color-error-muted); font-weight: 500; - color: var(--crimson); + color: var(--color-text-default); + border: 1px solid var(--color-error-default); width: 292px; padding: 9px 15px; margin-top: 18px; @@ -64,14 +68,14 @@ &__password-display-wrapper { height: 80px; width: 291px; - border: 1px solid var(--silver); + border: 1px solid var(--color-border-default); border-radius: 2px; } &__password-display-textarea { @include Paragraph; - color: var(--crimson); + color: var(--color-error-default); border: none; height: 75px; width: 100%; @@ -98,13 +102,19 @@ margin-right: 15px; } - & .ellip-address-wrapper { + .ellip-address-wrapper { display: flex; justify-content: center; - border: 1px solid var(--alto); + border: 1px solid var(--color-border-default); padding: 5px 10px; margin-top: 7px; width: 286px; + + input { + background: var(--color-background-default); + color: var(--color-text-default); + border: 0; + } } } diff --git a/ui/components/app/modals/hide-token-confirmation-modal/index.scss b/ui/components/app/modals/hide-token-confirmation-modal/index.scss index efcbc4444..90e9dbc32 100644 --- a/ui/components/app/modals/hide-token-confirmation-modal/index.scss +++ b/ui/components/app/modals/hide-token-confirmation-modal/index.scss @@ -1,7 +1,7 @@ .hide-token-confirmation { min-height: 250.72px; border-radius: 4px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 1px 7px 0 rgba(0, 0, 0, 0.5); &__container { @@ -18,7 +18,7 @@ &__symbol { @include Paragraph; - color: var(--tundora); + color: var(--color-text-alternative); text-align: center; margin-bottom: 7.5px; } @@ -28,7 +28,7 @@ height: 30px; width: 271.28px; - color: var(--tundora); + color: var(--color-text-alternative); text-align: center; margin-bottom: 10.5px; } @@ -38,7 +38,7 @@ min-height: 41px; width: 318px; - color: var(--scorpion); + color: var(--color-text-alternative); text-align: center; } diff --git a/ui/components/app/modals/index.scss b/ui/components/app/modals/index.scss index 0ca2954e6..286322a76 100644 --- a/ui/components/app/modals/index.scss +++ b/ui/components/app/modals/index.scss @@ -1,6 +1,5 @@ @import 'account-details-modal/index'; @import 'account-modal-container/index'; -@import 'add-to-addressbook-modal/index'; @import 'cancel-transaction/index'; @import 'confirm-remove-account/index'; @import 'deposit-ether-modal/index'; @@ -24,7 +23,7 @@ &__content { margin: 0; - background-color: white; + background-color: var(--color-background-default); animation-fill-mode: forwards; } @@ -35,7 +34,7 @@ bottom: 0; left: 0; z-index: 1040; - background-color: #373a47; + background-color: var(--color-overlay-alterantive); animation-fill-mode: forwards; animation-duration: 0.3s; } diff --git a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js index 847a57bf6..b80cc894c 100644 --- a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js +++ b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.component.js @@ -10,12 +10,12 @@ export default class MetaMetricsOptInModal extends Component { }; static contextTypes = { - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, t: PropTypes.func, }; render() { - const { metricsEvent, t } = this.context; + const { trackEvent, t } = this.context; const { setParticipateInMetaMetrics, hideModal } = this.props; return ( @@ -108,16 +108,17 @@ export default class MetaMetricsOptInModal extends Component { { setParticipateInMetaMetrics(false).then(() => { - metricsEvent( + trackEvent( { - eventOpts: { - category: 'Onboarding', + category: 'Onboarding', + event: 'Metrics Opt Out', + properties: { action: 'Metrics Option', - name: 'Metrics Opt Out', + legacy_event: true, }, - isOptIn: true, }, { + isOptIn: true, excludeMetaMetricsId: true, }, ); @@ -128,14 +129,19 @@ export default class MetaMetricsOptInModal extends Component { hideCancel={false} onSubmit={() => { setParticipateInMetaMetrics(true).then(() => { - metricsEvent({ - eventOpts: { + trackEvent( + { category: 'Onboarding', - action: 'Metrics Option', - name: 'Metrics Opt In', + event: 'Metrics Opt In', + properties: { + action: 'Metrics Option', + legacy_event: true, + }, }, - isOptIn: true, - }); + { + isOptIn: true, + }, + ); hideModal(); }); }} diff --git a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js index ff6650652..3af10b36b 100644 --- a/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js +++ b/ui/components/app/modals/metametrics-opt-in-modal/metametrics-opt-in-modal.test.js @@ -16,7 +16,7 @@ describe('MetaMetrics Opt In', () => { beforeEach(() => { wrapper = mount(, { context: { - metricsEvent: () => undefined, + trackEvent: () => undefined, t: (key) => messages[key].message, }, }); diff --git a/ui/components/app/modals/modal.js b/ui/components/app/modals/modal.js index b81abbac3..3eab79fcc 100644 --- a/ui/components/app/modals/modal.js +++ b/ui/components/app/modals/modal.js @@ -26,7 +26,6 @@ import FadeModal from './fade-modal'; import MetaMetricsOptInModal from './metametrics-opt-in-modal'; import RejectTransactions from './reject-transactions'; import ConfirmDeleteNetwork from './confirm-delete-network'; -import AddToAddressBookModal from './add-to-addressbook-modal'; import EditApprovalPermission from './edit-approval-permission'; import NewAccountModal from './new-account-modal'; import CustomizeNonceModal from './customize-nonce'; @@ -34,9 +33,9 @@ import ConvertTokenToNftModal from './convert-token-to-nft-modal/convert-token-t const modalContainerBaseStyle = { transform: 'translate3d(-50%, 0, 0px)', - border: '1px solid #CCCFD1', + border: '1px solid var(--color-border-default)', borderRadius: '8px', - backgroundColor: '#FFFFFF', + backgroundColor: 'var(--color-background-default)', boxShadow: '0 2px 22px 0 rgba(0,0,0,0.2)', }; @@ -114,33 +113,6 @@ const MODALS = { }, }, - ADD_TO_ADDRESSBOOK: { - contents: , - mobileModalStyle: { - width: '95%', - top: '10%', - boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', - transform: 'none', - left: '0', - right: '0', - margin: '0 auto', - borderRadius: '10px', - }, - laptopModalStyle: { - width: '375px', - top: '10%', - boxShadow: 'rgba(0, 0, 0, 0.15) 0px 2px 2px 2px', - transform: 'none', - left: '0', - right: '0', - margin: '0 auto', - borderRadius: '10px', - }, - contentStyle: { - borderRadius: '10px', - }, - }, - NEW_ACCOUNT: { contents: , mobileModalStyle: { @@ -417,7 +389,7 @@ const MODALS = { }; const BACKDROPSTYLE = { - backgroundColor: 'rgba(0, 0, 0, 0.5)', + backgroundColor: 'var(--color-overlay-default)', }; function mapStateToProps(state) { diff --git a/ui/components/app/modals/new-account-modal/index.scss b/ui/components/app/modals/new-account-modal/index.scss index de08a672c..364cf1ce4 100644 --- a/ui/components/app/modals/new-account-modal/index.scss +++ b/ui/components/app/modals/new-account-modal/index.scss @@ -1,7 +1,7 @@ .new-account-modal { display: flex; flex-flow: column nowrap; - background-color: var(--white); + background-color: var(--color-background-default); border-radius: 10px; box-shadow: 0 5px 16px rgba($black, 0.25); @@ -9,7 +9,7 @@ @extend %col-nowrap; padding: 1.5rem; - border-bottom: 1px solid var(--Grey-100); + border-bottom: 1px solid var(--color-border-muted); &__header { @include H4; @@ -23,28 +23,29 @@ &__header-close { @include H4; - color: #24292e; + color: var(--color-icon-default); background: none; } } &__input-label { - color: var(--Grey-600); + color: var(--color-text-alternative); margin-top: 1.25rem; } &__input { @include H4; - background: var(--white); - border: 1px solid var(--Grey-100); + background: var(--color-background-default); + border: 1px solid var(--color-border-muted); + color: var(--color-text-default); box-sizing: border-box; border-radius: 8px; padding: 0.625rem 0.75rem; margin-top: 0.75rem; &::placeholder { - color: var(--Grey-300); + color: var(--color-text-muted); } } diff --git a/ui/components/app/modals/qr-scanner/index.scss b/ui/components/app/modals/qr-scanner/index.scss index a29200237..9c970397f 100644 --- a/ui/components/app/modals/qr-scanner/index.scss +++ b/ui/components/app/modals/qr-scanner/index.scss @@ -1,7 +1,7 @@ .qr-scanner { width: 100%; height: 100%; - background-color: #fff; + background-color: var(--color-background-default); display: flex; flex-flow: column; border-radius: 8px; @@ -67,16 +67,16 @@ button:last-of-type { margin-right: 0; - background-color: #009eec; + background-color: var(--color-primary-default); border: none; - color: #fff; + color: var(--color-primary-inverse); } } &__close::after { content: '\00D7'; font-size: 35px; - color: #9b9b9b; + color: var(--color-icon-default); position: absolute; top: 4px; right: 20px; diff --git a/ui/components/app/modals/qr-scanner/qr-scanner.component.js b/ui/components/app/modals/qr-scanner/qr-scanner.component.js index 610c2631b..cab88a139 100644 --- a/ui/components/app/modals/qr-scanner/qr-scanner.component.js +++ b/ui/components/app/modals/qr-scanner/qr-scanner.component.js @@ -250,7 +250,9 @@ export default class QrScanner extends Component { display: ready === READY_STATE.READY ? 'block' : 'none', }} /> - {ready === READY_STATE.READY ? null : } + {ready === READY_STATE.READY ? null : ( + + )}
{message}
diff --git a/ui/components/app/modals/transaction-confirmed/transaction-confirmed.component.js b/ui/components/app/modals/transaction-confirmed/transaction-confirmed.component.js index 570867df2..84ede9dbb 100644 --- a/ui/components/app/modals/transaction-confirmed/transaction-confirmed.component.js +++ b/ui/components/app/modals/transaction-confirmed/transaction-confirmed.component.js @@ -28,7 +28,10 @@ export default class TransactionConfirmed extends PureComponent { return (
- +
{`${t('confirmed')}!`}
diff --git a/ui/components/app/modals/transaction-confirmed/transaction-confirmed.stories.js b/ui/components/app/modals/transaction-confirmed/transaction-confirmed.stories.js new file mode 100644 index 000000000..1be314295 --- /dev/null +++ b/ui/components/app/modals/transaction-confirmed/transaction-confirmed.stories.js @@ -0,0 +1,11 @@ +import React from 'react'; +import TransactionConfirmed from '.'; + +export default { + title: 'Components/App/Modals/TransactionConfirmed', + id: __filename, +}; + +export const DefaultStory = () => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/multilayer-fee-message/index.scss b/ui/components/app/multilayer-fee-message/index.scss new file mode 100644 index 000000000..f4b684ecd --- /dev/null +++ b/ui/components/app/multilayer-fee-message/index.scss @@ -0,0 +1,3 @@ +.multi-layer-fee-message { + padding-top: 24px; +} diff --git a/ui/components/app/multilayer-fee-message/multi-layer-fee-message.js b/ui/components/app/multilayer-fee-message/multi-layer-fee-message.js index 22368d146..e5ad8d6fd 100644 --- a/ui/components/app/multilayer-fee-message/multi-layer-fee-message.js +++ b/ui/components/app/multilayer-fee-message/multi-layer-fee-message.js @@ -2,7 +2,9 @@ import React, { useContext, useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { captureException } from '@sentry/browser'; import TransactionDetailItem from '../transaction-detail-item/transaction-detail-item.component'; +import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'; import fetchEstimatedL1Fee from '../../../helpers/utils/optimism/fetchEstimatedL1Fee'; +import { SECONDARY } from '../../../helpers/constants/common'; import { I18nContext } from '../../../contexts/i18n'; import { sumHexes } from '../../../helpers/utils/transactions.util'; import { @@ -21,23 +23,26 @@ export default function MultilayerFeeMessage({ const [fetchedLayer1Total, setLayer1Total] = useState(null); let layer1Total = 'unknown'; + let layer1TotalBN; if (fetchedLayer1Total !== null) { - const layer1TotalBN = toBigNumber.hex(fetchedLayer1Total); + layer1TotalBN = toBigNumber.hex(fetchedLayer1Total); layer1Total = `${toNormalizedDenomination .WEI(layer1TotalBN) - .toString(10)} ${nativeCurrency}`; + .toFixed(12)} ${nativeCurrency}`; } + const feeTotal = sumHexes(layer2fee || '0x0', fetchedLayer1Total || '0x0'); + const totalInWeiHex = sumHexes( - layer2fee || '0x0', - fetchedLayer1Total || '0x0', + feeTotal || '0x0', transaction.txParams.value || '0x0', ); + const totalBN = toBigNumber.hex(totalInWeiHex); const totalInEth = `${toNormalizedDenomination .WEI(totalBN) - .toString(10)} ${nativeCurrency}`; + .toFixed(12)} ${nativeCurrency}`; useEffect(() => { const getEstimatedL1Fee = async () => { @@ -52,12 +57,31 @@ export default function MultilayerFeeMessage({ getEstimatedL1Fee(); }, [transaction]); + const feeTotalInFiat = ( + + ); + + const totalInFiat = ( + + ); + return ( - <> +
@@ -65,11 +89,12 @@ export default function MultilayerFeeMessage({ key="total-item" detailTitle={t('total')} detailTotal={totalInEth} + detailText={totalInFiat} subTitle={t('transactionDetailMultiLayerTotalSubtitle')} noBold={plainStyle} flexWidthValues={plainStyle} /> - +
); } diff --git a/ui/components/app/multiple-notifications/index.scss b/ui/components/app/multiple-notifications/index.scss index 529e6658a..3d3eaf2ad 100644 --- a/ui/components/app/multiple-notifications/index.scss +++ b/ui/components/app/multiple-notifications/index.scss @@ -22,14 +22,12 @@ position: absolute; bottom: 14px; left: 16px; - color: white; + color: var(--color-icon-muted); cursor: pointer; visibility: visible; &:hover { @include H4; - - color: #b0d7f2; } } } diff --git a/ui/components/app/multiple-notifications/multiple-notifications.component.js b/ui/components/app/multiple-notifications/multiple-notifications.component.js index 4bc64ab14..96abbf4a2 100644 --- a/ui/components/app/multiple-notifications/multiple-notifications.component.js +++ b/ui/components/app/multiple-notifications/multiple-notifications.component.js @@ -40,7 +40,7 @@ export default class MultipleNotifications extends PureComponent { > {childrenToRender.length > 1 ? ( diff --git a/ui/components/app/network-display/network-display.js b/ui/components/app/network-display/network-display.js index a92278ef6..6e3d6aabb 100644 --- a/ui/components/app/network-display/network-display.js +++ b/ui/components/app/network-display/network-display.js @@ -47,7 +47,11 @@ export default function NetworkDisplay({ isLoading={networkIsLoading} > {Object.keys(permissions).map((permission) => { const { label, leftIcon, rightIcon } = getPermissionDescription( + t, permission, ); diff --git a/ui/components/app/qr-hardware-popover/enhanced-reader.js b/ui/components/app/qr-hardware-popover/enhanced-reader.js index d1d696717..c9aa354b4 100644 --- a/ui/components/app/qr-hardware-popover/enhanced-reader.js +++ b/ui/components/app/qr-hardware-popover/enhanced-reader.js @@ -55,7 +55,7 @@ const EnhancedReader = ({ handleScan }) => { filter: 'blur(4px)', }} /> - {canplay ? null : } + {canplay ? null : }
); }; diff --git a/ui/components/app/selected-account/index.scss b/ui/components/app/selected-account/index.scss index cd5d39548..bb973d5c7 100644 --- a/ui/components/app/selected-account/index.scss +++ b/ui/components/app/selected-account/index.scss @@ -14,7 +14,7 @@ width: 100%; font-weight: 500; - color: var(--black); + color: var(--color-text-default); text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -25,7 +25,7 @@ &__address { @include H7; - color: #989a9b; + color: var(--color-text-muted); display: flex; align-items: center; } @@ -42,12 +42,9 @@ width: 100%; background-color: unset; - &:hover { - background-color: var(--Grey-000); - } - + &:hover, &:active { - background-color: #d9d7da; + background-color: var(--color-background-alternative); } } diff --git a/ui/components/app/selected-account/selected-account.component.js b/ui/components/app/selected-account/selected-account.component.js index 3e505d560..1dc3c135f 100644 --- a/ui/components/app/selected-account/selected-account.component.js +++ b/ui/components/app/selected-account/selected-account.component.js @@ -63,7 +63,7 @@ class SelectedAccount extends Component {
{shortenAddress(checksummedAddress)}
- +
diff --git a/ui/components/app/signature-request-original/index.scss b/ui/components/app/signature-request-original/index.scss index da6d3512e..2e2aca65f 100644 --- a/ui/components/app/signature-request-original/index.scss +++ b/ui/components/app/signature-request-original/index.scss @@ -2,7 +2,7 @@ &__container { width: 380px; border-radius: 8px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); display: flex; flex-flow: column nowrap; @@ -54,7 +54,7 @@ &__header-background { position: absolute; - background-color: var(--athens-grey); + background-color: var(--color-background-alternative); z-index: 2; width: 100%; height: 100%; @@ -63,7 +63,7 @@ &__header__text { @include H3; - color: #5b5d67; + color: var(--color-default-text); z-index: 3; } @@ -76,7 +76,7 @@ &__header__tip { height: 25px; width: 25px; - background: var(--athens-grey); + background: var(--color-background-alternative); transform: rotate(45deg); position: absolute; bottom: -8px; @@ -91,7 +91,7 @@ } &__account { - color: var(--dusty-gray); + color: var(--color-text-alternative); margin-left: 17px; } @@ -103,7 +103,7 @@ @include H7; height: 22px; - background-color: var(--white); + background-color: var(--color-background-default); width: 124px; .account-list-item { @@ -123,7 +123,7 @@ } &__balance { - color: var(--dusty-gray); + color: var(--color-text-alternative); margin-right: 17px; width: 124px; } @@ -181,19 +181,19 @@ } &__notice { - color: var(--dusty-gray); + color: var(--color-text-alternative); padding: 0 10px; } &__warning { - color: var(--crimson); + color: var(--color-error-default); } &__rows { height: 100%; overflow-y: scroll; overflow-x: hidden; - border-top: 1px solid var(--geyser); + border-top: 1px solid var(--color-border-default); display: flex; flex-flow: column; } @@ -208,7 +208,7 @@ @include H5; width: 80px; - color: var(--dusty-gray); + color: var(--color-text-alternative); margin-top: 12px; margin-left: 18px; width: 100%; @@ -217,10 +217,10 @@ &__row-value { @include H6; - color: var(--scorpion); + color: var(--color-text-alternative); width: 100%; overflow-wrap: break-word; - border-bottom: 1px solid #d2d8dd; + border-bottom: 1px solid var(--color-border-default); padding: 6px 18px 15px; white-space: pre-wrap; } @@ -228,7 +228,7 @@ &__help-link { cursor: pointer; text-decoration: underline; - color: var(--primary-blue); + color: var(--color-primary-default); margin-inline-start: 3px; } @@ -241,7 +241,7 @@ justify-content: center; position: relative; flex: 0 0 auto; - border-top: 1px solid var(--geyser); + border-top: 1px solid var(--color-border-default); padding: 1.6rem; button { diff --git a/ui/components/app/signature-request-original/signature-request-original.component.js b/ui/components/app/signature-request-original/signature-request-original.component.js index 8b83e77e0..1c8959f18 100644 --- a/ui/components/app/signature-request-original/signature-request-original.component.js +++ b/ui/components/app/signature-request-original/signature-request-original.component.js @@ -16,7 +16,7 @@ import SiteIcon from '../../ui/site-icon'; export default class SignatureRequestOriginal extends Component { static contextTypes = { t: PropTypes.func.isRequired, - metricsEvent: PropTypes.func.isRequired, + trackEvent: PropTypes.func.isRequired, }; static propTypes = { @@ -262,7 +262,7 @@ export default class SignatureRequestOriginal extends Component { txData: { type }, hardwareWalletRequiresConnection, } = this.props; - const { metricsEvent, t } = this.context; + const { trackEvent, t } = this.context; return (
@@ -272,13 +272,12 @@ export default class SignatureRequestOriginal extends Component { className="request-signature__footer__cancel-button" onClick={async (event) => { await cancel(event); - metricsEvent({ - eventOpts: { - category: 'Transactions', + trackEvent({ + category: 'Transactions', + event: 'Cancel', + properties: { action: 'Sign Request', - name: 'Cancel', - }, - customVariables: { + legacy_event: true, type, }, }); @@ -296,13 +295,12 @@ export default class SignatureRequestOriginal extends Component { disabled={hardwareWalletRequiresConnection} onClick={async (event) => { await sign(event); - metricsEvent({ - eventOpts: { - category: 'Transactions', + trackEvent({ + category: 'Transactions', + event: 'Confirm', + properties: { action: 'Sign Request', - name: 'Confirm', - }, - customVariables: { + legacy_event: true, type, }, }); diff --git a/ui/components/app/signature-request/index.scss b/ui/components/app/signature-request/index.scss index b92452e46..1483054d5 100644 --- a/ui/components/app/signature-request/index.scss +++ b/ui/components/app/signature-request/index.scss @@ -51,7 +51,7 @@ height: 75px; width: 75px; border-radius: 50%; - border: 1px solid white; + border: 1px solid var(--color-background-default); position: absolute; box-shadow: 0 2px 2px 0.5px rgba(0, 0, 0, 0.19); } @@ -61,7 +61,7 @@ font-style: normal; font-weight: 500; font-size: 60px; - color: white; + color: var(--color-background-default); z-index: 1; text-shadow: 0 4px 6px rgba(0, 0, 0, 0.422); } @@ -79,7 +79,7 @@ p { @include H6; - color: #999; + color: var(--color-text-muted); } .identicon {} diff --git a/ui/components/app/signature-request/signature-request-footer/index.scss b/ui/components/app/signature-request/signature-request-footer/index.scss index e679bef43..89fe1ffd2 100644 --- a/ui/components/app/signature-request/signature-request-footer/index.scss +++ b/ui/components/app/signature-request/signature-request-footer/index.scss @@ -1,6 +1,6 @@ .signature-request-footer { display: flex; - border-top: 1px solid #d2d8dd; + border-top: 1px solid var(--color-border-muted); button { text-transform: uppercase; diff --git a/ui/components/app/signature-request/signature-request-header/index.scss b/ui/components/app/signature-request/signature-request-header/index.scss index fe32b328a..6f454f559 100644 --- a/ui/components/app/signature-request/signature-request-header/index.scss +++ b/ui/components/app/signature-request/signature-request-header/index.scss @@ -3,7 +3,7 @@ display: flex; padding: 1rem; - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-default); justify-content: space-between; &--account, diff --git a/ui/components/app/signature-request/signature-request-message/index.scss b/ui/components/app/signature-request/signature-request-message/index.scss index 6673d38f7..343bd0563 100644 --- a/ui/components/app/signature-request/signature-request-message/index.scss +++ b/ui/components/app/signature-request/signature-request-message/index.scss @@ -9,7 +9,7 @@ @include H6; font-weight: 500; - color: #636778; + color: var(--color-text-alternative); margin-left: 12px; } @@ -18,15 +18,15 @@ flex: 1 1 0; text-align: left; - border-bottom: 1px solid #d2d8dd; + border-bottom: 1px solid var(--color-border-default); padding: 0.5rem; margin: 0; - color: #ccc; + color: var(--color-text-alternative); } &--root { flex: 1 100%; - background-color: #f8f9fb; + background-color: var(--color-background-alternative); padding-bottom: 0.5rem; overflow: auto; padding-left: 12px; @@ -42,12 +42,12 @@ padding-left: 0.3rem; &-label { - color: #5b5d67; + color: var(--color-text-alternative); margin-left: 0.5rem; } &-value { - color: black; + color: var(--color-text-default); margin-left: 0.5rem; white-space: pre-line; overflow: hidden; @@ -63,7 +63,9 @@ display: flex; align-items: center; justify-content: center; - background-color: var(--Grey-500); + border: 1px solid var(--color-border-default); + background: var(--color-background-alternative); + color: var(--color-icon-default); position: absolute; right: 24px; bottom: 12px; diff --git a/ui/components/app/signature-request/signature-request-message/signature-request-message.component.js b/ui/components/app/signature-request/signature-request-message/signature-request-message.component.js index 3c186f4c5..2a02cdcdd 100644 --- a/ui/components/app/signature-request/signature-request-message/signature-request-message.component.js +++ b/ui/components/app/signature-request/signature-request-message/signature-request-message.component.js @@ -77,12 +77,7 @@ export default class SignatureRequestMessage extends PureComponent { className="signature-request-message__scroll-button" data-testid="signature-request-scroll-button" > - {this.context.t('scrollDown')} +
); } diff --git a/ui/components/app/signature-request/signature-request.component.js b/ui/components/app/signature-request/signature-request.component.js index 164221e16..32a501880 100644 --- a/ui/components/app/signature-request/signature-request.component.js +++ b/ui/components/app/signature-request/signature-request.component.js @@ -41,7 +41,7 @@ export default class SignatureRequest extends PureComponent { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; state = { @@ -73,17 +73,16 @@ export default class SignatureRequest extends PureComponent { } = this.props; const { address: fromAddress } = fromAccount; const { message, domain = {}, primaryType, types } = JSON.parse(data); - const { metricsEvent } = this.context; + const { trackEvent } = this.context; const onSign = (event) => { sign(event); - metricsEvent({ - eventOpts: { - category: 'Transactions', + trackEvent({ + category: 'Transactions', + event: 'Confirm', + properties: { action: 'Sign Request', - name: 'Confirm', - }, - customVariables: { + legacy_event: true, type, version, }, @@ -92,13 +91,12 @@ export default class SignatureRequest extends PureComponent { const onCancel = (event) => { cancel(event); - metricsEvent({ - eventOpts: { - category: 'Transactions', + trackEvent({ + category: 'Transactions', + event: 'Cancel', + properties: { action: 'Sign Request', - name: 'Cancel', - }, - customVariables: { + legacy_event: true, type, version, }, diff --git a/ui/components/app/srp-input/srp-input.js b/ui/components/app/srp-input/srp-input.js index 436df3644..f6ac3b261 100644 --- a/ui/components/app/srp-input/srp-input.js +++ b/ui/components/app/srp-input/srp-input.js @@ -127,7 +127,7 @@ export default function SrpInput({ onChange }) { @@ -197,7 +197,7 @@ export default function SrpInput({ onChange }) { {srpError ? ( , + icon: , content: 'General', key: 'general', }, { - icon: , + icon: , content: 'Contacts', key: 'contacts', }, { - icon: , + icon: , content: 'Snaps', key: 'snaps', }, { - icon: , + icon: , content: 'SecurityAndPrivacy', key: 'securityAndPrivacy', }, { - icon: , + icon: , content: 'Alerts', key: 'alerts', }, { - icon: , + icon: , content: 'Networks', key: 'networks', }, { - icon: , + icon: , content: 'Experimental', key: 'experimental', }, { - icon: , + icon: , content: 'About', key: 'about', }, diff --git a/ui/components/app/token-cell/token-cell.js b/ui/components/app/token-cell/token-cell.js index 8be05c823..932976977 100644 --- a/ui/components/app/token-cell/token-cell.js +++ b/ui/components/app/token-cell/token-cell.js @@ -29,7 +29,7 @@ export default function TokenCell({ rel="noopener noreferrer" target="_blank" onClick={(event) => event.stopPropagation()} - style={{ color: '#F7861C' }} + style={{ color: 'var(--color-secondary-default)' }} > {t('here')} diff --git a/ui/components/app/token-cell/token-cell.scss b/ui/components/app/token-cell/token-cell.scss index d2087afda..15ddbdea4 100644 --- a/ui/components/app/token-cell/token-cell.scss +++ b/ui/components/app/token-cell/token-cell.scss @@ -1,5 +1,5 @@ .token-cell { &--outdated .list-item__heading { - color: var(--Grey-500); + color: var(--color-text-alternative); } } diff --git a/ui/components/app/transaction-activity-log/index.scss b/ui/components/app/transaction-activity-log/index.scss index 12247e88f..caa925d4c 100644 --- a/ui/components/app/transaction-activity-log/index.scss +++ b/ui/components/app/transaction-activity-log/index.scss @@ -1,6 +1,6 @@ .transaction-activity-log { &__title { - border-bottom: 1px solid #d8d8d8; + border-bottom: 1px solid var(--color-border-muted); padding-bottom: 4px; text-transform: capitalize; } @@ -23,7 +23,7 @@ top: 0; height: 100%; width: 7px; - border-right: 1px solid #909090; + border-right: 1px solid var(--color-border-muted); } &:first-child::after { @@ -45,7 +45,7 @@ height: 15px; margin-right: 6px; border-radius: 50%; - background: #909090; + background: var(--color-icon-default); flex: 0 0 auto; display: flex; justify-content: center; @@ -56,11 +56,11 @@ &__activity-text { @include H7; - color: var(--dusty-gray); + color: var(--color-text-alternate); cursor: pointer; &:hover { - color: var(--black); + color: var(--color-text-default); } } @@ -77,7 +77,7 @@ @include H7; cursor: pointer; - color: var(--primary-blue); + color: var(--color-primary-default); } b { diff --git a/ui/components/app/transaction-activity-log/transaction-activity-log-icon/index.scss b/ui/components/app/transaction-activity-log/transaction-activity-log-icon/index.scss new file mode 100644 index 000000000..80f97c70b --- /dev/null +++ b/ui/components/app/transaction-activity-log/transaction-activity-log-icon/index.scss @@ -0,0 +1,5 @@ +.transaction-activity-log-icon { + &__icon { + color: var(--color-icon-default); + } +} diff --git a/ui/components/app/transaction-activity-log/transaction-activity-log-icon/transaction-activity-log-icon.component.js b/ui/components/app/transaction-activity-log/transaction-activity-log-icon/transaction-activity-log-icon.component.js index 7f52daaa8..891562805 100644 --- a/ui/components/app/transaction-activity-log/transaction-activity-log-icon/transaction-activity-log-icon.component.js +++ b/ui/components/app/transaction-activity-log/transaction-activity-log-icon/transaction-activity-log-icon.component.js @@ -13,15 +13,15 @@ import { TRANSACTION_CANCEL_SUCCESS_EVENT, } from '../transaction-activity-log.constants'; -const imageHash = { - [TRANSACTION_CREATED_EVENT]: '/images/icons/new.svg', - [TRANSACTION_SUBMITTED_EVENT]: '/images/icons/submitted.svg', - [TRANSACTION_RESUBMITTED_EVENT]: '/images/icons/retry.svg', - [TRANSACTION_CONFIRMED_EVENT]: '/images/icons/confirm.svg', - [TRANSACTION_DROPPED_EVENT]: '/images/icons/cancelled.svg', - [TRANSACTION_ERRORED_EVENT]: '/images/icons/error.svg', - [TRANSACTION_CANCEL_ATTEMPTED_EVENT]: '/images/icons/cancelled.svg', - [TRANSACTION_CANCEL_SUCCESS_EVENT]: '/images/icons/cancelled.svg', +export const imageHash = { + [TRANSACTION_CREATED_EVENT]: 'fa-plus', + [TRANSACTION_SUBMITTED_EVENT]: 'fa-arrow-up', + [TRANSACTION_RESUBMITTED_EVENT]: 'fa-retweet', + [TRANSACTION_CONFIRMED_EVENT]: 'fa-check', + [TRANSACTION_DROPPED_EVENT]: 'fa-times', + [TRANSACTION_ERRORED_EVENT]: 'fa-exclamation', + [TRANSACTION_CANCEL_ATTEMPTED_EVENT]: 'fa-times', + [TRANSACTION_CANCEL_SUCCESS_EVENT]: 'fa-times', }; export default class TransactionActivityLogIcon extends PureComponent { @@ -36,11 +36,19 @@ export default class TransactionActivityLogIcon extends PureComponent { render() { const { className, eventKey } = this.props; - const imagePath = imageHash[eventKey]; + const iconClassName = imageHash[eventKey]; return (
- {imagePath ? : null} + {iconClassName ? ( + + ) : null}
); } diff --git a/ui/components/app/transaction-activity-log/transaction-activity-log-icon/transaction-activity-log-icon.stories.js b/ui/components/app/transaction-activity-log/transaction-activity-log-icon/transaction-activity-log-icon.stories.js new file mode 100644 index 000000000..94bda3be5 --- /dev/null +++ b/ui/components/app/transaction-activity-log/transaction-activity-log-icon/transaction-activity-log-icon.stories.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { imageHash } from './transaction-activity-log-icon.component'; +import TransactionActivityLogIcon from '.'; + +export default { + title: 'Components/App/TransactionActivityLog/TransactionActivityLogIcon', + id: __filename, + argTypes: { + className: { + control: 'text', + }, + eventKey: { + control: 'select', + options: Object.keys(imageHash), + }, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/transaction-breakdown/index.scss b/ui/components/app/transaction-breakdown/index.scss index 75c6be56b..5bc00da50 100644 --- a/ui/components/app/transaction-breakdown/index.scss +++ b/ui/components/app/transaction-breakdown/index.scss @@ -5,7 +5,7 @@ padding-bottom: 4px; padding-top: 8px; font-size: 14px; - color: var(--Black-100); + color: var(--color-text-default); font-weight: bold; text-transform: capitalize; } @@ -23,12 +23,12 @@ &--eth-total { font-weight: bold; - color: var(--Black-100); + color: var(--color-text-default); } &--amount { font-weight: bold; - color: var(--Black-100); + color: var(--color-text-default); } } } diff --git a/ui/components/app/transaction-breakdown/transaction-breakdown-row/index.scss b/ui/components/app/transaction-breakdown/transaction-breakdown-row/index.scss index cb8c763a7..a39c73685 100644 --- a/ui/components/app/transaction-breakdown/transaction-breakdown-row/index.scss +++ b/ui/components/app/transaction-breakdown/transaction-breakdown-row/index.scss @@ -1,13 +1,13 @@ .transaction-breakdown-row { @include H7; - color: var(--Grey-500); + color: var(--color-text-alternative); display: flex; justify-content: space-between; padding: 8px 0; &--with-bottom-border { - border-bottom: 1px solid #d8d8d8; + border-bottom: 1px solid var(---color-border-muted); } &__title { diff --git a/ui/components/app/transaction-decoding/components/ui/accreditation/index.scss b/ui/components/app/transaction-decoding/components/ui/accreditation/index.scss index a499ba367..e71dfcdf3 100644 --- a/ui/components/app/transaction-decoding/components/ui/accreditation/index.scss +++ b/ui/components/app/transaction-decoding/components/ui/accreditation/index.scss @@ -8,7 +8,7 @@ } &__info { - color: var(--ui-black); + color: var(--color-text-default); display: flex; flex-flow: column; flex-wrap: wrap; diff --git a/ui/components/app/transaction-decoding/components/ui/copy-raw-data/copy-raw-data.component.js b/ui/components/app/transaction-decoding/components/ui/copy-raw-data/copy-raw-data.component.js index 8e9361e70..ae61cfd0b 100644 --- a/ui/components/app/transaction-decoding/components/ui/copy-raw-data/copy-raw-data.component.js +++ b/ui/components/app/transaction-decoding/components/ui/copy-raw-data/copy-raw-data.component.js @@ -20,7 +20,7 @@ const CopyRawData = ({ data }) => { className="copy-raw-data__button" >
- +
{t('copyRawTransactionData')} diff --git a/ui/components/app/transaction-decoding/components/ui/copy-raw-data/index.scss b/ui/components/app/transaction-decoding/components/ui/copy-raw-data/index.scss index d9f9e6719..2d5297b9c 100644 --- a/ui/components/app/transaction-decoding/components/ui/copy-raw-data/index.scss +++ b/ui/components/app/transaction-decoding/components/ui/copy-raw-data/index.scss @@ -12,11 +12,11 @@ background-color: transparent; &:hover { - background-color: var(--Grey-000); + background-color: var(--color-background-alternative); } &:active { - background-color: #ededed; + background-color: var(--color-background-alternative); } } @@ -25,6 +25,6 @@ } &__label { - color: #6a737d; + color: var(--color-text-alternative); } } diff --git a/ui/components/app/transaction-decoding/index.scss b/ui/components/app/transaction-decoding/index.scss index 98992c7aa..71cf1d75e 100644 --- a/ui/components/app/transaction-decoding/index.scss +++ b/ui/components/app/transaction-decoding/index.scss @@ -53,7 +53,7 @@ .eth-tx-params { padding: 8px; width: 343px; - background: white; + background: var(--color-background-default); text-align: left; display: flex; flex-direction: column; @@ -80,8 +80,8 @@ width: fit-content; padding: 4px; text-transform: uppercase; - border: 1px solid rgb(149, 149, 149); - color: rgb(149, 149, 149); + border: 1px solid var(--color-border-default); + color: var(--color-background-alternative); border-radius: 4px; } @@ -130,7 +130,7 @@ } .solidity-value { - color: #6a737d; + color: var(--color-text-alternative); overflow-x: hidden; padding-bottom: 5px; @@ -152,18 +152,18 @@ } .eth-tx-params details > summary { - color: black; + color: var(--color-text-default); font-family: sans-serif; } .eth-tx-params footer { text-align: center; - color: #8d959e; + color: var(--color-text-muted); } .eth-tx-params footer a { text-align: center; - color: #8d959e; + color: var(--color-text-muted); } } } diff --git a/ui/components/app/transaction-decoding/transaction-decoding.component.js b/ui/components/app/transaction-decoding/transaction-decoding.component.js index 1a6c0d9f7..04e80eb2e 100644 --- a/ui/components/app/transaction-decoding/transaction-decoding.component.js +++ b/ui/components/app/transaction-decoding/transaction-decoding.component.js @@ -202,7 +202,7 @@ export default function TransactionDecoding({ to = '', inputData: data = '' }) { if (loading) { return (
- +
); } diff --git a/ui/components/app/transaction-detail-item/index.scss b/ui/components/app/transaction-detail-item/index.scss index d09c1ae46..c7a39c250 100644 --- a/ui/components/app/transaction-detail-item/index.scss +++ b/ui/components/app/transaction-detail-item/index.scss @@ -1,7 +1,7 @@ .transaction-detail-item { - color: var(--ui-4); + color: var(--color-text-alternative); padding: 20px 0; - border-bottom: 1px solid var(--ui-3); + border-bottom: 1px solid var(--color-border-default); &__row { display: flex; @@ -34,7 +34,7 @@ margin-inline-start: 4px; path { - fill: var(--ui-3); + fill: var(--color-icon-default); } } diff --git a/ui/components/app/transaction-detail-item/transaction-detail-item.component.js b/ui/components/app/transaction-detail-item/transaction-detail-item.component.js index 8601185a9..e82c78f1a 100644 --- a/ui/components/app/transaction-detail-item/transaction-detail-item.component.js +++ b/ui/components/app/transaction-detail-item/transaction-detail-item.component.js @@ -16,7 +16,7 @@ import { export default function TransactionDetailItem({ detailTitle = '', detailText = '', - detailTitleColor = COLORS.BLACK, + detailTitleColor = COLORS.TEXT_DEFAULT, detailTotal = '', subTitle = '', subText = '', diff --git a/ui/components/app/transaction-detail/index.scss b/ui/components/app/transaction-detail/index.scss index c4505f10f..9c7813d95 100644 --- a/ui/components/app/transaction-detail/index.scss +++ b/ui/components/app/transaction-detail/index.scss @@ -8,7 +8,7 @@ button { @include H7; - color: var(--primary-1); + color: var(--color-primary-default); background: transparent; border: 0; padding-inline-end: 0; diff --git a/ui/components/app/transaction-icon/transaction-icon.js b/ui/components/app/transaction-icon/transaction-icon.js index 7aedfd7f4..1036b853a 100644 --- a/ui/components/app/transaction-icon/transaction-icon.js +++ b/ui/components/app/transaction-icon/transaction-icon.js @@ -23,9 +23,9 @@ const ICON_MAP = { [TRANSACTION_GROUP_CATEGORIES.SWAP]: Swap, }; -const FAIL_COLOR = '#D73A49'; -const PENDING_COLOR = '#6A737D'; -const OK_COLOR = '#2F80ED'; +const FAIL_COLOR = 'var(--color-error-default)'; +const PENDING_COLOR = 'var(--color-icon-default)'; +const OK_COLOR = 'var(--color-primary-default)'; const COLOR_MAP = { [TRANSACTION_GROUP_STATUSES.PENDING]: PENDING_COLOR, diff --git a/ui/components/app/transaction-icon/transaction-icon.scss b/ui/components/app/transaction-icon/transaction-icon.scss index cc50190b1..050d14714 100644 --- a/ui/components/app/transaction-icon/transaction-icon.scss +++ b/ui/components/app/transaction-icon/transaction-icon.scss @@ -3,6 +3,6 @@ height: 28px; width: 28px; border-radius: 14px; - background: var(--Grey-100); + background: var(--color-background-alternative); } } diff --git a/ui/components/app/transaction-list-item-details/index.scss b/ui/components/app/transaction-list-item-details/index.scss index 1ac29436d..506b1bbdc 100644 --- a/ui/components/app/transaction-list-item-details/index.scss +++ b/ui/components/app/transaction-list-item-details/index.scss @@ -16,7 +16,7 @@ &__sender-to-recipient-header { display: flex; font-size: 14px; - color: var(--Black-100); + color: var(--color-text-default); font-weight: bold; padding-bottom: 7px; @@ -34,7 +34,7 @@ & > div:first-child { font-size: 14px; - color: var(--Black-100); + color: var(--color-text-default); font-weight: bold; } diff --git a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js index 670be87fd..ed0a49113 100644 --- a/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js +++ b/ui/components/app/transaction-list-item-details/transaction-list-item-details.component.js @@ -19,7 +19,6 @@ import TransactionDecoding from '../transaction-decoding'; export default class TransactionListItemDetails extends PureComponent { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, trackEvent: PropTypes.func, }; @@ -69,6 +68,7 @@ export default class TransactionListItemDetails extends PureComponent { link_type: 'Transaction Block Explorer', action: 'Transaction Details', block_explorer_domain: getURLHostName(blockExplorerLink), + legacy_event: true, }, }); @@ -94,11 +94,12 @@ export default class TransactionListItemDetails extends PureComponent { const { primaryTransaction: transaction } = transactionGroup; const { hash } = transaction; - this.context.metricsEvent({ - eventOpts: { - category: 'Navigation', + this.context.trackEvent({ + category: 'Navigation', + event: 'Copied Transaction ID', + properties: { action: 'Activity Log', - name: 'Copied Transaction ID', + legacy_event: true, }, }); @@ -225,20 +226,22 @@ export default class TransactionListItemDetails extends PureComponent { senderName={senderNickname} senderAddress={senderAddress} onRecipientClick={() => { - this.context.metricsEvent({ - eventOpts: { - category: 'Navigation', + this.context.trackEvent({ + category: 'Navigation', + event: 'Copied "To" Address', + properties: { action: 'Activity Log', - name: 'Copied "To" Address', + legacy_event: true, }, }); }} onSenderClick={() => { - this.context.metricsEvent({ - eventOpts: { - category: 'Navigation', + this.context.trackEvent({ + category: 'Navigation', + event: 'Copied "From" Address', + properties: { action: 'Activity Log', - name: 'Copied "From" Address', + legacy_event: true, }, }); }} diff --git a/ui/components/app/transaction-list-item/index.scss b/ui/components/app/transaction-list-item/index.scss index 2a8e9d7d9..69413b1e1 100644 --- a/ui/components/app/transaction-list-item/index.scss +++ b/ui/components/app/transaction-list-item/index.scss @@ -1,6 +1,6 @@ .transaction-list-item { &__primary-currency { - color: var(--Black-100); + color: var(--color-text-default); overflow: hidden; text-overflow: ellipsis; } @@ -9,15 +9,15 @@ @include H7; margin-top: 4px; - color: var(--Grey-500); + color: var(--color-text-alternative); } & &--unconfirmed { - color: var(--Grey-500); + color: var(--color-text-alternative); } &--unconfirmed &__primary-currency { - color: var(--Grey-500); + color: var(--color-text-alternative); } &__pending-actions { diff --git a/ui/components/app/transaction-list-item/transaction-list-item.component.js b/ui/components/app/transaction-list-item/transaction-list-item.component.js index e503c294c..f911dcff8 100644 --- a/ui/components/app/transaction-list-item/transaction-list-item.component.js +++ b/ui/components/app/transaction-list-item/transaction-list-item.component.js @@ -1,4 +1,4 @@ -import React, { useMemo, useState, useCallback } from 'react'; +import React, { useMemo, useState, useCallback, useContext } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useHistory } from 'react-router-dom'; @@ -31,13 +31,13 @@ import { getEIP1559V2Enabled, } from '../../../selectors'; import { isLegacyTransaction } from '../../../helpers/utils/transactions.util'; -import { useMetricEvent } from '../../../hooks/useMetricEvent'; import Button from '../../ui/button'; import AdvancedGasFeePopover from '../advanced-gas-fee-popover'; import CancelButton from '../cancel-button'; import CancelSpeedupPopover from '../cancel-speedup-popover'; import EditGasFeePopover from '../edit-gas-fee-popover'; import EditGasPopover from '../edit-gas-popover'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; function TransactionListItemInner({ transactionGroup, @@ -60,26 +60,19 @@ function TransactionListItemInner({ primaryTransaction: { err, status }, } = transactionGroup; - const speedUpMetricsEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Activity Log', - name: 'Clicked "Speed Up"', - }, - }); - - const cancelMetricsEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Activity Log', - name: 'Clicked "Cancel"', - }, - }); + const trackEvent = useContext(MetaMetricsContext); const retryTransaction = useCallback( async (event) => { event.stopPropagation(); - speedUpMetricsEvent(); + trackEvent({ + event: 'Clicked "Speed Up"', + category: 'Navigation', + properties: { + action: 'Activity Log', + legacy_event: true, + }, + }); if (supportsEIP1559V2) { setEditGasMode(EDIT_GAS_MODES.SPEED_UP); openModal('cancelSpeedUpTransaction'); @@ -87,13 +80,20 @@ function TransactionListItemInner({ setShowRetryEditGasPopover(true); } }, - [openModal, setEditGasMode, speedUpMetricsEvent, supportsEIP1559V2], + [openModal, setEditGasMode, trackEvent, supportsEIP1559V2], ); const cancelTransaction = useCallback( (event) => { event.stopPropagation(); - cancelMetricsEvent(); + trackEvent({ + event: 'Clicked "Cancel"', + category: 'Navigation', + properties: { + action: 'Activity Log', + legacy_event: true, + }, + }); if (supportsEIP1559V2) { setEditGasMode(EDIT_GAS_MODES.CANCEL); openModal('cancelSpeedUpTransaction'); @@ -101,7 +101,7 @@ function TransactionListItemInner({ setShowCancelEditGasPopover(true); } }, - [cancelMetricsEvent, openModal, setEditGasMode, supportsEIP1559V2], + [trackEvent, openModal, setEditGasMode, supportsEIP1559V2], ); const shouldShowSpeedUp = useShouldShowSpeedUp( diff --git a/ui/components/app/transaction-list/index.scss b/ui/components/app/transaction-list/index.scss index 431c4cc2b..ee932bc3b 100644 --- a/ui/components/app/transaction-list/index.scss +++ b/ui/components/app/transaction-list/index.scss @@ -13,8 +13,8 @@ @include H6; flex: 0 0 auto; - color: var(--Grey-400); - border-bottom: 1px solid var(--Grey-100); + color: var(--color-text-muted); + border-bottom: 1px solid var(--color-border-muted); padding: 8px 0 8px 20px; @media screen and (max-width: $break-small) { @@ -41,7 +41,7 @@ grid-row-start: 2; display: flex; justify-content: center; - color: var(--silver); + color: var(--color-text-muted); } &__view-more { diff --git a/ui/components/app/transaction-status/index.scss b/ui/components/app/transaction-status/index.scss index 6c66a9a17..55c22472c 100644 --- a/ui/components/app/transaction-status/index.scss +++ b/ui/components/app/transaction-status/index.scss @@ -2,34 +2,22 @@ display: inline; &--confirmed { - color: var(--success-3); - } - - &--unapproved { - color: var(--Orange-500); - } - - &--failed { - color: var(--Red-500); - } - - &--cancelled { - color: var(--Red-500); - } - - &--dropped { - color: var(--Red-500); + color: var(--color-success-default); } + &--failed, + &--cancelled, + &--dropped, &--rejected { - color: var(--Red-500); + color: var(--color-error-default); } + &--unapproved, &--pending { - color: var(--Orange-500); + color: var(--color-secondary-default); } &--queued { - color: var(--Grey-500); + color: var(--color-text-alternative); } } diff --git a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js index 9143c94a1..c2beced07 100644 --- a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js +++ b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.component.js @@ -6,24 +6,32 @@ import { useUserPreferencedCurrency } from '../../../hooks/useUserPreferencedCur export default function UserPreferencedCurrencyDisplay({ 'data-testid': dataTestId, - ethLogoHeight = 12, + ethLogoHeight = 14, ethNumberOfDecimals, fiatNumberOfDecimals, numberOfDecimals: propsNumberOfDecimals, showEthLogo, type, + showFiat, ...restProps }) { const { currency, numberOfDecimals } = useUserPreferencedCurrency(type, { ethNumberOfDecimals, fiatNumberOfDecimals, numberOfDecimals: propsNumberOfDecimals, + showFiatOverride: showFiat, }); const prefixComponent = useMemo(() => { return ( currency === ETH && showEthLogo && ( - + ) ); }, [currency, showEthLogo, ethLogoHeight]); @@ -49,7 +57,7 @@ UserPreferencedCurrencyDisplay.propTypes = { hideTitle: PropTypes.bool, style: PropTypes.object, showEthLogo: PropTypes.bool, - ethLogoHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + ethLogoHeight: PropTypes.number, type: PropTypes.oneOf([PRIMARY, SECONDARY]), ethNumberOfDecimals: PropTypes.oneOfType([ PropTypes.string, @@ -59,4 +67,5 @@ UserPreferencedCurrencyDisplay.propTypes = { PropTypes.string, PropTypes.number, ]), + showFiat: PropTypes.boolean, }; diff --git a/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.stories.js b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.stories.js new file mode 100644 index 000000000..0fcdf1f9f --- /dev/null +++ b/ui/components/app/user-preferenced-currency-display/user-preferenced-currency-display.stories.js @@ -0,0 +1,60 @@ +import React from 'react'; +import { PRIMARY, SECONDARY, ETH } from '../../../helpers/constants/common'; + +import UserPreferencedCurrencyDisplay from '.'; + +export default { + title: 'Components/App/UserPreferencedCurrencyDisplay', + id: __filename, + argTypes: { + className: { + control: 'text', + }, + 'data-testid': { + control: 'text', + }, + prefix: { + control: 'text', + }, + value: { + control: 'text', + }, + numberOfDecimals: { + control: 'number', + }, + hideLabel: { + control: 'boolean', + }, + hideTitle: { + control: 'boolean', + }, + style: { + control: 'object', + }, + showEthLogo: { + control: 'boolean', + }, + ethLogoHeight: { + control: 'number', + }, + type: { + control: 'select', + options: [PRIMARY, SECONDARY], + }, + ethNumberOfDecimals: { + control: 'number', + }, + fiatNumberOfDecimals: { + control: 'number', + }, + }, + args: { + type: ETH, + }, +}; + +export const DefaultStory = (args) => ( + +); + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/app/wallet-overview/eth-overview.js b/ui/components/app/wallet-overview/eth-overview.js index 001f84694..95ef5aec6 100644 --- a/ui/components/app/wallet-overview/eth-overview.js +++ b/ui/components/app/wallet-overview/eth-overview.js @@ -10,10 +10,6 @@ import { SEND_ROUTE, BUILD_QUOTE_ROUTE, } from '../../../helpers/constants/routes'; -import { - useMetricEvent, - useNewMetricEvent, -} from '../../../hooks/useMetricEvent'; import Tooltip from '../../ui/tooltip'; import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display'; import { PRIMARY, SECONDARY } from '../../../helpers/constants/common'; @@ -34,25 +30,13 @@ import SendIcon from '../../ui/icon/overview-send-icon.component'; import { setSwapsFromToken } from '../../../ducks/swaps/swaps'; import IconButton from '../../ui/icon-button'; import { isHardwareKeyring } from '../../../helpers/utils/hardware'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; import WalletOverview from './wallet-overview'; const EthOverview = ({ className }) => { const dispatch = useDispatch(); const t = useContext(I18nContext); - const sendEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Home', - name: 'Clicked Send: Eth', - }, - }); - const depositEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Home', - name: 'Clicked Deposit', - }, - }); + const trackEvent = useContext(MetaMetricsContext); const history = useHistory(); const keyring = useSelector(getCurrentKeyring); const usingHardwareWallet = isHardwareKeyring(keyring?.type); @@ -63,12 +47,6 @@ const EthOverview = ({ className }) => { const isSwapsChain = useSelector(getIsSwapsChain); const isBuyableChain = useSelector(getIsBuyableChain); const primaryTokenImage = useSelector(getNativeCurrencyImage); - - const enteredSwapsEvent = useNewMetricEvent({ - event: 'Swaps Opened', - properties: { source: 'Main View', active_currency: 'ETH' }, - category: 'swaps', - }); const defaultSwapsToken = useSelector(getSwapsDefaultToken); return ( @@ -119,7 +97,14 @@ const EthOverview = ({ className }) => { disabled={!isBuyableChain} label={t('buy')} onClick={() => { - depositEvent(); + trackEvent({ + event: 'Clicked Deposit', + category: 'Navigation', + properties: { + action: 'Home', + legacy_event: true, + }, + }); dispatch(showModal({ name: 'DEPOSIT_ETHER' })); }} /> @@ -129,7 +114,14 @@ const EthOverview = ({ className }) => { Icon={SendIcon} label={t('send')} onClick={() => { - sendEvent(); + trackEvent({ + event: 'Clicked Send: Eth', + category: 'Navigation', + properties: { + action: 'Home', + legacy_event: true, + }, + }); history.push(SEND_ROUTE); }} /> @@ -139,7 +131,14 @@ const EthOverview = ({ className }) => { Icon={SwapIcon} onClick={() => { if (isSwapsChain) { - enteredSwapsEvent(); + trackEvent({ + event: 'Swaps Opened', + category: 'swaps', + properties: { + source: 'Main View', + active_currency: 'ETH', + }, + }); dispatch(setSwapsFromToken(defaultSwapsToken)); if (usingHardwareWallet) { global.platform.openExtensionInBrowser(BUILD_QUOTE_ROUTE); diff --git a/ui/components/app/wallet-overview/index.scss b/ui/components/app/wallet-overview/index.scss index b99ad8ca6..19a4db1a2 100644 --- a/ui/components/app/wallet-overview/index.scss +++ b/ui/components/app/wallet-overview/index.scss @@ -56,7 +56,7 @@ &__primary-balance { @include H2; - color: var(--black); + color: var(--color-text-default); width: 100%; justify-content: center; } @@ -67,19 +67,19 @@ &__cached-balance, &__cached-star { - color: var(--web-orange); + color: var(--color-secondary-default); } &__cached-secondary-balance { @include Paragraph; - color: rgba(220, 153, 18, 0.6901960784313725); + color: var(--color-secondary-muted); } &__secondary-balance { @include Paragraph; - color: var(--Grey-400); + color: var(--color-text-muted); } &__button { @@ -96,7 +96,7 @@ align-items: center; height: 36px; width: 36px; - background: #037dd6; + background: var(--color-primary-default); border-radius: 18px; margin-top: 6px; } @@ -117,7 +117,7 @@ &__primary-balance { @include H2; - color: var(--black); + color: var(--color-text-default); width: 100%; justify-content: center; } @@ -125,7 +125,7 @@ &__secondary-balance { @include H5; - color: var(--Grey-400); + color: var(--color-text-muted); } &__button { diff --git a/ui/components/app/wallet-overview/token-overview.js b/ui/components/app/wallet-overview/token-overview.js index 5fae06d63..c2860e727 100644 --- a/ui/components/app/wallet-overview/token-overview.js +++ b/ui/components/app/wallet-overview/token-overview.js @@ -12,13 +12,9 @@ import { SEND_ROUTE, BUILD_QUOTE_ROUTE, } from '../../../helpers/constants/routes'; -import { - useMetricEvent, - useNewMetricEvent, -} from '../../../hooks/useMetricEvent'; import { useTokenTracker } from '../../../hooks/useTokenTracker'; import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount'; -import { ASSET_TYPES, updateSendAsset } from '../../../ducks/send'; +import { updateSendAsset } from '../../../ducks/send'; import { setSwapsFromToken } from '../../../ducks/swaps/swaps'; import { getCurrentKeyring, @@ -31,18 +27,14 @@ import SendIcon from '../../ui/icon/overview-send-icon.component'; import IconButton from '../../ui/icon-button'; import { INVALID_ASSET_TYPE } from '../../../helpers/constants/error-keys'; import { showModal } from '../../../store/actions'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; +import { ASSET_TYPES } from '../../../../shared/constants/transaction'; import WalletOverview from './wallet-overview'; const TokenOverview = ({ className, token }) => { const dispatch = useDispatch(); const t = useContext(I18nContext); - const sendTokenEvent = useMetricEvent({ - eventOpts: { - category: 'Navigation', - action: 'Home', - name: 'Clicked Send: Token', - }, - }); + const trackEvent = useContext(MetaMetricsContext); const history = useHistory(); const keyring = useSelector(getCurrentKeyring); const usingHardwareWallet = isHardwareKeyring(keyring.type); @@ -55,11 +47,6 @@ const TokenOverview = ({ className, token }) => { token.symbol, ); const isSwapsChain = useSelector(getIsSwapsChain); - const enteredSwapsEvent = useNewMetricEvent({ - event: 'Swaps Opened', - properties: { source: 'Token View', active_currency: token.symbol }, - category: 'swaps', - }); useEffect(() => { if (token.isERC721 && process.env.COLLECTIBLES_V1) { @@ -95,7 +82,14 @@ const TokenOverview = ({ className, token }) => { { - sendTokenEvent(); + trackEvent({ + event: 'Clicked Send: Token', + category: 'Navigation', + properties: { + action: 'Home', + legacy_event: true, + }, + }); try { await dispatch( updateSendAsset({ @@ -121,10 +115,18 @@ const TokenOverview = ({ className, token }) => { Icon={SwapIcon} onClick={() => { if (isSwapsChain) { - enteredSwapsEvent(); + trackEvent({ + event: 'Swaps Opened', + category: 'swaps', + properties: { + source: 'Token View', + active_currency: token.symbol, + }, + }); dispatch( setSwapsFromToken({ ...token, + address: token.address.toLowerCase(), iconUrl: token.image, balance, string: balanceToRender, diff --git a/ui/components/app/whats-new-popup/index.scss b/ui/components/app/whats-new-popup/index.scss index 920375571..ba588d00e 100644 --- a/ui/components/app/whats-new-popup/index.scss +++ b/ui/components/app/whats-new-popup/index.scss @@ -9,7 +9,7 @@ display: flex; flex-direction: column; margin: 0 24px 24px 24px; - border-bottom: 1px solid var(--Grey-100); + border-bottom: 1px solid var(--color-border-muted); position: relative; } @@ -41,7 +41,7 @@ } &__notification-date { - color: var(--Grey-500); + color: var(--color-text-alternative); } &__button { @@ -56,7 +56,7 @@ &__link { @include H6; - color: var(--Blue-500); + color: var(--color-primary-default); cursor: pointer; } diff --git a/ui/components/app/whats-new-popup/whats-new-popup.js b/ui/components/app/whats-new-popup/whats-new-popup.js index 51b425b4c..bba492040 100644 --- a/ui/components/app/whats-new-popup/whats-new-popup.js +++ b/ui/components/app/whats-new-popup/whats-new-popup.js @@ -45,6 +45,10 @@ function getActionFunctionById(id, history) { updateViewedNotifications({ 8: true }); history.push(ADVANCED_ROUTE); }, + 10: () => { + updateViewedNotifications({ 10: true }); + history.push(`${ADVANCED_ROUTE}#token-description`); + }, }; return actionFunctions[id]; diff --git a/ui/components/ui/actionable-message/actionable-message.stories.js b/ui/components/ui/actionable-message/actionable-message.stories.js index 6ce95259a..b4087a115 100644 --- a/ui/components/ui/actionable-message/actionable-message.stories.js +++ b/ui/components/ui/actionable-message/actionable-message.stories.js @@ -110,7 +110,7 @@ export const WithIcon = (args) => ; WithIcon.args = { className: 'actionable-message--left-aligned actionable-message--warning', useIcon: true, - iconFillColor: '#f8c000', + iconFillColor: 'var(--color-waring-default)', }; export const PrimaryV2Action = (args) => ; @@ -119,7 +119,7 @@ PrimaryV2Action.args = { message: 'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.', useIcon: true, - iconFillColor: '#d73a49', + iconFillColor: 'var(--color-error-default)', type: 'danger', primaryActionV2: { label: 'I want to proceed anyway', diff --git a/ui/components/ui/actionable-message/index.scss b/ui/components/ui/actionable-message/index.scss index 0bfdda370..e9f3e7f1f 100644 --- a/ui/components/ui/actionable-message/index.scss +++ b/ui/components/ui/actionable-message/index.scss @@ -107,6 +107,11 @@ .actionable-message__action--secondary { text-decoration: underline; } + + button { + background: var(--color-warning-default); + color: var(--color-warning-inverse); + } } &--danger { @@ -120,6 +125,11 @@ .actionable-message__message { text-align: left; } + + button { + background: var(--color-error-default); + color: var(--color-error-inverse); + } } &--success { @@ -128,6 +138,11 @@ &::before { background: var(--color-success-muted); } + + button { + background: var(--color-success-default); + color: var(--color-success-inverse); + } } &--left-aligned { diff --git a/ui/components/ui/alert-circle-icon/README.mdx b/ui/components/ui/alert-circle-icon/README.mdx deleted file mode 100644 index c7d05782f..000000000 --- a/ui/components/ui/alert-circle-icon/README.mdx +++ /dev/null @@ -1,25 +0,0 @@ -import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; - -import AlertCircleIcon from '.'; - -# Alert Circle Icon - - - - - -## Component API - - - -## Usage - -The following describes the props and example usage for this component. - -### Warning - -Render warning alert icon - - - - diff --git a/ui/components/ui/alert-circle-icon/alert-circle-icon.component.js b/ui/components/ui/alert-circle-icon/alert-circle-icon.component.js deleted file mode 100644 index 689447810..000000000 --- a/ui/components/ui/alert-circle-icon/alert-circle-icon.component.js +++ /dev/null @@ -1,27 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import CircleIcon from '../circle-icon'; - -const typeConfig = { - danger: { - circleClass: 'alert-circle-icon--danger', - iconSource: 'images/icons/red-triangle-exclaim.svg', - }, - warning: { - circleClass: 'alert-circle-icon--warning', - iconSource: 'images/icons/yellow-bell.svg', - }, -}; - -export default class AlertCircleIcon extends Component { - static propTypes = { - /** - * giving different alert icon for the component - */ - type: PropTypes.oneOf(Object.keys(typeConfig)).isRequired, - }; - - render() { - return ; - } -} diff --git a/ui/components/ui/alert-circle-icon/alert-circle-icon.stories.js b/ui/components/ui/alert-circle-icon/alert-circle-icon.stories.js deleted file mode 100644 index c02480a64..000000000 --- a/ui/components/ui/alert-circle-icon/alert-circle-icon.stories.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import README from './README.mdx'; -import AlertCircleIcon from './alert-circle-icon.component'; - -export default { - title: 'Components/UI/AlertCircleIcon', - id: __filename, - component: AlertCircleIcon, - parameters: { - docs: { - page: README, - }, - }, - argTypes: { - type: { options: ['warning', 'danger'], control: { type: 'select' } }, - }, -}; - -export const DefaultStory = (args) => ; - -DefaultStory.storyName = 'Default'; - -export const Warning = (args) => ; - -Warning.args = { - type: 'warning', -}; diff --git a/ui/components/ui/alert-circle-icon/index.js b/ui/components/ui/alert-circle-icon/index.js deleted file mode 100644 index 80ab7a4c9..000000000 --- a/ui/components/ui/alert-circle-icon/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './alert-circle-icon.component'; diff --git a/ui/components/ui/alert-circle-icon/index.scss b/ui/components/ui/alert-circle-icon/index.scss deleted file mode 100644 index 82e746e77..000000000 --- a/ui/components/ui/alert-circle-icon/index.scss +++ /dev/null @@ -1,13 +0,0 @@ -.alert-circle-icon { - &--danger { - border-color: var(--accent-red); - color: var(--accent-red); - background: var(--Red-000); - } - - &--warning { - border-color: var(--accent-yellow); - color: var(--accent-yellow); - background: var(--Yellow-000); - } -} diff --git a/ui/components/ui/alert/index.scss b/ui/components/ui/alert/index.scss index 737244a2c..55e980705 100644 --- a/ui/components/ui/alert/index.scss +++ b/ui/components/ui/alert/index.scss @@ -1,14 +1,14 @@ .global-alert { position: relative; width: 100%; - background-color: #33a4e7; + background-color: var(--color-primary-default); .msg { @include H7; width: 100%; display: block; - color: white; + color: var(--color-primary-inverse); text-align: center; } } diff --git a/ui/components/ui/box/README.mdx b/ui/components/ui/box/README.mdx index 71f7dd42a..fe43d2380 100644 --- a/ui/components/ui/box/README.mdx +++ b/ui/components/ui/box/README.mdx @@ -91,34 +91,3 @@ import Box from '../ui/box'; COLORS.BORDER_DEFAULT ; ``` - -## Deprecated Colors - -List of deprecated background and border color props that are not theme compatible and should not be used. - -```js -/** !!! DEPRECATED DO NOT USE!!! */ -UI1: 'ui-1', -UI2: 'ui-2', -UI3: 'ui-3', -UI4: 'ui-4', -BLACK: 'black', -GREY: 'grey', -NEUTRAL_GREY: 'neutral-grey', -WHITE: 'white', -PRIMARY1: 'primary-1', -PRIMARY2: 'primary-2', -PRIMARY3: 'primary-3', -SECONDARY1: 'secondary-1', -SECONDARY2: 'secondary-2', -SECONDARY3: 'secondary-3', -SUCCESS1: 'success-1', -SUCCESS2: 'success-2', -SUCCESS3: 'success-3', -ERROR1: 'error-1', -ERROR2: 'error-2', -ERROR3: 'error-3', -ALERT1: 'alert-1', -ALERT2: 'alert-2', -ALERT3: 'alert-3', -``` diff --git a/ui/components/ui/breadcrumbs/breadcrumbs.component.js b/ui/components/ui/breadcrumbs/breadcrumbs.component.js index c00844aa1..986d709b0 100644 --- a/ui/components/ui/breadcrumbs/breadcrumbs.component.js +++ b/ui/components/ui/breadcrumbs/breadcrumbs.component.js @@ -21,7 +21,10 @@ export default class Breadcrumbs extends PureComponent { key={i} className="breadcrumb" style={{ - backgroundColor: i === currentIndex ? '#D8D8D8' : '#FFFFFF', + backgroundColor: + i === currentIndex + ? 'var(--color-background-alternative)' + : 'var(--color-background-default)', }} /> ))} diff --git a/ui/components/ui/breadcrumbs/breadcrumbs.component.test.js b/ui/components/ui/breadcrumbs/breadcrumbs.component.test.js index ec18a434a..73b9347ce 100644 --- a/ui/components/ui/breadcrumbs/breadcrumbs.component.test.js +++ b/ui/components/ui/breadcrumbs/breadcrumbs.component.test.js @@ -11,12 +11,12 @@ describe('Breadcrumbs Component', () => { expect(wrapper.find('.breadcrumb')).toHaveLength(3); expect( wrapper.find('.breadcrumb').at(0).props().style.backgroundColor, - ).toStrictEqual('#FFFFFF'); + ).toStrictEqual('var(--color-background-default)'); expect( wrapper.find('.breadcrumb').at(1).props().style.backgroundColor, - ).toStrictEqual('#D8D8D8'); + ).toStrictEqual('var(--color-background-alternative)'); expect( wrapper.find('.breadcrumb').at(2).props().style.backgroundColor, - ).toStrictEqual('#FFFFFF'); + ).toStrictEqual('var(--color-background-default)'); }); }); diff --git a/ui/components/ui/breadcrumbs/index.scss b/ui/components/ui/breadcrumbs/index.scss index e23aa7970..37b6727a3 100644 --- a/ui/components/ui/breadcrumbs/index.scss +++ b/ui/components/ui/breadcrumbs/index.scss @@ -6,7 +6,7 @@ .breadcrumb { height: 10px; width: 10px; - border: 1px solid #979797; + border: 1px solid var(--color-border-default); border-radius: 50%; } diff --git a/ui/components/ui/button-group/index.scss b/ui/components/ui/button-group/index.scss index 460e9eae5..bb77fc9f8 100644 --- a/ui/components/ui/button-group/index.scss +++ b/ui/components/ui/button-group/index.scss @@ -5,9 +5,8 @@ &__button { font-size: $font-size-h5; - color: var(--tundora); border-style: solid; - border-color: var(--alto); + border-color: var(--color-border-default); border-width: 1px 1px 1px; border-left: 0; flex: 1; @@ -17,7 +16,7 @@ text-overflow: ellipsis; &:first-child { - border-left: 1px solid var(--alto); + border-left: 1px solid var(--color-border-default); border-radius: 4px 0 0 4px; } @@ -26,8 +25,8 @@ } &--active { - background-color: var(--dodger-blue); - color: var(--white); + background-color: var(--color-primary-default); + color: var(--color-primary-inverse); } &:disabled { @@ -40,9 +39,9 @@ .radio-button { @include H7; - color: var(--Grey-700); - border: 1px solid var(--Grey-100); - background: var(--white); + color: var(--color-text-alternative); + border: 1px solid var(--color-border-muted); + background: var(--color-background-default); border-radius: 25px; height: 25px; margin-right: 8px; @@ -50,15 +49,15 @@ padding: 0; &--active { - background: var(--Blue-500); - color: white; + background: var(--color-primary-default); + color: var(--color-primary-inverse); border: none; } &--danger { - border: 1px solid var(--Red-500); - color: var(--Red-500); - background: var(--white); + border: 1px solid var(--color-error-default); + color: var(--color-error-default); + background: var(--color-background-default); } &:hover { @@ -67,8 +66,8 @@ } .radio-button--active.radio-button--danger { - border: 1px solid var(--Red-500); - color: white; - background: var(--Red-500); + border: 1px solid var(--color-error-default); + color: var(--color-error-inverse); + background: var(--color-error-default); } } diff --git a/ui/components/ui/button/buttons.scss b/ui/components/ui/button/buttons.scss index 5bbe4cd96..e0ea6dcfa 100644 --- a/ui/components/ui/button/buttons.scss +++ b/ui/components/ui/button/buttons.scss @@ -302,7 +302,7 @@ input[type="submit"][disabled] { .btn--inline { display: inline; - padding: 0 4px; + padding: 0; font-size: inherit; width: auto; color: var(--color-primary-default); diff --git a/ui/components/ui/card/card.js b/ui/components/ui/card/card.js index e4ecce4b0..d77d92081 100644 --- a/ui/components/ui/card/card.js +++ b/ui/components/ui/card/card.js @@ -48,7 +48,7 @@ Card.propTypes = { padding: Box.propTypes.padding, /** * The background color of the card - * Defaults to COLORS.WHITE + * Defaults to COLORS.BACKGROUND_DEFAULT */ backgroundColor: Box.propTypes.backgroundColor, /** diff --git a/ui/components/ui/card/card.stories.js b/ui/components/ui/card/card.stories.js index 3fe77495c..06ca63cde 100644 --- a/ui/components/ui/card/card.stories.js +++ b/ui/components/ui/card/card.stories.js @@ -162,8 +162,8 @@ DefaultStory.args = { padding: 4, border: true, borderWidth: 1, - borderColor: COLORS.UI2, + borderColor: COLORS.BORDER_DEFAULT, borderStyle: BORDER_STYLE.SOLID, - backgroundColor: COLORS.WHITE, + backgroundColor: COLORS.BACKGROUND_DEFAULT, display: DISPLAY.BLOCK, }; diff --git a/ui/components/ui/check-box/index.scss b/ui/components/ui/check-box/index.scss index 067dfb53e..a366d9136 100644 --- a/ui/components/ui/check-box/index.scss +++ b/ui/components/ui/check-box/index.scss @@ -1,8 +1,8 @@ .check-box { -webkit-appearance: none; appearance: none; - background: var(--white); - color: var(--Grey-100); + background: var(--color-background-default); + color: var(--color-icon-muted); width: 18px; height: 18px; font-size: 21px; @@ -12,12 +12,12 @@ &__checked, &__indeterminate { - color: var(--primary-blue); - border-color: var(--primary-blue); + color: var(--color-primary-default); + border-color: var(--color-primary-default); } &:disabled { - color: var(--Grey-100); + color: var(--color-icon-muted); cursor: not-allowed; } } diff --git a/ui/components/ui/circle-icon/README.mdx b/ui/components/ui/circle-icon/README.mdx deleted file mode 100644 index ee4a95b7d..000000000 --- a/ui/components/ui/circle-icon/README.mdx +++ /dev/null @@ -1,16 +0,0 @@ -import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; - -import CircleIcon from '.'; - -# Circle Icon - -Add circle border to the image component - - - - - -## Component API - - - diff --git a/ui/components/ui/circle-icon/circle-icon.component.js b/ui/components/ui/circle-icon/circle-icon.component.js deleted file mode 100644 index c772941c4..000000000 --- a/ui/components/ui/circle-icon/circle-icon.component.js +++ /dev/null @@ -1,60 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; - -export default class CircleIcon extends PureComponent { - static propTypes = { - /** - * add size (px) for the image container - */ - size: PropTypes.string, - /** - * add css classname for the component based on the parent css - */ - circleClass: PropTypes.string, - /** - * image source path - */ - iconSource: PropTypes.string.isRequired, - /** - * add size (px) for the image - */ - iconSize: PropTypes.string, - }; - - static defaultProps = { - size: '56px', - iconSize: '18px', - circleClass: '', - }; - - render() { - const { size, circleClass, iconSize, iconSource } = this.props; - - return ( -
-
- -
- ); - } -} diff --git a/ui/components/ui/circle-icon/circle-icon.stories.js b/ui/components/ui/circle-icon/circle-icon.stories.js deleted file mode 100644 index 90c63d817..000000000 --- a/ui/components/ui/circle-icon/circle-icon.stories.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import README from './README.mdx'; -import CircleIcon from './circle-icon.component'; - -export default { - title: 'Components/UI/CircleIcon', - id: __filename, - component: CircleIcon, - parameters: { - docs: { - page: README, - }, - }, - argTypes: { - size: { control: 'text' }, - circleClass: { control: 'text' }, - iconSource: { control: 'text' }, - iconSize: { control: 'text' }, - }, -}; - -export const DefaultStory = (args) => ( - -); - -DefaultStory.storyName = 'Default'; - -DefaultStory.args = { - iconSize: '42px', - iconSource: 'images/eth_logo.svg', -}; diff --git a/ui/components/ui/circle-icon/index.js b/ui/components/ui/circle-icon/index.js deleted file mode 100644 index 7a4964c71..000000000 --- a/ui/components/ui/circle-icon/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './circle-icon.component'; diff --git a/ui/components/ui/circle-icon/index.scss b/ui/components/ui/circle-icon/index.scss deleted file mode 100644 index 1ffa5323d..000000000 --- a/ui/components/ui/circle-icon/index.scss +++ /dev/null @@ -1,23 +0,0 @@ -.circle-icon { - &__container { - position: relative; - display: flex; - justify-content: center; - align-items: center; - } - - &__border { - border-radius: 50%; - position: absolute; - } - - &__circle { - border: 1px solid; - border-color: var(--black); - background: var(--white); - } - - &__icon { - position: relative; - } -} diff --git a/ui/components/ui/color-indicator/color-indicator.js b/ui/components/ui/color-indicator/color-indicator.js index 71e15706f..00cf3932d 100644 --- a/ui/components/ui/color-indicator/color-indicator.js +++ b/ui/components/ui/color-indicator/color-indicator.js @@ -6,7 +6,7 @@ import { COLORS, SIZES } from '../../../helpers/constants/design-system'; export default function ColorIndicator({ size = SIZES.SM, type = 'outlined', - color = COLORS.UI4, + color = COLORS.ICON_DEFAULT, borderColor, iconClassName, }) { diff --git a/ui/components/ui/color-indicator/color-indicator.scss b/ui/components/ui/color-indicator/color-indicator.scss index ebd1b4668..b8bc064a9 100644 --- a/ui/components/ui/color-indicator/color-indicator.scss +++ b/ui/components/ui/color-indicator/color-indicator.scss @@ -49,7 +49,7 @@ $sizes: ( background-color: var($color); } & #{$self}__icon { - color: var(--black); + color: var(--color-icon-default); } } } diff --git a/ui/components/ui/color-indicator/color-indicator.stories.js b/ui/components/ui/color-indicator/color-indicator.stories.js index a458afcd7..a0f87c535 100644 --- a/ui/components/ui/color-indicator/color-indicator.stories.js +++ b/ui/components/ui/color-indicator/color-indicator.stories.js @@ -25,7 +25,7 @@ export default { type: 'select', }, options: COLORS, - defaultValue: COLORS.PRIMARY1, + defaultValue: COLORS.PRIMARY_DEFAULT, }, borderColor: { control: { diff --git a/ui/components/ui/confusable/index.scss b/ui/components/ui/confusable/index.scss index 09c751e57..06552627d 100644 --- a/ui/components/ui/confusable/index.scss +++ b/ui/components/ui/confusable/index.scss @@ -1,5 +1,5 @@ .confusable { &__point { - color: var(--Red-500); + color: var(--color-error-default); } } diff --git a/ui/components/ui/currency-display/currency-display.component.js b/ui/components/ui/currency-display/currency-display.component.js index b7bc3108b..3a62ce396 100644 --- a/ui/components/ui/currency-display/currency-display.component.js +++ b/ui/components/ui/currency-display/currency-display.component.js @@ -35,7 +35,9 @@ export default function CurrencyDisplay({ style={style} title={(!hideTitle && title) || null} > - {prefixComponent} + + {prefixComponent} + {parts.prefix} {parts.value} diff --git a/ui/components/ui/currency-display/index.scss b/ui/components/ui/currency-display/index.scss index 074a65df3..8f9ebf301 100644 --- a/ui/components/ui/currency-display/index.scss +++ b/ui/components/ui/currency-display/index.scss @@ -10,6 +10,10 @@ text-overflow: ellipsis; } + &__prefix { + padding-right: 4px; + } + &__suffix { padding-left: 4px; } diff --git a/ui/components/ui/definition-list/definition-list.scss b/ui/components/ui/definition-list/definition-list.scss index 6443ca97e..1517d46b7 100644 --- a/ui/components/ui/definition-list/definition-list.scss +++ b/ui/components/ui/definition-list/definition-list.scss @@ -6,7 +6,7 @@ align-items: center; & i { - color: var(--ui-3); + color: var(--color-icon-default); margin-left: 6px; font-size: $font-size-h8; } diff --git a/ui/components/ui/definition-list/definition-list.stories.js b/ui/components/ui/definition-list/definition-list.stories.js index 68f6a91f9..e2d351b01 100644 --- a/ui/components/ui/definition-list/definition-list.stories.js +++ b/ui/components/ui/definition-list/definition-list.stories.js @@ -58,7 +58,7 @@ export const WithTypographyControl = () => ( gapSize={select('gapSize', SIZES, SIZES.SM)} termTypography={{ variant: select('termTypography.variant', TYPOGRAPHY, TYPOGRAPHY.H6), - color: select('termTypography.color', COLORS, COLORS.BLACK), + color: select('termTypography.color', COLORS, COLORS.TEXT_DEFAULT), }} definitionTypography={{ variant: select( @@ -66,7 +66,7 @@ export const WithTypographyControl = () => ( TYPOGRAPHY, TYPOGRAPHY.H6, ), - color: select('definitionTypography.color', COLORS, COLORS.BLACK), + color: select('definitionTypography.color', COLORS, COLORS.TEXT_DEFAULT), }} /> ); diff --git a/ui/components/ui/dialog/dialog.scss b/ui/components/ui/dialog/dialog.scss index c22ee3a78..0b2a75bfb 100644 --- a/ui/components/ui/dialog/dialog.scss +++ b/ui/components/ui/dialog/dialog.scss @@ -2,25 +2,24 @@ @include H7; padding: 1rem; - border: 1px solid var(--black); box-sizing: border-box; border-radius: 8px; &--message { - border-color: var(--Blue-200); - color: var(--Blue-600); - background-color: var(--Blue-000); + color: var(--color-text-default); + background-color: var(--color-info-muted); + border: 1px solid var(--color-info-default); } &--error { - border-color: var(--Red-300); - color: var(--Red-600); - background-color: var(--Red-000); + color: 1px solid var(--color-text-default); + background-color: var(--color-error-muted); + border: 1px solid var(--color-error-default); } &--warning { - border-color: var(--Orange-300); - color: var(--Orange-600); - background-color: var(--Orange-000); + color: 1px solid var(--color-text-default); + background-color: var(--color-warning-muted); + border: 1px solid var(--color-warning-default); } } diff --git a/ui/components/ui/disclosure/disclosure.js b/ui/components/ui/disclosure/disclosure.js index 9e3d56ebc..534787366 100644 --- a/ui/components/ui/disclosure/disclosure.js +++ b/ui/components/ui/disclosure/disclosure.js @@ -22,8 +22,9 @@ const Disclosure = ({ children, title, size }) => {
setOpen((state) => !state)}> {title ? (
- - {title}:{' '} + + + {title}
{children} diff --git a/ui/components/ui/disclosure/disclosure.scss b/ui/components/ui/disclosure/disclosure.scss index 7fa87806b..a02f76b3b 100644 --- a/ui/components/ui/disclosure/disclosure.scss +++ b/ui/components/ui/disclosure/disclosure.scss @@ -7,26 +7,18 @@ position: relative; padding-left: 24px; padding-bottom: 10px; + font-weight: bold; + + i { + margin-inline-start: -20px; + margin-inline-end: 10px; + } &::-webkit-details-marker, &::marker { display: none; content: ''; } - - &::before { - position: absolute; - content: ' '; - flex: 0 0 auto; - height: 16px; - width: 16px; - background-image: url('images/icons/expand.svg'); - background-size: contain; - background-repeat: no-repeat; - cursor: pointer; - left: 0; - top: 2px; - } } &__content { diff --git a/ui/components/ui/editable-label/index.scss b/ui/components/ui/editable-label/index.scss index e704fafb2..fc2c28b08 100644 --- a/ui/components/ui/editable-label/index.scss +++ b/ui/components/ui/editable-label/index.scss @@ -16,10 +16,10 @@ width: 250px; text-align: center; - border: 1px solid var(--alto); + border: 1px solid var(--color-border-default); &--error { - border: 1px solid var(--monzo); + border: 1px solid var(--color-error-default); } } @@ -32,14 +32,14 @@ &__icon { cursor: pointer; - color: var(--dusty-gray); + color: var(--color-icon-default); } &__error { @include H7; left: 8px; - color: var(--red); + color: var(--color-error-default); } &__error-amount { diff --git a/ui/components/ui/error-message/error-message.component.js b/ui/components/ui/error-message/error-message.component.js index ceaf354db..dd1e16c5c 100644 --- a/ui/components/ui/error-message/error-message.component.js +++ b/ui/components/ui/error-message/error-message.component.js @@ -7,11 +7,7 @@ const ErrorMessage = (props, context) => { return (
- +
{error}
); diff --git a/ui/components/ui/error-message/index.scss b/ui/components/ui/error-message/index.scss index 80f38bc3c..42defef6e 100644 --- a/ui/components/ui/error-message/index.scss +++ b/ui/components/ui/error-message/index.scss @@ -2,9 +2,9 @@ @include H7; min-height: 32px; - border: 1px solid var(--Red-300); - color: var(--ui-black); - background: var(--error-2); + border: 1px solid var(--color-error-default); + color: var(--color-text-default); + background-color: var(--color-error-muted); border-radius: 8px; display: flex; justify-content: flex-start; @@ -14,6 +14,7 @@ &__icon { margin-right: 8px; flex: 0 0 auto; + color: var(--color-error-default); } &__text { diff --git a/ui/components/ui/export-text-container/export-text-container.component.js b/ui/components/ui/export-text-container/export-text-container.component.js index 8eb56cf8b..7223305c0 100644 --- a/ui/components/ui/export-text-container/export-text-container.component.js +++ b/ui/components/ui/export-text-container/export-text-container.component.js @@ -21,7 +21,7 @@ function ExportTextContainer({ text = '' }) { handleCopy(text); }} > - +
{copied ? t('copiedExclamation') : t('copyToClipboard')}
diff --git a/ui/components/ui/export-text-container/index.scss b/ui/components/ui/export-text-container/index.scss index c1ac0adec..8d01f0113 100644 --- a/ui/components/ui/export-text-container/index.scss +++ b/ui/components/ui/export-text-container/index.scss @@ -3,7 +3,7 @@ justify-content: center; flex-direction: column; align-items: center; - border: 1px solid var(--alto); + border: 1px solid var(--color-border-default); border-radius: 4px; font-weight: 400; @@ -13,7 +13,7 @@ justify-content: center; padding: 20px; border-radius: 4px; - background: var(--alabaster); + background: var(--color-background-default); } &__text { @@ -21,14 +21,14 @@ resize: none; border: none; - background: var(--alabaster); + background: var(--color-background-default); text-align: center; } &__buttons-container { display: flex; flex-direction: row; - border-top: 1px solid var(--alto); + border-top: 1px solid var(--color-border-default); width: 100%; } @@ -41,10 +41,10 @@ justify-content: center; align-items: center; cursor: pointer; - color: var(--primary-blue); + color: var(--color-primary-default); &--copy { - border-right: 1px solid var(--alto); + border-right: 1px solid var(--color-border-default); } } diff --git a/ui/components/ui/form-field/form-field.stories.js b/ui/components/ui/form-field/form-field.stories.js index 3d5ee3b7f..0c7d0c36f 100644 --- a/ui/components/ui/form-field/form-field.stories.js +++ b/ui/components/ui/form-field/form-field.stories.js @@ -55,7 +55,11 @@ export const FormFieldWithTitleDetail = (args) => { text:
Detail
, button: (
diff --git a/ui/components/ui/numeric-input/numeric-input.scss b/ui/components/ui/numeric-input/numeric-input.scss index f90d3de87..a72a951ba 100644 --- a/ui/components/ui/numeric-input/numeric-input.scss +++ b/ui/components/ui/numeric-input/numeric-input.scss @@ -4,7 +4,7 @@ border-radius: 6px; &--error { - border-color: var(--error-1); + border-color: var(--color-error-default); } input { diff --git a/ui/components/ui/page-container/index.scss b/ui/components/ui/page-container/index.scss index 45f5f8854..8f37ff560 100644 --- a/ui/components/ui/page-container/index.scss +++ b/ui/components/ui/page-container/index.scss @@ -1,6 +1,6 @@ .page-container { width: 408px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 0 7px 0 rgba(0, 0, 0, 0.08); z-index: 25; display: flex; @@ -11,7 +11,7 @@ &__header { display: flex; flex-flow: column; - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-muted); padding: 16px; flex: 0 0 auto; position: relative; @@ -22,7 +22,7 @@ } &__header-close { - color: var(--tundora); + color: var(--color-icon-default); position: absolute; top: 16px; right: 16px; @@ -54,7 +54,7 @@ display: flex; flex-flow: column; justify-content: center; - border-top: 1px solid var(--geyser); + border-top: 1px solid var(--color-border-muted); flex: 0 0 auto; footer { @@ -79,7 +79,7 @@ text-decoration: none; cursor: pointer; text-transform: uppercase; - color: #2f9ae0; + color: var(--color-primary-alternative); } } } @@ -95,14 +95,14 @@ &__back-button { @include Paragraph; - color: #2f9ae0; + color: var(--color-primary-default); cursor: pointer; } &__title { @include H2; - color: var(--black); + color: var(--color-text-default); font-weight: 500; margin-right: 1.5rem; @@ -115,7 +115,7 @@ @include H6; padding-top: 0.5rem; - color: var(--gray); + color: var(--color-text-alternative); } &__tabs { @@ -127,7 +127,7 @@ @include Paragraph; min-width: 5rem; - color: var(--dusty-gray); + color: var(--color-text-alternative); border-bottom: none; margin-right: 16px; @@ -152,7 +152,7 @@ } &__warning-container { - background: var(--linen); + background: var(--color-warning-muted); padding: 20px; display: flex; align-items: start; @@ -193,7 +193,7 @@ .page-container { height: 100%; width: 100%; - background-color: var(--white); + background-color: var(--color-background-default); border-radius: 0; flex: 1; overflow-y: auto; diff --git a/ui/components/ui/popover/index.scss b/ui/components/ui/popover/index.scss index 9e0142075..d51d02a31 100644 --- a/ui/components/ui/popover/index.scss +++ b/ui/components/ui/popover/index.scss @@ -7,7 +7,7 @@ ::-webkit-scrollbar-thumb { -webkit-border-radius: 10px; border-radius: 10px; - background: #c4c4c4; + background: var(--color-icon-muted); } display: flex; @@ -17,7 +17,7 @@ max-height: 94vh; box-shadow: 0 4px 30px rgba(0, 0, 0, 0.25); border-radius: 10px; - background: white; + background: var(--color-background-default); } &-header { @@ -56,6 +56,7 @@ background: none; font-size: inherit; padding: 0; + color: var(--color-icon-default); } i { @@ -66,7 +67,7 @@ &-bg { width: 100%; height: 100%; - background: black; + background: var(--color-overlay-alternative); opacity: 0.2; } @@ -90,7 +91,7 @@ } &-footer { - border-top: 1px solid #d2d8dd; + border-top: 1px solid var(--color-border-muted); > :only-child { margin: 0 auto; @@ -100,7 +101,7 @@ &-arrow { width: 22px; height: 22px; - background: white; + background: var(--color-background-default); position: absolute; transform: rotate(45deg); box-shadow: 0 4px 30px rgba(0, 0, 0, 0.25); diff --git a/ui/components/ui/popover/popover.component.js b/ui/components/ui/popover/popover.component.js index 2cdaee0d5..68c927b5e 100644 --- a/ui/components/ui/popover/popover.component.js +++ b/ui/components/ui/popover/popover.component.js @@ -6,6 +6,7 @@ import { useI18nContext } from '../../../hooks/useI18nContext'; import Box from '../box'; import { ALIGN_ITEMS, + COLORS, FLEX_DIRECTION, JUSTIFY_CONTENT, } from '../../../helpers/constants/design-system'; @@ -14,7 +15,7 @@ const defaultHeaderProps = { padding: [6, 4, 4], display: 'flex', flexDirection: FLEX_DIRECTION.COLUMN, - backgroundColor: 'white', + backgroundColor: COLORS.BACKGROUND_DEFAULT, borderRadius: 'xl', }; diff --git a/ui/components/ui/pulse-loader/index.scss b/ui/components/ui/pulse-loader/index.scss index 8f79d70fe..28da065ae 100644 --- a/ui/components/ui/pulse-loader/index.scss +++ b/ui/components/ui/pulse-loader/index.scss @@ -4,7 +4,7 @@ &__loading-dot-one, &__loading-dot-two, &__loading-dot-three { - background: var(--Blue-500); + background: var(--color-primary-default); width: 9px; height: 9px; margin-right: 2px; diff --git a/ui/components/ui/qr-code/index.scss b/ui/components/ui/qr-code/index.scss index 62230303f..05a27b7e2 100644 --- a/ui/components/ui/qr-code/index.scss +++ b/ui/components/ui/qr-code/index.scss @@ -8,20 +8,20 @@ @include Paragraph; margin-top: 18px; - color: #4d4d4d; + color: var(--color-text-muted); } &__message { @include H7; - color: #f7861c; + color: var(--color-warning-default); } &__error { display: flex; justify-content: center; align-items: center; - color: #f7861c; + color: var(--color-error-default); margin-bottom: 9px; } @@ -37,7 +37,7 @@ cursor: pointer; .qr-code__copy-icon__svg { - fill: var(--primary-1); + fill: var(--color-primary-default); } } } @@ -45,7 +45,7 @@ &__address { @include H7; - background-color: var(--Grey-000); + background-color: var(--color-background-alternative); width: 76%; padding: 8px 12px; word-break: break-all; @@ -59,7 +59,7 @@ right: 24px; &__svg { - fill: var(--ui-5); + fill: var(--color-icon-default); } } } diff --git a/ui/components/ui/qr-code/qr-code.js b/ui/components/ui/qr-code/qr-code.js index 69921e260..d41364113 100644 --- a/ui/components/ui/qr-code/qr-code.js +++ b/ui/components/ui/qr-code/qr-code.js @@ -49,7 +49,7 @@ function QrCodeView(props) { ) : ( header )} - {warning ? {warning} : null} + {warning ? {warning} : null}
message
, + data: 'data', + }, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/ui/radio-group/index.scss b/ui/components/ui/radio-group/index.scss index 06d0ed554..43585036a 100644 --- a/ui/components/ui/radio-group/index.scss +++ b/ui/components/ui/radio-group/index.scss @@ -36,28 +36,28 @@ &__column-start-connector { width: calc(50% + 0.5px); height: 6px; - border-left: 1px solid var(--ui-2); - border-bottom: 1px solid var(--ui-2); + border-left: 1px solid var(--color-border-muted); + border-bottom: 1px solid var(--color-border-muted); align-self: flex-end; } &__column-end-connector { width: calc(50% + 0.5px); height: 6px; - border-right: 1px solid var(--ui-2); - border-bottom: 1px solid var(--ui-2); + border-right: 1px solid var(--color-border-muted); + border-bottom: 1px solid var(--color-border-muted); align-self: flex-start; } &__column-vertical-line { width: 1px; height: 5px; - border-left: 1px solid var(--ui-2); + border-left: 1px solid var(--color-border-muted); } &__column-horizontal-line { height: 1px; - border-bottom: 1px solid var(--ui-2); + border-bottom: 1px solid var(--color-border-muted); width: 100%; } diff --git a/ui/components/ui/radio-group/radio-group.component.js b/ui/components/ui/radio-group/radio-group.component.js index 0704de2a2..dce4e09b9 100644 --- a/ui/components/ui/radio-group/radio-group.component.js +++ b/ui/components/ui/radio-group/radio-group.component.js @@ -69,7 +69,7 @@ export default function RadioGroup({ options, name, selectedValue, onChange }) { isLast={index === options.length - 1} />
- +
) : ( diff --git a/ui/components/ui/slider/index.scss b/ui/components/ui/slider/index.scss index b4d89ae2a..bf2d17f95 100644 --- a/ui/components/ui/slider/index.scss +++ b/ui/components/ui/slider/index.scss @@ -35,7 +35,7 @@ border: none; background: none; font-size: 12px; - color: var(--Blue-500); + color: var(--color-primary-default); &:focus { outline: none; diff --git a/ui/components/ui/slider/slider.component.js b/ui/components/ui/slider/slider.component.js index 69c05ba26..9061dee58 100644 --- a/ui/components/ui/slider/slider.component.js +++ b/ui/components/ui/slider/slider.component.js @@ -19,12 +19,12 @@ const styles = { }, rail: { borderRadius: 50, - background: '#D6D9DC', + background: 'var(--color-background-alternative)', height: 6, }, track: { borderRadius: 50, - background: '#037DD6', + background: 'var(--color-primary-default)', height: 6, }, thumb: { @@ -32,8 +32,8 @@ const styles = { width: 20, marginTop: -7, marginLeft: -7, - backgroundColor: '#037DD6', - border: '1px solid #EAF6FF', + backgroundColor: 'var(--color-primary-default)', + border: '1px solid var(--color-border-muted)', boxSizing: 'border-box', boxShadow: '0px 0px 14px 0px rgba(0, 0, 0, 0.18)', '&:focus, &$active': { diff --git a/ui/components/ui/snackbar/index.scss b/ui/components/ui/snackbar/index.scss index c048f174b..62e66161f 100644 --- a/ui/components/ui/snackbar/index.scss +++ b/ui/components/ui/snackbar/index.scss @@ -2,10 +2,10 @@ @include H7; padding: 0.75rem 1rem; - color: var(--Blue-600); + color: var(--color-text-default); min-width: 360px; width: fit-content; - background: var(--Blue-000); - border: 1px solid var(--Blue-200); + background: var(--color-info-muted); + border: 1px solid var(--color-info-default); border-radius: 6px; } diff --git a/ui/components/ui/spinner/spinner.component.js b/ui/components/ui/spinner/spinner.component.js index 634398823..58af19aff 100644 --- a/ui/components/ui/spinner/spinner.component.js +++ b/ui/components/ui/spinner/spinner.component.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -const Spinner = ({ className = '', color = '#000000' }) => { +const Spinner = ({ className = '', color = 'var(--color-text-default)' }) => { return (
{ resize: RESIZE.BOTH, scrollable: false, boxProps: { - borderColor: COLORS.UI3, + borderColor: COLORS.BORDER_MUTED, borderRadius: SIZES.SM, borderStyle: BORDER_STYLE.SOLID, padding: [2, 4], diff --git a/ui/components/ui/toggle-button/index.scss b/ui/components/ui/toggle-button/index.scss index 57bbd4505..503a7f1d0 100644 --- a/ui/components/ui/toggle-button/index.scss +++ b/ui/components/ui/toggle-button/index.scss @@ -1,5 +1,7 @@ .toggle-button { display: flex; + cursor: pointer; + $self: &; &__status { diff --git a/ui/components/ui/toggle-button/toggle-button.component.js b/ui/components/ui/toggle-button/toggle-button.component.js index 271707cbe..c14cdbcef 100644 --- a/ui/components/ui/toggle-button/toggle-button.component.js +++ b/ui/components/ui/toggle-button/toggle-button.component.js @@ -8,7 +8,7 @@ const trackStyle = { height: '24px', padding: '0px', borderRadius: '26px', - border: '2px solid rgb(3, 125, 214)', + border: '2px solid var(--color-primary-default)', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -16,7 +16,7 @@ const trackStyle = { const offTrackStyle = { ...trackStyle, - border: '2px solid #8E8E8E', + border: '2px solid var(--color-border-default)', }; const thumbStyle = { @@ -37,12 +37,12 @@ const colors = { base: '#6A737D', }, active: { - base: '#ffffff', - hover: '#ffffff', + base: '#F2F4F6', + hover: '#F2F4F6', }, inactive: { - base: '#DADADA', - hover: '#DADADA', + base: '#F2F4F6', + hover: '#F2F4F6', }, }; @@ -52,7 +52,7 @@ const ToggleButton = (props) => { const modifier = value ? 'on' : 'off'; return ( -
{ if (e.key === 'Enter') { @@ -82,7 +82,7 @@ const ToggleButton = (props) => { {offLabel} {onLabel}
-
+ ); }; diff --git a/ui/components/ui/tooltip/index.scss b/ui/components/ui/tooltip/index.scss index cc0ca470d..fe1e5819a 100644 --- a/ui/components/ui/tooltip/index.scss +++ b/ui/components/ui/tooltip/index.scss @@ -1,6 +1,6 @@ .tippy-tooltip.white-theme { - background: white; - color: black; + background: var(--color-background-default); + color: var(--color-text-default); box-shadow: 0 0 14px rgba(0, 0, 0, 0.18); padding: 12px 16px; padding-bottom: 11px; @@ -9,22 +9,22 @@ @include H7; text-align: left; - color: var(--Grey-500); + color: var(--color-text-alternative); } } .tippy-popper[x-placement^=top] .white-theme [x-arrow] { - border-top-color: var(--white); + border-top-color: var(--color-background-default); } .tippy-popper[x-placement^=right] .white-theme [x-arrow] { - border-right-color: var(--white); + border-right-color: var(--color-background-default); } .tippy-popper[x-placement^=left] .white-theme [x-arrow] { - border-left-color: var(--white); + border-left-color: var(--color-background-default); } .tippy-popper[x-placement^=bottom] .white-theme [x-arrow] { - border-bottom-color: var(--white); + border-bottom-color: var(--color-background-default); } diff --git a/ui/components/ui/typography/README.mdx b/ui/components/ui/typography/README.mdx index 552b26573..c9e0dee7e 100644 --- a/ui/components/ui/typography/README.mdx +++ b/ui/components/ui/typography/README.mdx @@ -108,37 +108,6 @@ import { COLORS} from '../../../helpers/constants/design-system'; ``` -## Deprecated Colors - -List of deprecated color props that are not theme compatible and should not be used. - -```js -/** !!! DEPRECATED DO NOT USE!!! */ -UI1: 'ui-1', -UI2: 'ui-2', -UI3: 'ui-3', -UI4: 'ui-4', -BLACK: 'black', -GREY: 'grey', -NEUTRAL_GREY: 'neutral-grey', -WHITE: 'white', -PRIMARY1: 'primary-1', -PRIMARY2: 'primary-2', -PRIMARY3: 'primary-3', -SECONDARY1: 'secondary-1', -SECONDARY2: 'secondary-2', -SECONDARY3: 'secondary-3', -SUCCESS1: 'success-1', -SUCCESS2: 'success-2', -SUCCESS3: 'success-3', -ERROR1: 'error-1', -ERROR2: 'error-2', -ERROR3: 'error-3', -ALERT1: 'alert-1', -ALERT2: 'alert-2', -ALERT3: 'alert-3', -``` - ### Font Weight Use the `fontWeight` prop and the `FONT_WEIGHT` object from `./ui/helpers/constants/design-system.js` to change the font weight of the Typography component. There are 2 font weights: diff --git a/ui/components/ui/typography/typography.js b/ui/components/ui/typography/typography.js index 166ad4ac1..55224a295 100644 --- a/ui/components/ui/typography/typography.js +++ b/ui/components/ui/typography/typography.js @@ -29,32 +29,6 @@ export const ValidColors = [ COLORS.WARNING_INVERSE, COLORS.INFO_DEFAULT, COLORS.INFO_INVERSE, - /** - * COLORS BELOW HAVE BEEN DEPRECATED - */ - COLORS.UI1, - COLORS.UI2, - COLORS.UI3, - COLORS.UI4, - COLORS.BLACK, - COLORS.GREY, - COLORS.NEUTRAL_GREY, - COLORS.WHITE, - COLORS.PRIMARY1, - COLORS.PRIMARY2, - COLORS.PRIMARY3, - COLORS.SECONDARY1, - COLORS.SECONDARY2, - COLORS.SECONDARY3, - COLORS.SUCCESS1, - COLORS.SUCCESS2, - COLORS.SUCCESS3, - COLORS.ERROR1, - COLORS.ERROR2, - COLORS.ERROR3, - COLORS.ALERT1, - COLORS.ALERT2, - COLORS.ALERT3, ]; export const ValidTags = [ diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss index 0a3b51552..ff57d3769 100644 --- a/ui/components/ui/ui-components.scss +++ b/ui/components/ui/ui-components.scss @@ -2,7 +2,6 @@ @import 'account-mismatch-warning/index'; @import 'account-list/index'; @import 'actionable-message/index'; -@import 'alert-circle-icon/index'; @import 'alert/index'; @import 'box/box'; @import 'breadcrumbs/index'; @@ -11,7 +10,6 @@ @import 'callout/callout'; @import 'check-box/index'; @import 'chip/chip'; -@import 'circle-icon/index'; @import 'color-indicator/color-indicator'; @import 'confusable/index'; @import 'currency-display/index'; diff --git a/ui/components/ui/unit-input/index.scss b/ui/components/ui/unit-input/index.scss index e1f01eb03..a6b29a267 100644 --- a/ui/components/ui/unit-input/index.scss +++ b/ui/components/ui/unit-input/index.scss @@ -5,10 +5,10 @@ flex-flow: row nowrap; align-items: center; min-height: 54px; - border: 1px solid #dedede; + border: 1px solid var(--color-border-default); border-radius: 4px; - background-color: #fff; - color: #4d4d4d; + background-color: var(--color-background-default); + color: var(--color-text-default); padding: 8px 10px; position: relative; @@ -37,16 +37,13 @@ &__input { @include Paragraph; - color: #4d4d4d; + color: var(--color-text-default); + background: transparent; border: none; max-width: 15ch; overflow: hidden; text-overflow: ellipsis; height: 16px; - - &__disabled { - background-color: rgb(222, 222, 222); - } } &__input-container { @@ -62,10 +59,6 @@ } &--error { - border-color: var(--red); - } - - &__disabled { - background-color: #f2f3f4; + border-color: var(--color-error-default); } } diff --git a/ui/components/ui/update-nickname-popover/index.scss b/ui/components/ui/update-nickname-popover/index.scss index 650edf11d..a3684a68f 100644 --- a/ui/components/ui/update-nickname-popover/index.scss +++ b/ui/components/ui/update-nickname-popover/index.scss @@ -8,7 +8,7 @@ width: auto; .popover-header { - border-bottom: 1px solid #d2d8dd; + border-bottom: 1px solid var(--color-border-muted); margin-bottom: 16px; border-radius: 10px; } @@ -42,7 +42,7 @@ &__address { margin-top: 8px; font-size: 13px; - color: #bbc0c5; + color: var(--color-text-alternative); margin-bottom: 16px; overflow-wrap: break-word; } @@ -51,13 +51,13 @@ &__label--capitalized { text-transform: capitalize; margin-top: 16px; - color: #24292e; + color: var(--color-text-default); font-size: 14px; } &__nickname-label { margin-bottom: 8px; - color: #24292e; + color: var(--color-text-default); font-size: 14px; } } diff --git a/ui/components/ui/url-icon/index.scss b/ui/components/ui/url-icon/index.scss index ff4266f90..7753d667e 100644 --- a/ui/components/ui/url-icon/index.scss +++ b/ui/components/ui/url-icon/index.scss @@ -3,7 +3,7 @@ height: 24px; background-position: center; border-radius: 50%; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba($black, 0.24); flex: 0 0 auto; -moz-animation: fadein 1s; @@ -18,7 +18,7 @@ width: 24px; height: 24px; border-radius: 50%; - background: #bbc0c5; + background: var(--color-background-alternative); flex: 0 1 auto; display: flex; justify-content: center; diff --git a/ui/contexts/metametrics.js b/ui/contexts/metametrics.js index bb23afef8..869cb84e2 100644 --- a/ui/contexts/metametrics.js +++ b/ui/contexts/metametrics.js @@ -15,11 +15,12 @@ import { getNumberOfAccounts, getNumberOfTokens, } from '../selectors/selectors'; -import { getSendAsset, ASSET_TYPES } from '../ducks/send'; +import { getSendAsset } from '../ducks/send'; import { txDataSelector } from '../selectors/confirm-transaction'; import { getEnvironmentType } from '../../app/scripts/lib/util'; import { trackMetaMetricsEvent } from '../store/actions'; import { getNativeCurrency } from '../ducks/metamask/metamask'; +import { ASSET_TYPES } from '../../shared/constants/transaction'; export const MetaMetricsContext = createContext(() => { captureException( diff --git a/ui/contexts/metametrics.new.js b/ui/contexts/metametrics.new.js index 6b179b010..fc63dafda 100644 --- a/ui/contexts/metametrics.new.js +++ b/ui/contexts/metametrics.new.js @@ -10,15 +10,14 @@ import React, { useRef, useCallback, } from 'react'; -import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; -import { matchPath, useLocation, useRouteMatch } from 'react-router-dom'; +import { matchPath, useLocation } from 'react-router-dom'; import { captureException, captureMessage } from '@sentry/browser'; import { omit } from 'lodash'; import { getEnvironmentType } from '../../app/scripts/lib/util'; import { PATH_NAME_MAP } from '../helpers/constants/routes'; -import { txDataSelector } from '../selectors'; +import { useSegmentContext } from '../hooks/useSegmentContext'; import { trackMetaMetricsEvent, trackMetaMetricsPage } from '../store/actions'; @@ -54,44 +53,6 @@ export const MetaMetricsContext = createContext(() => { const PATHS_TO_CHECK = Object.keys(PATH_NAME_MAP); -/** - * Returns the current page if it matches out route map, as well as the origin - * if there is a confirmation that was triggered by a dapp - * - * @returns {{ - * page?: MetaMetricsPageObject - * referrer?: MetaMetricsReferrerObject - * }} - */ -function useSegmentContext() { - const match = useRouteMatch({ - path: PATHS_TO_CHECK, - exact: true, - strict: true, - }); - const txData = useSelector(txDataSelector) || {}; - const confirmTransactionOrigin = txData.origin; - - const referrer = confirmTransactionOrigin - ? { - url: confirmTransactionOrigin, - } - : undefined; - - const page = match - ? { - path: match.path, - title: PATH_NAME_MAP[match.path], - url: match.path, - } - : undefined; - - return { - page, - referrer, - }; -} - export function MetaMetricsProvider({ children }) { const location = useLocation(); const context = useSegmentContext(); diff --git a/ui/contexts/transaction-modal.js b/ui/contexts/transaction-modal.js index 51ebffa51..cfe563e01 100644 --- a/ui/contexts/transaction-modal.js +++ b/ui/contexts/transaction-modal.js @@ -6,13 +6,15 @@ export const TransactionModalContext = createContext({}); export const TransactionModalContextProvider = ({ children }) => { const [openModals, setOpenModals] = useState([]); - const closeModal = (modalName) => { - const index = openModals.indexOf(modalName); + const closeModal = (modalNames) => { if (openModals < 0) { return; } const modals = [...openModals]; - modals.splice(index, 1); + modalNames.forEach((modal) => { + const index = openModals.indexOf(modal); + modals.splice(index, 1); + }); setOpenModals(modals); }; diff --git a/ui/css/base-styles.scss b/ui/css/base-styles.scss index 3ab475169..c3bd9a106 100644 --- a/ui/css/base-styles.scss +++ b/ui/css/base-styles.scss @@ -38,7 +38,7 @@ html { /ui/pages/keychains/restore-vault.js */ .error { - color: #f7861c; + color: var(--color-error-default); margin-top: 3px; margin-bottom: 9px; } @@ -63,7 +63,7 @@ a { } a:hover { - color: var(--Blue-400); + color: var(--color-primary-alternative); } input.large-input, @@ -90,6 +90,7 @@ input.form-control { font-size: 14px; height: 40px; border: 1px solid var(--color-border-muted); + background: transparent; border-radius: 3px; width: 100%; diff --git a/ui/css/design-system/colors.scss b/ui/css/design-system/colors.scss index ec3e6fdf3..4ea5700b4 100644 --- a/ui/css/design-system/colors.scss +++ b/ui/css/design-system/colors.scss @@ -48,31 +48,4 @@ $color-map: ( 'localhost': --localhost, 'transparent': transparent, 'flask-purple': --flask-purple, - /** - * !!! DEPRECATED DO NOT USE!!! - */ - 'ui-1': --ui-1, - 'ui-2': --ui-2, - 'ui-3': --ui-3, - 'ui-4': --ui-4, - 'ui-5': --ui-5, - 'white': --ui-white, - 'black': --ui-black, - 'grey': --ui-grey, - 'neutral-grey': --neutral-grey, - 'primary-1': --primary-1, - 'primary-2': --primary-2, - 'primary-3': --primary-3, - 'secondary-1': --secondary-1, - 'secondary-2': --secondary-2, - 'secondary-3': --secondary-3, - 'alert-1': --alert-1, - 'alert-2': --alert-2, - 'alert-3': --alert-3, - 'error-1': --error-1, - 'error-2': --error-2, - 'error-3': --error-3, - 'success-1': --success-1, - 'success-2': --success-2, - 'success-3': --success-3 ); diff --git a/ui/css/design-system/deprecated-colors.scss b/ui/css/design-system/deprecated-colors.scss index c43c8ee3b..529d5a53e 100644 --- a/ui/css/design-system/deprecated-colors.scss +++ b/ui/css/design-system/deprecated-colors.scss @@ -5,7 +5,6 @@ // These are declared as variables and hoisted because they are // used as rgba's and native rgba doesn't accept var() -$malibu-blue: #7ac9fd; $alto: #dedede; $black: #000; $white: #fff; diff --git a/ui/css/itcss/components/network.scss b/ui/css/itcss/components/network.scss index 887e9e5f5..624a3b597 100644 --- a/ui/css/itcss/components/network.scss +++ b/ui/css/itcss/components/network.scss @@ -7,7 +7,7 @@ } .network-component.pointer { - border: 1px solid var(--Grey-200); + border: 1px solid var(--color-border-muted); border-radius: 82px; padding: 6px 3px; flex: 0 0 auto; @@ -46,6 +46,7 @@ } .dropdown-menu-item .fa.delete { + color: var(--color-icon-default); margin-right: 10px; display: none; } @@ -55,10 +56,12 @@ } .network-droppo { + background-color: var(--color-background-default); + border-radius: 4px; right: 2px; .color-indicator { - margin: 0 14px; + margin: 0 14px 0 4px; } @media screen and (min-width: $break-large) { @@ -76,22 +79,14 @@ .network-name-item { flex: 1; - color: var(--dusty-gray); text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } -.network-check, -.network-check__transparent { - color: var(--white); - margin-left: 7px; -} - .network-check__transparent { opacity: 0; - width: 16px; - margin: 0; + width: 24px; } .menu-icon-circle, @@ -106,8 +101,8 @@ } .menu-icon-circle--active { - border: 1px solid var(--white); - background: rgba(100, 100, 100, 0.4); + border: 1px solid var(--color-border-default); + background: var(--color-background-default); } .menu-icon-circle div, @@ -138,7 +133,7 @@ width: 100%; height: 1px; margin: 10px 0; - background-color: var(--scorpion); + background-color: var(--color-border-default); } .network-dropdown-title { @@ -146,7 +141,7 @@ height: 25px; width: 120px; - color: var(--white); + color: var(--color-text-default); text-align: center; } @@ -155,26 +150,24 @@ min-height: 36px; width: 265px; - color: var(--dusty-gray); + color: var(--color-text-default); &--link { - color: var(--white); + color: var(--color-primary-default); cursor: pointer; text-decoration: underline; &:hover { - color: var(--white); + color: var(--color-primary-alternative); } } &--dismiss { - color: inherit; - background: inherit; position: absolute; top: 63px; right: 10px; - border: 1px solid var(--dusty-gray); - border-radius: 10px; + padding: 2px 8px; + width: auto; } } @@ -198,3 +191,7 @@ left: -6px; } } + +.network__add-network-button { + padding: 0 16px; +} diff --git a/ui/css/itcss/components/newui-sections.scss b/ui/css/itcss/components/newui-sections.scss index 7271c6a3b..284168093 100644 --- a/ui/css/itcss/components/newui-sections.scss +++ b/ui/css/itcss/components/newui-sections.scss @@ -22,7 +22,7 @@ $sub-mid-size-breakpoint-range: "screen and (min-width: #{$break-large}) and (ma left: 0; width: 1px; height: 1px; - background-color: var(--Grey-000); + background-color: var(--color-background-alternative); animation: emptySpinningDiv 1s infinite linear; } @@ -95,7 +95,7 @@ $sub-mid-size-breakpoint-range: "screen and (min-width: #{$break-large}) and (ma .main-container { width: 100%; overflow-y: auto; - background-color: var(--white); + background-color: var(--color-background-default); } .main-container-wrapper { @@ -140,7 +140,7 @@ $sub-mid-size-breakpoint-range: "screen and (min-width: #{$break-large}) and (ma justify-content: center; align-items: center; flex: 1 0 auto; - background: #f7f7f7; + background: var(--color-background-alternative); width: 100%; } @@ -158,5 +158,5 @@ $sub-mid-size-breakpoint-range: "screen and (min-width: #{$break-large}) and (ma height: 100%; justify-content: center; padding: 0 10px; - background: white; + background: var(--color-background-default); } diff --git a/ui/css/itcss/components/send.scss b/ui/css/itcss/components/send.scss index 90b4743a0..787dd05bf 100644 --- a/ui/css/itcss/components/send.scss +++ b/ui/css/itcss/components/send.scss @@ -14,7 +14,7 @@ } .send-screen-card { - background-color: #fff; + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); padding: 46px 40.5px 26px; position: relative; @@ -48,13 +48,13 @@ border-radius: 50%; width: 70px; height: 70px; - border: 1px solid var(--alto); + border: 1px solid var(--color-border-muted); box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2); position: absolute; top: -35px; z-index: 25; padding: 4px; - background-color: var(--white); + background-color: var(--color-background-default); @media screen and (max-width: $break-small) { position: relative; @@ -73,7 +73,7 @@ .large-input { @include Paragraph; - border: 1px solid var(--dusty-gray); + border: 1px solid var(--color-border-default); border-radius: 4px; margin: 4px 0 20px; } @@ -89,7 +89,7 @@ &--error { input, .send-screen-gas-input { - border-color: var(--red) !important; + border-color: var(--color-error-default) !important; } .send-screen-input-wrapper__error-message { @@ -99,7 +99,7 @@ position: absolute; bottom: 4px; left: 8px; - color: var(--red); + color: var(--color-error-default); } } @@ -110,7 +110,7 @@ position: absolute; bottom: 4px; left: 8px; - color: var(--red); + color: var(--color-error-default); } } @@ -124,7 +124,7 @@ width: 100%; height: 41px; border-radius: 3px; - background-color: #f3f3f3; + background-color: var(--color-background-default); border-width: 0; border-style: none; display: flex; @@ -132,7 +132,7 @@ align-items: center; padding-left: 10px; padding-right: 12px; - color: var(--scorpion); + color: var(--color-text-alternative); } .send-screen-amount-labels { @@ -149,11 +149,11 @@ .currency-toggle { &__item { - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; &--selected { - color: var(--black); + color: var(--color-text-default); cursor: default; } } @@ -162,7 +162,7 @@ .send-screen-gas-input-customize { @include H7; - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; } @@ -182,9 +182,9 @@ bottom: 50px; width: 237px; height: 307px; - background-color: var(--white); + background-color: var(--color-background-default); opacity: 1; - box-shadow: var(--alto) 0 0 5px; + box-shadow: var(--color-border-muted) 0 0 5px; z-index: 1050; padding: 13px 19px; border-radius: 4px; @@ -195,12 +195,12 @@ height: 25px; width: 25px; z-index: 1200; - background: var(--white); + background: var(--color-background-default); position: absolute; transform: rotate(45deg); left: 107px; top: 294px; - box-shadow: 2px 2px 2px var(--alto); + box-shadow: 2px 2px 2px var(--color-border-muted); } .customize-gas-tooltip-container input[type='number']::-webkit-inner-spin-button { @@ -225,7 +225,7 @@ .gas-tooltip-label { @include Paragraph; - color: var(--tundora); + color: var(--color-text-alternative); } .gas-tooltip-header { @@ -237,7 +237,7 @@ } .gas-tooltip-input-label i { - color: var(--silver-chalice); + color: var(--color-icon-muted); margin-left: 6px; } @@ -246,8 +246,8 @@ width: 178px; height: 28px; - border: 1px solid var(--alto); - color: var(--nile-blue); + border: 1px solid var(--color-border-muted); + color: var(--color-text-default); padding-left: 8px; } @@ -261,7 +261,7 @@ position: absolute; top: 4px; right: 26px; - color: var(--silver-chalice); + color: var(--color-text-muted); } .gas-tooltip-input-arrows { @@ -272,11 +272,11 @@ right: 4px; width: 17px; height: 28px; - border: 1px solid #dadada; + border: 1px solid var(--color-icon-muted); border-left: 0; display: flex; flex-direction: column; - color: #9b9b9b; + color: var(--color-text-muted); padding: 1px 4px; cursor: pointer; } @@ -296,7 +296,7 @@ &__title { @include H4; - color: var(--scorpion); + color: var(--color-text-alternative); } &__subtitle { @@ -325,7 +325,7 @@ &__content { width: 498px; height: 605px; - background-color: #fff; + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); padding: 46px 40.5px 26px; position: relative; @@ -357,7 +357,7 @@ &__title { @include H4; - color: var(--scorpion); + color: var(--color-text-alternative); } &__description, @@ -405,7 +405,7 @@ &__container { width: 380px; border-radius: 8px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); display: flex; flex-flow: column nowrap; @@ -434,16 +434,16 @@ border-radius: 50%; width: 48px; height: 48px; - border: 1px solid var(--alto); + border: 1px solid var(--color-border-muted); z-index: 25; padding: 4px; - background-color: var(--white); + background-color: var(--color-background-default); } &__send-arrow-icon { @include H4; - color: #f28930; + color: var(--color-secondary-default); transform: rotate(-45deg); position: absolute; top: -2px; @@ -451,7 +451,7 @@ } &__arrow-background { - background-color: var(--white); + background-color: var(--color-background-default); height: 14px; width: 14px; position: absolute; @@ -468,7 +468,7 @@ &__header { height: 88px; width: 380px; - background-color: var(--athens-grey); + background-color: var(--color-background-alternative); position: relative; display: flex; justify-content: center; @@ -483,7 +483,7 @@ &__header-tip { height: 25px; width: 25px; - background: var(--athens-grey); + background-color: var(--color-background-alternative); position: absolute; transform: rotate(45deg); left: 178px; @@ -500,7 +500,7 @@ &__title { @include H3; - color: var(--scorpion); + color: var(--color-text-alternative); text-align: center; margin-top: 25px; } @@ -508,7 +508,7 @@ &__copy { @include H6; - color: var(--gray); + color: var(--color-text-alternative); text-align: center; margin-top: 10px; width: 287px; @@ -518,7 +518,7 @@ @include H7; left: 8px; - color: var(--red); + color: var(--color-error-default); } &__error-amount { @@ -529,11 +529,11 @@ @include H7; left: 8px; - color: var(--orange); + color: var(--color-secondary-default); } &__error-border { - color: var(--red); + color: var(--color-error-default); } &__form { @@ -572,21 +572,21 @@ min-width: 0; .currency-display { - color: var(--tundora); + color: var(--color-text-alternative); &__currency-symbol { - color: var(--tundora); + color: var(--color-text-alternative); } &__converted-value, &__converted-currency { - color: var(--tundora); + color: var(--color-text-alternative); } } .account-list-item { &__account-secondary-balance { - color: var(--tundora); + color: var(--color-text-alternative); } } } @@ -594,7 +594,7 @@ &__form-label { @include Paragraph; - color: var(--scorpion); + color: var(--color-text-alternative); width: 95px; flex: 0 0 auto; } @@ -604,10 +604,10 @@ @include H7; width: 100%; - border: 1px solid var(--alto); + border: 1px solid var(--color-border-muted); border-radius: 4px; - background-color: var(--white); - color: var(--tundora); + background-color: var(--color-background-default); + color: var(--color-text-default); position: relative; &__close-area { @@ -624,9 +624,9 @@ position: absolute; height: 220px; width: 100%; - border: 1px solid var(--geyser); + border: 1px solid var(--color-border-muted); border-radius: 4px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.11); margin-top: 11px; margin-left: -1px; @@ -648,14 +648,10 @@ flex-flow: row nowrap; align-items: center; padding: 8px 8px; - - &:hover { - background-color: rgba($alto, 0.2); - } } &__caret { - color: var(--silver); + color: var(--color-text-muted); padding: 0 10px; } @@ -665,15 +661,11 @@ align-items: center; padding: 0 8px; cursor: pointer; - - &:hover { - background-color: rgba($alto, 0.2); - } } &__asset-icon { .identicon { - border: 1px solid var(--alto); + border: 1px solid var(--color-border-muted); } } @@ -720,9 +712,9 @@ position: absolute; height: 220px; width: 100%; - border: 1px solid var(--geyser); + border: 1px solid var(--color-border-muted); border-radius: 4px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.11); top: 65px; left: 0; @@ -737,7 +729,7 @@ } &__input-wrapper { - border: 1px solid var(--alto); + border: 1px solid var(--color-border-muted); border-radius: 4px; height: 100%; @@ -750,7 +742,7 @@ height: 100%; &:hover { - background-color: var(--white); + background-color: var(--color-background-default); } } } @@ -764,8 +756,8 @@ width: 100%; border: none; border-radius: 4px; - background-color: var(--white); - color: var(--tundora); + background-color: var(--color-background-default); + color: var(--color-text-alternative); padding: 10px; } } @@ -779,10 +771,10 @@ position: relative; height: 54px; width: 100%; - border: 1px solid var(--alto); + border: 1px solid var(--color-border-muted); border-radius: 4px; - background-color: var(--white); - color: var(--tundora); + background-color: var(--color-background-default); + color: var(--color-text-alternative); padding: 10px; } } @@ -801,7 +793,7 @@ width: 56px; height: 20px; position: absolute; - border: 1px solid #b0d7f2; + border: 1px solid var(--color-primary-default); border-radius: 100px; cursor: pointer; top: 0; @@ -811,19 +803,19 @@ display: flex; align-items: center; justify-content: center; - color: #2f9ae0; - background: #fff; + color: var(--color-primary-default); + background: var(--color-background-default); &__disabled { - color: #b0d7f2; + opacity: 0.5; cursor: auto; } } input:checked + &__button { - background-color: var(--primary-blue); - border: 1px solid var(--primary-blue); - color: #fff; + background-color: var(--color-primary-default); + border: 1px solid var(--color-primary-default); + color: var(--color-primary-inverse); } } @@ -833,17 +825,6 @@ height: 0; } - &__gas-fee-display { - width: 100%; - position: relative; - - .currency-display--message { - padding: 8px 38px 8px 10px; - display: flex; - align-items: center; - } - } - &__sliders-icon-container { @include Paragraph; @@ -852,9 +833,9 @@ justify-content: center; height: 24px; width: 24px; - border: 1px solid var(--primary-blue); + border: 1px solid var(--color-primary-default); border-radius: 4px; - background-color: var(--white); + background-color: var(--color-background-default); position: absolute; right: 15px; top: 14px; @@ -862,7 +843,7 @@ } &__sliders-icon { - color: var(--primary-blue); + color: var(--color-primary-default); } &__memo-text-area { @@ -877,8 +858,8 @@ display: flex; justify-content: space-evenly; align-items: center; - border-top: 1px solid var(--alto); - background: var(--white); + border-top: 1px solid var(--color-border-muted); + background: var(--color-background-default); padding: 0 12px; flex-shrink: 0; } @@ -890,9 +871,9 @@ } &__customize-gas { - border: 1px solid #d8d8d8; + border: 1px solid var(--color-border-muted); border-radius: 4px; - background-color: #fff; + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.14); display: flex; flex-flow: column; @@ -906,7 +887,7 @@ @include H3; height: 52px; - border-bottom: 1px solid var(--alto); + border-bottom: 1px solid var(--color-border-muted); display: flex; align-items: center; justify-content: space-between; @@ -923,7 +904,7 @@ &__close::after { content: '\00D7'; font-size: 1.8em; - color: var(--dusty-gray); + color: var(--color-icon-default); cursor: pointer; margin-right: 19.25px; } @@ -948,7 +929,7 @@ @include H3; height: 75px; - border-top: 1px solid var(--alto); + border-top: 1px solid var(--color-border-muted); display: flex; align-items: center; justify-content: space-between; @@ -979,7 +960,7 @@ &__revert { @include Paragraph; - color: var(--silver-chalice); + color: var(--color-text-muted); margin-left: 21.25px; } @@ -1002,7 +983,7 @@ position: absolute; top: -18px; right: 0; - color: var(--red); + color: var(--color-error-default); width: 100%; text-align: center; } @@ -1023,7 +1004,7 @@ @include H4; height: 26px; - color: var(--tundora); + color: var(--color-text-alternative); margin-top: 17px; } @@ -1032,7 +1013,7 @@ height: 38px; width: 314px; - color: var(--tundora); + color: var(--color-text-alternative); margin-top: 17px; } @@ -1043,8 +1024,8 @@ .customize-gas-input { height: 54px; width: 315px; - border: 1px solid var(--geyser); - background-color: var(--white); + border: 1px solid var(--color-border-muted); + background-color: var(--color-background-default); padding-left: 15px; } @@ -1053,8 +1034,8 @@ width: 32px; height: 54px; - border-left: 1px solid #dadada; - color: var(--tundora); + border-left: 1px solid var(--color-border-muted); + color: var(--color-text-alternative); right: 0; padding: 0; display: flex; @@ -1088,7 +1069,7 @@ display: flex; justify-content: flex-end; - color: #2f9ae0; + color: var(--color-primary-default); cursor: pointer; margin-top: 5px; } @@ -1101,9 +1082,9 @@ justify-content: center; height: 24px; width: 24px; - border: 1px solid var(--primary-blue); + border: 1px solid var(--color-primary-default); border-radius: 4px; - background-color: var(--white); + background-color: var(--color-background-default); position: absolute; right: 15px; top: 14px; @@ -1118,14 +1099,14 @@ justify-content: center; height: 24px; border-radius: 4px; - background-color: #fff; + background-color: var(--color-background-default); position: absolute; right: 12px; top: 14px; cursor: pointer; - color: #2f9ae0; + color: var(--color-primary-default); } .sliders-icon { - color: var(--primary-blue); + color: var(--color-primary-default); } diff --git a/ui/css/itcss/tools/utilities.scss b/ui/css/itcss/tools/utilities.scss index 55fc0a95e..758de34e2 100644 --- a/ui/css/itcss/tools/utilities.scss +++ b/ui/css/itcss/tools/utilities.scss @@ -39,5 +39,5 @@ .critical-error { text-align: center; margin-top: 20px; - color: var(--red); + color: var(--color-error-default); } diff --git a/ui/css/utilities/colors.scss b/ui/css/utilities/colors.scss index 3409dc8a9..fc3bdcd43 100644 --- a/ui/css/utilities/colors.scss +++ b/ui/css/utilities/colors.scss @@ -1,103 +1,6 @@ :root { - // These are the colors of the MetaMask design system - // Only design system colors should be added, no superfluous variables - --Blue-000: #eaf6ff; - --Blue-100: #a7d9fe; - --Blue-200: #75c4fd; - --Blue-300: #43aefc; - --Blue-400: #1098fc; - --Blue-500: #037dd6; - --Blue-600: #0260a4; - --Blue-700: #024272; - --Blue-800: #01253f; - --Blue-900: #00080d; - // Greys - --Grey-000: #f2f3f4; - --Grey-100: #d6d9dc; - --Grey-200: #bbc0c5; - --Grey-300: #9fa6ae; - --Grey-400: #848c96; - --Grey-500: #6a737d; - --Grey-600: #535a61; - --Grey-700: #3b4046; - --Grey-800: #24272a; - --Grey-900: #141618; - // Greens - --Green-000: #f3fcf5; - --Green-100: #d6ffdf; - --Green-200: #afecbd; - --Green-300: #86e29b; - --Green-400: #5dd879; - --Green-500: #28a745; - --Green-600: #1e7e34; - --Green-700: #145523; - --Green-800: #0a2c12; - --Green-900: #041007; - // Reds - --Red-000: #fcf2f3; - --Red-100: #f7d5d8; - --Red-200: #f1b9be; - --Red-300: #e88f97; - --Red-400: #e06470; - --Red-500: #d73a49; - --Red-600: #b92534; - --Red-700: #8e1d28; - --Red-800: #64141c; - --Red-900: #3a0c10; - // Orange - --Orange-000: #fef5ef; - --Orange-100: #fde2cf; - --Orange-200: #fbc49d; - --Orange-300: #faa66c; - --Orange-400: #f8883b; - --Orange-500: #f66a0a; - --Orange-600: #c65507; - --Orange-700: #954005; - --Orange-800: #632b04; - --Orange-900: #321602; - // Yellow - --Yellow-000: #fefae8; // Temporary placeholder - --Yellow-100: #fefcde; - --Yellow-500: #ffd33d; - // Black - --Black-100: #24292e; - // Primary colors - --primary-blue: var(--Blue-500); - --primary-orange: var(--Orange-500); // Accents - --accent-yellow: var(--Yellow-500); - --accent-green: var(--Green-500); - --accent-red: var(--Red-500); - --accent-purple: #6f42c1; - --accent-pink: #ff45d8; - // Neutrals - --neutral-white: #fff; - --neutral-black: var(--Black-100); - --neutral-grey: var(--Grey-500); // Everything below this line is part of the new color system - --primary-1: #037dd6; - --primary-2: #eaf6ff; - --primary-3: #0260a4; - --secondary-1: #f66a0a; - --secondary-2: #fef5ef; - --secondary-3: #c65507; - --error-1: #d73a49; - --error-2: #fcf2f3; - --error-3: #b92534; - --alert-1: #ffd33d; - --alert-2: #fefcde; - --alert-3: #f8c000; - --success-1: #4cd964; - --success-2: #caf4d1; - --success-3: #219e37; - --ui-black: #24292e; - --ui-grey: #d6d9dc; - --ui-white: #fff; - --ui-1: #f2f3f4; - --ui-2: #d6d9dc; - --ui-3: #bbc0c5; - --ui-4: #6a737d; - --ui-5: #c4c4c4; --mainnet: #29b6af; --ropsten: #ff4a8d; --kovan: #9064ff; @@ -106,49 +9,3 @@ --localhost: #29b6af; --flask-purple: #8b45b6; } - -/* Deprecated colors */ -:root { - --white: #fff; - --black: #000; - --orange: #ffa500; - --red: #f00; - --gray: #808080; - - /* - Colors - http://chir.ag/projects/name-that-color - */ - --gallery: #efefef; - --wild-sand: #f6f6f6; - --dusty-gray: #9b9b9b; - --alto: #dedede; - --alabaster: #fafafa; - --silver-chalice: #aeaeae; - --tundora: #4d4d4d; - --nile-blue: #1b344d; - --scorpion: #5d5d5d; - --silver: #cdcdcd; - --caribbean-green: #02c9b1; - --monzo: #d0021b; - --crimson: #e91550; - --blue-lagoon: #038789; - --purple: #690496; - --tulip-tree: #ebb33f; - --malibu-blue: #b8cbd8; - --athens-grey: #e9edf0; - --geyser: #d2d8dd; - --manatee: #93949d; - --spindle: #c7ddec; - --mid-gray: #5b5d67; - --cape-cod: #38393a; - --dodger-blue: #3099f2; - --ecstasy: #f7861c; - --linen: #fdf4f4; - --oslo-gray: #8c8e94; - --polar: #fafcfe; - --blizzard-blue: #bfdef3; - --mischka: #dddee9; - --web-orange: #f2a202; - --mercury: #e5e5e5; -} diff --git a/ui/ducks/confirm-transaction/confirm-transaction.duck.js b/ui/ducks/confirm-transaction/confirm-transaction.duck.js index c05050f28..b16d60312 100644 --- a/ui/ducks/confirm-transaction/confirm-transaction.duck.js +++ b/ui/ducks/confirm-transaction/confirm-transaction.duck.js @@ -13,14 +13,12 @@ import { addEth, } from '../../helpers/utils/confirm-tx.util'; -import { - getTransactionData, - sumHexes, -} from '../../helpers/utils/transactions.util'; +import { sumHexes } from '../../helpers/utils/transactions.util'; import { conversionUtil } from '../../../shared/modules/conversion.utils'; import { getAveragePriceEstimateInHexWEI } from '../../selectors/custom-gas'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; +import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; // Actions const createActionType = (action) => `metamask/confirm-transaction/${action}`; @@ -285,7 +283,7 @@ export function setTransactionToConfirm(transactionId) { if (txParams.data) { const { to: tokenAddress, data } = txParams; - const tokenData = getTransactionData(data); + const tokenData = parseStandardTokenTransactionData(data); const tokens = getTokens(state); const currentToken = tokens?.find(({ address }) => isEqualCaseInsensitive(tokenAddress, address), diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index dcf4b033f..d2dabcd17 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -11,7 +11,7 @@ import { checkNetworkAndAccountSupports1559, getAddressBook, } from '../../selectors'; -import { updateTransaction } from '../../store/actions'; +import { updateTransactionGasFees } from '../../store/actions'; import { setCustomGasLimit, setCustomGasPrice } from '../gas/gas.duck'; import { decGWEIToHexWEI } from '../../helpers/utils/conversions.util'; @@ -209,7 +209,7 @@ const toHexWei = (value, expectHexWei) => { }; // Action Creators -export function updateTransactionGasFees({ +export function updateGasFees({ gasPrice, gasLimit, maxPriorityFeePerGas, @@ -239,7 +239,7 @@ export function updateTransactionGasFees({ ? addHexPrefix(gasLimit) : addHexPrefix(gasLimit.toString(16)); dispatch(setCustomGasLimit(customGasLimit)); - await dispatch(updateTransaction(updatedTx)); + await dispatch(updateTransactionGasFees(updatedTx.id, updatedTx)); }; } diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index d4b4c1084..e4a541ffc 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -44,6 +44,7 @@ import { getUseTokenDetection, getTokenList, getAddressBookEntryOrAccountName, + getIsMultiLayerFeeNetwork, } from '../../selectors'; import { disconnectGasFeeEstimatePoller, @@ -91,6 +92,9 @@ import { isBurnAddress, isValidHexAddress, } from '../../../shared/modules/hexstring-utils'; +import { sumHexes } from '../../helpers/utils/transactions.util'; +import fetchEstimatedL1Fee from '../../helpers/utils/optimism/fetchEstimatedL1Fee'; + import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network'; import { ERC20, @@ -99,7 +103,10 @@ import { ETH, GWEI, } from '../../helpers/constants/common'; -import { TRANSACTION_ENVELOPE_TYPES } from '../../../shared/constants/transaction'; +import { + ASSET_TYPES, + TRANSACTION_ENVELOPE_TYPES, +} from '../../../shared/constants/transaction'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; import { INVALID_ASSET_TYPE } from '../../helpers/constants/error-keys'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; @@ -164,18 +171,6 @@ export const GAS_INPUT_MODES = { CUSTOM: 'CUSTOM', }; -/** - * The types of assets that a user can send - * 1. NATIVE - The native asset for the current network, such as ETH - * 2. TOKEN - An ERC20 token. - * 2. COLLECTIBLE - An ERC721 or ERC1155 token. - */ -export const ASSET_TYPES = { - NATIVE: 'NATIVE', - TOKEN: 'TOKEN', - COLLECTIBLE: 'COLLECTIBLE', -}; - /** * The modes that the amount field can be set by * 1. INPUT - the user provides the amount by typing in the field @@ -201,6 +196,7 @@ async function estimateGasLimitForSend({ data, isNonStandardEthChain, chainId, + gasLimit, ...options }) { let isSimpleSendOnNonStandardNetwork = false; @@ -322,12 +318,14 @@ async function estimateGasLimitForSend({ error.message.includes('Transaction execution error.') || error.message.includes( 'gas required exceeds allowance or always failing transaction', - ); + ) || + (CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId] && + error.message.includes('gas required exceeds allowance')); if (simulationFailed) { const estimateWithBuffer = addGasBuffer( - paramsForGasEstimate.gas, + paramsForGasEstimate?.gas ?? gasLimit, blockGasLimit, - 1.5, + bufferMultiplier, ); return addHexPrefix(estimateWithBuffer); } @@ -371,9 +369,29 @@ export const computeEstimatedGasLimit = createAsyncThunk( const state = thunkApi.getState(); const { send, metamask } = state; const unapprovedTxs = getUnapprovedTxs(state); + const isMultiLayerFeeNetwork = getIsMultiLayerFeeNetwork(state); const transaction = unapprovedTxs[send.draftTransaction.id]; const isNonStandardEthChain = getIsNonStandardEthChain(state); const chainId = getCurrentChainId(state); + + let layer1GasTotal; + if (isMultiLayerFeeNetwork) { + layer1GasTotal = await fetchEstimatedL1Fee(global.eth, { + txParams: { + gasPrice: send.gas.gasPrice, + gas: send.gas.gasLimit, + to: send.recipient.address?.toLowerCase(), + value: + send.amount.mode === 'MAX' + ? send.account.balance + : send.amount.value, + from: send.account.address, + data: send.draftTransaction.userInputHexData, + type: '0x0', + }, + }); + } + if ( send.stage !== SEND_STAGES.EDIT || !transaction.dappSuggestedGasFees?.gas || @@ -389,10 +407,12 @@ export const computeEstimatedGasLimit = createAsyncThunk( data: send.draftTransaction.userInputHexData, isNonStandardEthChain, chainId, + gasLimit: send.gas.gasLimit, }); await thunkApi.dispatch(setCustomGasLimit(gasLimit)); return { gasLimit, + layer1GasTotal, }; } return null; @@ -660,6 +680,10 @@ export const initialState = { // Warning to display on the address field warning: null, }, + multiLayerFees: { + // Layer 1 gas fee total on multi-layer fee networks + layer1GasTotal: '0x0', + }, }; const slice = createSlice({ @@ -706,9 +730,13 @@ const slice = createSlice({ multiplierBase: 10, }); } else { + const _gasTotal = sumHexes( + state.gas.gasTotal || '0x0', + state.multiLayerFees?.layer1GasTotal || '0x0', + ); amount = subtractCurrencies( addHexPrefix(state.asset.balance), - addHexPrefix(state.gas.gasTotal), + addHexPrefix(_gasTotal), { toNumericBase: 'hex', aBase: 16, @@ -890,6 +918,21 @@ const slice = createSlice({ // Record the latest gasPriceEstimate for future comparisons state.gas.gasPriceEstimate = addHexPrefix(gasPriceEstimate); }, + /** + * sets the layer 1 fees total (for a multi-layer fee network) + * + * @param state + * @param action + */ + updateLayer1Fees: (state, action) => { + state.multiLayerFees.layer1GasTotal = action.payload; + if ( + state.amount.mode === AMOUNT_MODES.MAX && + state.asset.type === ASSET_TYPES.NATIVE + ) { + slice.caseReducers.updateAmountToMax(state); + } + }, /** * sets the amount mode to the provided value as long as it is one of the * supported modes (MAX|INPUT) @@ -1328,6 +1371,11 @@ const slice = createSlice({ payload: action.payload.gasLimit, }); } + if (action.payload?.layer1GasTotal) { + slice.caseReducers.updateLayer1Fees(state, { + payload: action.payload.layer1GasTotal, + }); + } }) .addCase(computeEstimatedGasLimit.rejected, (state) => { // If gas estimation fails, we should set the loading state to false, diff --git a/ui/ducks/send/send.test.js b/ui/ducks/send/send.test.js index 8e901b9dc..98f4fb794 100644 --- a/ui/ducks/send/send.test.js +++ b/ui/ducks/send/send.test.js @@ -16,6 +16,7 @@ import { } from '../../../shared/constants/network'; import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../../shared/constants/gas'; import { + ASSET_TYPES, TRANSACTION_ENVELOPE_TYPES, TRANSACTION_TYPES, } from '../../../shared/constants/transaction'; @@ -34,7 +35,6 @@ import sendReducer, { toggleSendMaxMode, signTransaction, SEND_STATUSES, - ASSET_TYPES, SEND_STAGES, AMOUNT_MODES, RECIPIENT_SEARCH_MODES, diff --git a/ui/ducks/swaps/swaps.js b/ui/ducks/swaps/swaps.js index 7d1dd91a5..dab39b90c 100644 --- a/ui/ducks/swaps/swaps.js +++ b/ui/ducks/swaps/swaps.js @@ -595,7 +595,7 @@ export const fetchQuotesAndSetQuoteState = ( history, inputValue, maxSlippage, - metaMetricsEvent, + trackEvent, pageRedirectionDisabled, ) => { return async (dispatch, getState) => { @@ -711,7 +711,7 @@ export const fetchQuotesAndSetQuoteState = ( const currentSmartTransactionsEnabled = getCurrentSmartTransactionsEnabled( state, ); - metaMetricsEvent({ + trackEvent({ event: 'Quotes Requested', category: 'swaps', sensitiveProperties: { @@ -765,7 +765,7 @@ export const fetchQuotesAndSetQuoteState = ( ]); if (Object.values(fetchedQuotes)?.length === 0) { - metaMetricsEvent({ + trackEvent({ event: 'No Quotes Available', category: 'swaps', sensitiveProperties: { @@ -786,7 +786,7 @@ export const fetchQuotesAndSetQuoteState = ( } else { const newSelectedQuote = fetchedQuotes[selectedAggId]; - metaMetricsEvent({ + trackEvent({ event: 'Quotes Received', category: 'swaps', sensitiveProperties: { @@ -832,7 +832,7 @@ export const fetchQuotesAndSetQuoteState = ( export const signAndSendSwapsSmartTransaction = ({ unsignedTransaction, - metaMetricsEvent, + trackEvent, history, additionalTrackingParams, }) => { @@ -888,7 +888,7 @@ export const signAndSendSwapsSmartTransaction = ({ stx_user_opt_in: smartTransactionsOptInStatus, ...additionalTrackingParams, }; - metaMetricsEvent({ + trackEvent({ event: 'STX Swap Started', category: 'swaps', sensitiveProperties: swapMetaData, @@ -984,7 +984,7 @@ export const signAndSendSwapsSmartTransaction = ({ export const signAndSendTransactions = ( history, - metaMetricsEvent, + trackEvent, additionalTrackingParams, ) => { return async (dispatch, getState) => { @@ -1138,7 +1138,7 @@ export const signAndSendTransactions = ( swapMetaData.base_and_priority_fee_per_gas = baseAndPriorityFeePerGas; } - metaMetricsEvent({ + trackEvent({ event: 'Swap Started', category: 'swaps', sensitiveProperties: swapMetaData, diff --git a/ui/ducks/swaps/swaps.test.js b/ui/ducks/swaps/swaps.test.js index 231a6c0e1..9808b0392 100644 --- a/ui/ducks/swaps/swaps.test.js +++ b/ui/ducks/swaps/swaps.test.js @@ -35,7 +35,7 @@ describe('Ducks - Swaps', () => { describe('fetchSwapsLivenessAndFeatureFlags', () => { const cleanFeatureFlagApiCache = () => { setStorageItem( - 'cachedFetch:https://api2.metaswap.codefi.network/featureFlags', + 'cachedFetch:https://swap.metaswap.codefi.network/featureFlags', null, ); }; @@ -48,7 +48,7 @@ describe('Ducks - Swaps', () => { featureFlagsResponse, replyWithError = false, } = {}) => { - const apiNock = nock('https://api2.metaswap.codefi.network').get( + const apiNock = nock('https://swap.metaswap.codefi.network').get( '/featureFlags', ); if (replyWithError) { diff --git a/ui/helpers/constants/common.js b/ui/helpers/constants/common.js index b62e053f1..3deae2356 100644 --- a/ui/helpers/constants/common.js +++ b/ui/helpers/constants/common.js @@ -9,6 +9,13 @@ export const ERC20 = 'ERC20'; export const ERC721 = 'ERC721'; export const ERC1155 = 'ERC1155'; +export const TOKEN_STANDARDS = { + ERC20, + ERC721, + ERC1155, + NONE: 'NONE', +}; + export const GAS_ESTIMATE_TYPES = { SLOW: 'SLOW', AVERAGE: 'AVERAGE', diff --git a/ui/helpers/constants/design-system.js b/ui/helpers/constants/design-system.js index d27c43d1f..1809ea31c 100644 --- a/ui/helpers/constants/design-system.js +++ b/ui/helpers/constants/design-system.js @@ -53,32 +53,6 @@ export const COLORS = { GOERLI: 'goerli', TRANSPARENT: 'transparent', LOCALHOST: 'localhost', - /** - * !!! DEPRECATED DO NOT USE!!! - */ - UI1: 'ui-1', - UI2: 'ui-2', - UI3: 'ui-3', - UI4: 'ui-4', - BLACK: 'black', - GREY: 'grey', - NEUTRAL_GREY: 'neutral-grey', - WHITE: 'white', - PRIMARY1: 'primary-1', - PRIMARY2: 'primary-2', - PRIMARY3: 'primary-3', - SECONDARY1: 'secondary-1', - SECONDARY2: 'secondary-2', - SECONDARY3: 'secondary-3', - SUCCESS1: 'success-1', - SUCCESS2: 'success-2', - SUCCESS3: 'success-3', - ERROR1: 'error-1', - ERROR2: 'error-2', - ERROR3: 'error-3', - ALERT1: 'alert-1', - ALERT2: 'alert-2', - ALERT3: 'alert-3', }; export const TYPOGRAPHY = { diff --git a/ui/helpers/constants/routes.js b/ui/helpers/constants/routes.js index 83d659dbf..519e0a098 100644 --- a/ui/helpers/constants/routes.js +++ b/ui/helpers/constants/routes.js @@ -88,6 +88,7 @@ const CONFIRM_SEND_TOKEN_PATH = '/send-token'; const CONFIRM_DEPLOY_CONTRACT_PATH = '/deploy-contract'; const CONFIRM_APPROVE_PATH = '/approve'; const CONFIRM_TRANSFER_FROM_PATH = '/transfer-from'; +const CONFIRM_SAFE_TRANSFER_FROM_PATH = '/safe-transfer-from'; const CONFIRM_TOKEN_METHOD_PATH = '/token-method'; const SIGNATURE_REQUEST_PATH = '/signature-request'; const DECRYPT_MESSAGE_REQUEST_PATH = '/decrypt-message-request'; @@ -140,6 +141,7 @@ const PATH_NAME_MAP = { [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_DEPLOY_CONTRACT_PATH}`]: 'Confirm Deploy Contract Transaction Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_APPROVE_PATH}`]: 'Confirm Approve Transaction Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_TRANSFER_FROM_PATH}`]: 'Confirm Transfer From Transaction Page', + [`${CONFIRM_TRANSACTION_ROUTE}/:id${CONFIRM_SAFE_TRANSFER_FROM_PATH}`]: 'Confirm Safe Transfer From Transaction Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${SIGNATURE_REQUEST_PATH}`]: 'Signature Request Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${DECRYPT_MESSAGE_REQUEST_PATH}`]: 'Decrypt Message Request Page', [`${CONFIRM_TRANSACTION_ROUTE}/:id${ENCRYPTION_PUBLIC_KEY_REQUEST_PATH}`]: 'Encryption Public Key Request Page', @@ -200,6 +202,7 @@ export { CONFIRM_DEPLOY_CONTRACT_PATH, CONFIRM_APPROVE_PATH, CONFIRM_TRANSFER_FROM_PATH, + CONFIRM_SAFE_TRANSFER_FROM_PATH, CONFIRM_TOKEN_METHOD_PATH, SIGNATURE_REQUEST_PATH, DECRYPT_MESSAGE_REQUEST_PATH, diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js new file mode 100644 index 000000000..7625880ea --- /dev/null +++ b/ui/helpers/utils/permission.js @@ -0,0 +1,97 @@ +import deepFreeze from 'deep-freeze-strict'; +import { + RestrictedMethods, + ///: BEGIN:ONLY_INCLUDE_IN(flask) + EndowmentPermissions, + PermissionNamespaces, + ///: END:ONLY_INCLUDE_IN +} from '../../../shared/constants/permissions'; +///: BEGIN:ONLY_INCLUDE_IN(flask) +import { coinTypeToProtocolName } from './util'; +///: END:ONLY_INCLUDE_IN + +const UNKNOWN_PERMISSION = Symbol('unknown'); + +const PERMISSION_DESCRIPTIONS = deepFreeze({ + [RestrictedMethods.eth_accounts]: { + label: (t) => t('permission_ethereumAccounts'), + leftIcon: 'fas fa-eye', + rightIcon: null, + }, + ///: BEGIN:ONLY_INCLUDE_IN(flask) + [RestrictedMethods.snap_confirm]: { + label: (t) => t('permission_customConfirmation'), + leftIcon: 'fas fa-user-check', + rightIcon: null, + }, + [RestrictedMethods.snap_notify]: { + leftIcon: 'fas fa-bell', + label: (t) => t('permission_notifications'), + rightIcon: null, + }, + [RestrictedMethods['snap_getBip44Entropy_*']]: { + label: (t, permissionName) => { + const coinType = permissionName.split('_').slice(-1); + return t('permission_manageBip44Keys', [ + coinTypeToProtocolName(coinType) || + `${coinType} (Unrecognized protocol)`, + ]); + }, + leftIcon: 'fas fa-door-open', + rightIcon: null, + }, + [RestrictedMethods.snap_manageState]: { + label: (t) => t('permission_manageState'), + leftIcon: 'fas fa-download', + rightIcon: null, + }, + [RestrictedMethods['wallet_snap_*']]: { + label: (t, permissionName) => { + const snapId = permissionName.split('_').slice(-1); + return t('permission_accessSnap', [snapId]); + }, + leftIcon: 'fas fa-bolt', + rightIcon: null, + }, + [EndowmentPermissions['endowment:network-access']]: { + label: (t) => t('permission_accessNetwork'), + leftIcon: 'fas fa-wifi', + rightIcon: null, + }, + ///: END:ONLY_INCLUDE_IN + [UNKNOWN_PERMISSION]: { + label: (t, permissionName) => + t('permission_unknown', [permissionName ?? 'undefined']), + leftIcon: 'fas fa-times-circle', + rightIcon: null, + }, +}); + +/** + * @typedef {Object} PermissionLabelObject + * @property {string} label - The text label. + * @property {string} leftIcon - The left icon. + * @property {string} rightIcon - The right icon. + */ + +/** + * @param {Function} t - The translation function + * @param {string} permissionName - The name of the permission to request + * @returns {(permissionName:string) => PermissionLabelObject} + */ +export const getPermissionDescription = (t, permissionName) => { + let value = PERMISSION_DESCRIPTIONS[UNKNOWN_PERMISSION]; + + if (Object.hasOwnProperty.call(PERMISSION_DESCRIPTIONS, permissionName)) { + value = PERMISSION_DESCRIPTIONS[permissionName]; + } + ///: BEGIN:ONLY_INCLUDE_IN(flask) + for (const namespace of Object.keys(PermissionNamespaces)) { + if (permissionName.startsWith(namespace)) { + value = PERMISSION_DESCRIPTIONS[PermissionNamespaces[namespace]]; + } + } + ///: END:ONLY_INCLUDE_IN + + return { ...value, label: value.label(t, permissionName) }; +}; diff --git a/ui/helpers/utils/settings-search.js b/ui/helpers/utils/settings-search.js index 31550eb5c..6c670af78 100644 --- a/ui/helpers/utils/settings-search.js +++ b/ui/helpers/utils/settings-search.js @@ -8,325 +8,291 @@ import { NETWORKS_ROUTE, CONTACT_LIST_ROUTE, EXPERIMENTAL_ROUTE, + ///: BEGIN:ONLY_INCLUDE_IN(flask) + SNAPS_LIST_ROUTE, + ///: END:ONLY_INCLUDE_IN } from '../constants/routes'; function showHideSettings(t, settings) { - if (!process.env.COLLECTIBLES_V1) { - return settings.filter( - (e) => - e.section !== t('enableOpenSeaAPI') && - e.section !== t('useCollectibleDetection'), - ); + if (process.env.COLLECTIBLES_V1) { + return [ + ...settings, + { + tab: t('experimental'), + section: t('enableOpenSeaAPI'), + description: t('enableOpenSeaAPIDescription'), + route: `${EXPERIMENTAL_ROUTE}#opensea-api`, + icon: 'fa fa-flask', + id: settings.length + 1, + }, + { + tab: t('experimental'), + section: t('useCollectibleDetection'), + description: t('useCollectibleDetectionDescription'), + route: `${EXPERIMENTAL_ROUTE}#autodetect-nfts`, + icon: 'fa fa-flask', + id: settings.length + 2, + }, + ]; } return settings; } export function getSettingsRoutes(t) { - const settingsRoutesList = [ + let settingsRoutesList = [ { tab: t('general'), section: t('currencyConversion'), description: '', route: `${GENERAL_ROUTE}#currency-conversion`, - image: 'general-icon.svg', - id: 1, + icon: 'fa fa-cog', }, { tab: t('general'), section: t('primaryCurrencySetting'), description: t('primaryCurrencySettingDescription'), route: `${GENERAL_ROUTE}#primary-currency`, - image: 'general-icon.svg', - id: 2, + icon: 'fa fa-cog', }, { tab: t('general'), section: t('currentLanguage'), description: '', route: `${GENERAL_ROUTE}#current-language`, - image: 'general-icon.svg', - id: 3, + icon: 'fa fa-cog', }, { tab: t('general'), section: t('accountIdenticon'), description: '', route: `${GENERAL_ROUTE}#account-identicon`, - image: 'general-icon.svg', - id: 4, + icon: 'fa fa-cog', }, { tab: t('general'), section: t('hideZeroBalanceTokens'), description: '', route: `${GENERAL_ROUTE}#zero-balancetokens`, - image: 'general-icon.svg', - id: 5, + icon: 'fa fa-cog', }, { tab: t('advanced'), section: t('stateLogs'), description: t('stateLogsDescription'), route: `${ADVANCED_ROUTE}#state-logs`, - image: 'advanced-icon.svg', - id: 6, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('syncWithMobile'), description: '', route: `${ADVANCED_ROUTE}#sync-withmobile`, - image: 'advanced-icon.svg', - id: 7, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('resetAccount'), description: t('resetAccountDescription'), route: `${ADVANCED_ROUTE}#reset-account`, - image: 'advanced-icon.svg', - id: 8, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('showAdvancedGasInline'), description: t('showAdvancedGasInlineDescription'), route: `${ADVANCED_ROUTE}#advanced-gascontrols`, - image: 'advanced-icon.svg', - id: 9, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('showHexData'), description: t('showHexDataDescription'), route: `${ADVANCED_ROUTE}#show-hexdata`, - image: 'advanced-icon.svg', - id: 10, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('showFiatConversionInTestnets'), description: t('showFiatConversionInTestnetsDescription'), route: `${ADVANCED_ROUTE}#conversion-testnetworks`, - image: 'advanced-icon.svg', - id: 11, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('showTestnetNetworks'), description: t('showTestnetNetworksDescription'), route: `${ADVANCED_ROUTE}#show-testnets`, - image: 'advanced-icon.svg', - id: 12, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('nonceField'), description: t('nonceFieldDescription'), route: `${ADVANCED_ROUTE}#customize-nonce`, - image: 'advanced-icon.svg', - id: 13, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('autoLockTimeLimit'), description: t('autoLockTimeLimitDescription'), route: `${ADVANCED_ROUTE}#autolock-timer`, - image: 'advanced-icon.svg', - id: 14, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('syncWithThreeBox'), description: t('syncWithThreeBoxDescription'), route: `${ADVANCED_ROUTE}#sync-with3box`, - image: 'advanced-icon.svg', - id: 15, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('ipfsGateway'), description: t('ipfsGatewayDescription'), route: `${ADVANCED_ROUTE}#ipfs-gateway`, - image: 'advanced-icon.svg', - id: 16, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('preferredLedgerConnectionType'), description: t('preferredLedgerConnectionType'), route: `${ADVANCED_ROUTE}#ledger-connection`, - image: 'advanced-icon.svg', - id: 17, + icon: 'fas fa-sliders-h', }, { tab: t('advanced'), section: t('dismissReminderField'), description: t('dismissReminderDescriptionField'), route: `${ADVANCED_ROUTE}#dimiss-secretrecovery`, - image: 'advanced-icon.svg', - id: 18, + icon: 'fas fa-sliders-h', }, { tab: t('contacts'), section: t('contacts'), description: t('contacts'), route: CONTACT_LIST_ROUTE, - image: 'contacts-icon.svg', - id: 19, + icon: 'fa fa-address-book', }, + ///: BEGIN:ONLY_INCLUDE_IN(flask) + { + tab: t('snaps'), + section: t('snaps'), + description: t('snaps'), + route: SNAPS_LIST_ROUTE, + icon: 'fa fa-flask', + }, + ///: END:ONLY_INCLUDE_IN { tab: t('securityAndPrivacy'), section: t('revealSeedWords'), description: t('revealSeedWords'), route: `${SECURITY_ROUTE}#reveal-secretrecovery`, - image: 'security-icon.svg', - id: 20, + icon: 'fa fa-lock', }, { tab: t('securityAndPrivacy'), section: t('showIncomingTransactions'), description: t('showIncomingTransactionsDescription'), route: `${SECURITY_ROUTE}#incoming-transaction`, - image: 'security-icon.svg', - id: 21, + icon: 'fa fa-lock', }, { tab: t('securityAndPrivacy'), section: t('usePhishingDetection'), description: t('usePhishingDetectionDescription'), route: `${SECURITY_ROUTE}#phishing-detection`, - image: 'security-icon.svg', - id: 22, + icon: 'fa fa-lock', }, { tab: t('securityAndPrivacy'), section: t('participateInMetaMetrics'), description: t('participateInMetaMetricsDescription'), route: `${SECURITY_ROUTE}#metrametrics`, - image: 'security-icon.svg', - id: 23, + icon: 'fa fa-lock', }, { tab: t('alerts'), section: t('alertSettingsUnconnectedAccount'), description: t('alertSettingsUnconnectedAccount'), route: `${ALERTS_ROUTE}#unconnected-account`, - image: 'alerts-icon.svg', - id: 24, + icon: 'fa fa-bell', }, { tab: t('alerts'), section: t('alertSettingsWeb3ShimUsage'), description: t('alertSettingsWeb3ShimUsage'), route: `${ALERTS_ROUTE}#web3-shimusage`, - image: 'alerts-icon.svg', - id: 25, + icon: 'fa fa-bell', }, { tab: t('networks'), section: t('mainnet'), description: t('mainnet'), route: `${NETWORKS_ROUTE}#networks-mainnet`, - image: 'network-icon.svg', - id: 26, + icon: 'fa fa-plug', }, { tab: t('networks'), section: t('ropsten'), description: t('ropsten'), route: `${NETWORKS_ROUTE}#networks-ropsten`, - image: 'network-icon.svg', - id: 27, + icon: 'fa fa-plug', }, { tab: t('networks'), section: t('rinkeby'), description: t('rinkeby'), route: `${NETWORKS_ROUTE}#networks-rinkeby`, - image: 'network-icon.svg', - id: 28, + icon: 'fa fa-plug', }, { tab: t('networks'), section: t('goerli'), description: t('goerli'), route: `${NETWORKS_ROUTE}#networks-goerli`, - image: 'network-icon.svg', - id: 29, + icon: 'fa fa-plug', }, { tab: t('networks'), section: t('kovan'), description: t('kovan'), - route: `${NETWORKS_ROUTE}#networtks-kovan`, - image: 'network-icon.svg', - id: 30, + route: `${NETWORKS_ROUTE}#networks-kovan`, + icon: 'fa fa-plug', }, { tab: t('networks'), section: t('localhost'), description: t('localhost'), - route: `${NETWORKS_ROUTE}#network-localhost`, - image: 'network-icon.svg', - id: 31, + route: `${NETWORKS_ROUTE}#networks-localhost`, + icon: 'fa fa-plug', }, - { - tab: t('experimental'), - section: t('useTokenDetection'), - description: t('useTokenDetectionDescription'), - route: `${EXPERIMENTAL_ROUTE}#token-description`, - image: 'experimental-icon.svg', - id: 32, - }, - { - tab: t('experimental'), - section: t('enableOpenSeaAPI'), - description: t('enableOpenSeaAPIDescription'), - route: `${EXPERIMENTAL_ROUTE}#opensea-api`, - image: 'experimental-icon.svg', - id: 33, - }, - { - tab: t('experimental'), - section: t('useCollectibleDetection'), - description: t('useCollectibleDetectionDescription'), - route: `${EXPERIMENTAL_ROUTE}#autodetect-nfts`, - image: 'experimental-icon.svg', - id: 34, - }, - { tab: t('about'), section: t('metamaskVersion'), description: t('builtAroundTheWorld'), route: `${ABOUT_US_ROUTE}#version`, - image: 'info-icon.svg', - id: 35, + icon: 'fa fa-info-circle', }, { tab: t('about'), section: t('links'), description: '', route: `${ABOUT_US_ROUTE}#links`, - image: 'info-icon.svg', - id: 36, + icon: 'fa fa-info-circle', }, { tab: t('about'), section: t('privacyMsg'), description: t('privacyMsg'), route: `${ABOUT_US_ROUTE}#privacy-policy`, - image: 'info-icon.svg', - id: 37, + icon: 'fa fa-info-circle', }, { tab: t('about'), section: t('terms'), description: t('terms'), route: `${ABOUT_US_ROUTE}#terms`, - image: 'info-icon.svg', - id: 38, + icon: 'fa fa-info-circle', }, { @@ -334,8 +300,7 @@ export function getSettingsRoutes(t) { section: t('attributions'), description: t('attributions'), route: `${ABOUT_US_ROUTE}#attributions`, - image: 'info-icon.svg', - id: 39, + icon: 'fa fa-info-circle', }, { @@ -343,8 +308,7 @@ export function getSettingsRoutes(t) { section: t('supportCenter'), description: t('supportCenter'), route: `${ABOUT_US_ROUTE}#supportcenter`, - image: 'info-icon.svg', - id: 40, + icon: 'fa fa-info-circle', }, { @@ -352,8 +316,7 @@ export function getSettingsRoutes(t) { section: t('visitWebSite'), description: t('visitWebSite'), route: `${ABOUT_US_ROUTE}#visitwebsite`, - image: 'info-icon.svg', - id: 41, + icon: 'fa fa-info-circle', }, { @@ -361,11 +324,44 @@ export function getSettingsRoutes(t) { section: t('contactUs'), description: t('contactUs'), route: `${ABOUT_US_ROUTE}#contactus`, - image: 'info-icon.svg', - id: 42, + icon: 'fa fa-info-circle', + }, + { + tab: t('experimental'), + section: t('enableEIP1559V2'), + description: t('enableEIP1559V2Description'), + route: `${EXPERIMENTAL_ROUTE}#enable-advanced-gas`, + icon: 'fa fa-flask', }, ]; - + if (process.env.TOKEN_DETECTION_V2) { + settingsRoutesList = [ + ...settingsRoutesList, + { + /** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */ + tab: t('advanced'), + section: t('tokenDetection'), + description: t('tokenDetectionToggleDescription'), + route: `${ADVANCED_ROUTE}#token-description`, + icon: 'fas fa-sliders-h', + }, + ]; + } else { + settingsRoutesList = [ + ...settingsRoutesList, + { + /** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */ + tab: t('experimental'), + section: t('useTokenDetection'), + description: t('useTokenDetectionDescription'), + route: `${EXPERIMENTAL_ROUTE}#token-description`, + icon: 'fa fa-flask', + }, + ]; + } + for (let i = 0; i < settingsRoutesList.length; i++) { + settingsRoutesList[i].id = i + 1; + } // TODO: write to json file? return showHideSettings(t, settingsRoutesList); } @@ -383,7 +379,6 @@ export function handleSettingsRefs(t, tabName, settingsRefs) { const settingsRefsIndex = settingsSearchJsonFiltered.findIndex( (s) => s.route.substring(1) === window.location.hash.substring(1), ); - if ( settingsRefsIndex !== -1 && settingsRefs[settingsRefsIndex].current !== null diff --git a/ui/helpers/utils/settings-search.test.js b/ui/helpers/utils/settings-search.test.js index c41eaf757..2e2372bbb 100644 --- a/ui/helpers/utils/settings-search.test.js +++ b/ui/helpers/utils/settings-search.test.js @@ -111,14 +111,23 @@ const t = (key) => { return 'Localhost 8545'; case 'experimental': return 'Experimental'; + /** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */ case 'useTokenDetection': return 'Use Token Detection'; case 'useTokenDetectionDescription': return 'We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want MetaMask to pull data from those services.'; + case 'tokenDetection': + return 'Token detection'; + case 'tokenDetectionToggleDescription': + return 'ConsenSys’ token API aggregates a list of tokens from various third party token lists. Turning it off will stop detecting new tokens added to your wallet, but will keep the option to search for tokens to import.'; + case 'enableEIP1559V2': + return 'Enable Enhanced Gas Fee UI'; + case 'enableEIP1559V2Description': + return "We've updated how gas estimation and customization works. Turn on if you'd like to use the new gas experience. Learn more"; case 'enableOpenSeaAPI': return 'Enable OpenSea API'; case 'enableOpenSeaAPIDescription': - return 'Use OpenSea API to fetch NFT data.NFT auto - detection relies on OpenSea API, and will not be available when this is turned off.'; + return "Use OpenSea's API to fetch NFT data. NFT auto-detection relies on OpenSea's API, and will not be available when this is turned off."; case 'useCollectibleDetection': return 'Autodetect NFTs'; case 'useCollectibleDetectionDescription': @@ -143,7 +152,8 @@ const t = (key) => { return 'Visit our web site'; case 'contactUs': return 'Contact us'; - + case 'snaps': + return 'Snaps'; default: return ''; } @@ -152,11 +162,10 @@ const t = (key) => { describe('Settings Search Utils', () => { describe('getSettingsRoutes', () => { it('should get all settings', () => { - const settingsListExcepted = [ + const expectedSettingsList = [ { description: '', - id: 1, - image: 'general-icon.svg', + icon: 'fa fa-cog', route: '/settings/general#currency-conversion', section: 'Currency Conversion', tab: 'General', @@ -164,32 +173,28 @@ describe('Settings Search Utils', () => { { description: 'Select native to prioritize displaying values in the native currency of the chain (e.g. ETH). Select Fiat to prioritize displaying values in your selected fiat currency.', - id: 2, - image: 'general-icon.svg', + icon: 'fa fa-cog', route: '/settings/general#primary-currency', section: 'Primary Currenc', tab: 'General', }, { description: '', - id: 3, - image: 'general-icon.svg', + icon: 'fa fa-cog', route: '/settings/general#current-language', section: 'Current Language', tab: 'General', }, { description: '', - id: 4, - image: 'general-icon.svg', + icon: 'fa fa-cog', route: '/settings/general#account-identicon', section: 'Current Language"', tab: 'General', }, { description: '', - id: 5, - image: 'general-icon.svg', + icon: 'fa fa-cog', route: '/settings/general#zero-balancetokens', section: 'Hide Tokens Without Balance', tab: 'General', @@ -197,16 +202,14 @@ describe('Settings Search Utils', () => { { description: 'State logs contain your public account addresses and sent transactions.', - id: 6, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#state-logs', section: 'State Logs', tab: 'Advanced', }, { description: '', - id: 7, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#sync-withmobile', section: 'Sync with mobile', tab: 'Advanced', @@ -214,8 +217,7 @@ describe('Settings Search Utils', () => { { description: 'Resetting your account will clear your transaction history. This will not change the balances in your accounts or require you to re-enter your Secret Recovery Phrase.', - id: 8, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#reset-account', section: 'Reset Account', tab: 'Advanced', @@ -223,8 +225,7 @@ describe('Settings Search Utils', () => { { description: 'Select this to show gas price and limit controls directly on the send and confirm screens.', - id: 9, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#advanced-gascontrols', section: 'Advanced gas controls', tab: 'Advanced', @@ -232,24 +233,21 @@ describe('Settings Search Utils', () => { { description: 'Select this to show the hex data field on the send screen', - id: 10, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#show-hexdata', section: 'Show Hex Data', tab: 'Advanced', }, { description: 'Select this to show fiat conversion on test network', - id: 11, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#conversion-testnetworks', section: 'Show Conversion on test networks', tab: 'Advanced', }, { description: 'Select this to show test networks in network list', - id: 12, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#show-testnets', section: 'Show test networks', tab: 'Advanced', @@ -257,8 +255,7 @@ describe('Settings Search Utils', () => { { description: 'Turn this on to change the nonce (transaction number) on confirmation screens. This is an advanced feature, use cautiously.', - id: 13, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#customize-nonce', section: 'Customize transaction nonce', tab: 'Advanced', @@ -266,8 +263,7 @@ describe('Settings Search Utils', () => { { description: 'Set the idle time in minutes before MetaMask will become locked.', - id: 14, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#autolock-timer', section: 'Auto-Lock Timer (minutes)', tab: 'Advanced', @@ -275,8 +271,7 @@ describe('Settings Search Utils', () => { { description: 'Turn on to have your settings backed up with 3Box. This feature is currently experimental; use at your own risk.', - id: 15, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#sync-with3box', section: 'Sync data with 3Box (experimental)', tab: 'Advanced', @@ -284,16 +279,14 @@ describe('Settings Search Utils', () => { { description: 'Enter the URL of the IPFS CID gateway to use for ENS content resolution.', - id: 16, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#ipfs-gateway', section: 'IPFS Gateway', tab: 'Advanced', }, { description: 'Preferred Ledger Connection Type', - id: 17, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#ledger-connection', section: 'Preferred Ledger Connection Type', tab: 'Advanced', @@ -301,24 +294,30 @@ describe('Settings Search Utils', () => { { description: 'Turn this on to dismiss the Secret Recovery Phrase backup reminder message. We highly recommend that you back up your Secret Recovery Phrase to avoid loss of funds', - id: 18, - image: 'advanced-icon.svg', + icon: 'fas fa-sliders-h', route: '/settings/advanced#dimiss-secretrecovery', section: 'Dismiss Secret Recovery Phrase backup reminder', tab: 'Advanced', }, { description: '', - id: 19, - image: 'contacts-icon.svg', + icon: 'fa fa-address-book', route: '/settings/contact-list', section: '', tab: '', }, + ///: BEGIN:ONLY_INCLUDE_IN(flask) + { + description: 'Snaps', + icon: 'fa fa-flask', + route: '/settings/snaps-list', + section: 'Snaps', + tab: 'Snaps', + }, + ///: END:ONLY_INCLUDE_IN { description: 'Reveal Secret Recovery Phrase', - id: 20, - image: 'security-icon.svg', + icon: 'fa fa-lock', route: '/settings/security#reveal-secretrecovery', section: 'Reveal Secret Recovery Phrase', tab: 'Security & Privacy', @@ -326,8 +325,7 @@ describe('Settings Search Utils', () => { { description: 'Select this to use Etherscan to show incoming transactions in the transactions list', - id: 21, - image: 'security-icon.svg', + icon: 'fa fa-lock', route: '/settings/security#incoming-transaction', section: 'Show Incoming Transactions', tab: 'Security & Privacy', @@ -335,8 +333,7 @@ describe('Settings Search Utils', () => { { description: 'Display a warning for phishing domains targeting Ethereum users', - id: 22, - image: 'security-icon.svg', + icon: 'fa fa-lock', route: '/settings/security#phishing-detection', section: 'Use Phishing Detection', tab: 'Security & Privacy', @@ -344,8 +341,7 @@ describe('Settings Search Utils', () => { { description: 'Participate in MetaMetrics to help us make MetaMask better', - id: 23, - image: 'security-icon.svg', + icon: 'fa fa-lock', route: '/settings/security#metrametrics', section: 'Participate in MetaMetrics', tab: 'Security & Privacy', @@ -353,8 +349,7 @@ describe('Settings Search Utils', () => { { description: 'Browsing a website with an unconnected account selected', - id: 24, - image: 'alerts-icon.svg', + icon: 'fa fa-bell', route: '/settings/alerts#unconnected-account', section: 'Browsing a website with an unconnected account selected', tab: 'Alerts', @@ -362,139 +357,135 @@ describe('Settings Search Utils', () => { { description: 'When a website tries to use the removed window.web3 API', - id: 25, - image: 'alerts-icon.svg', + icon: 'fa fa-bell', route: '/settings/alerts#web3-shimusage', section: 'When a website tries to use the removed window.web3 API', tab: 'Alerts', }, { description: 'Ethereum Mainnet', - id: 26, - image: 'network-icon.svg', + icon: 'fa fa-plug', route: '/settings/networks#networks-mainnet', section: 'Ethereum Mainnet', tab: 'Networks', }, { description: 'Ropsten Test Network', - id: 27, - image: 'network-icon.svg', + icon: 'fa fa-plug', route: '/settings/networks#networks-ropsten', section: 'Ropsten Test Network', tab: 'Networks', }, { description: 'Rinkeby Test Network', - id: 28, - image: 'network-icon.svg', + icon: 'fa fa-plug', route: '/settings/networks#networks-rinkeby', section: 'Rinkeby Test Network', tab: 'Networks', }, { description: 'Goerli Test Network', - id: 29, - image: 'network-icon.svg', + icon: 'fa fa-plug', route: '/settings/networks#networks-goerli', section: 'Goerli Test Network', tab: 'Networks', }, { description: 'Kovan Test Network', - id: 30, - image: 'network-icon.svg', - route: '/settings/networks#networtks-kovan', + icon: 'fa fa-plug', + route: '/settings/networks#networks-kovan', section: 'Kovan Test Network', tab: 'Networks', }, { description: 'Localhost 8545', - id: 31, - image: 'network-icon.svg', - route: '/settings/networks#network-localhost', + icon: 'fa fa-plug', + route: '/settings/networks#networks-localhost', section: 'Localhost 8545', tab: 'Networks', }, - { - description: - 'We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want MetaMask to pull data from those services.', - id: 32, - image: 'experimental-icon.svg', - route: '/settings/experimental#token-description', - section: 'Use Token Detection', - tab: 'Experimental', - }, { description: 'MetaMask is designed and built around the world.', - id: 35, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#version', section: 'MetaMask Version', tab: 'About', }, { description: '', - id: 36, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#links', section: 'Links', tab: 'About', }, { description: 'Privacy Policy', - id: 37, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#privacy-policy', section: 'Privacy Policy', tab: 'About', }, { description: 'Terms of Use', - id: 38, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#terms', section: 'Terms of Use', tab: 'About', }, { description: 'Attributions', - id: 39, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#attributions', section: 'Attributions', tab: 'About', }, { description: 'Visit our Support Center', - id: 40, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#supportcenter', section: 'Visit our Support Center', tab: 'About', }, { description: 'Visit our web site', - id: 41, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#visitwebsite', section: 'Visit our web site', tab: 'About', }, { description: 'Contact us', - id: 42, - image: 'info-icon.svg', + icon: 'fa fa-info-circle', route: '/settings/about-us#contactus', section: 'Contact us', tab: 'About', }, + { + tab: 'Experimental', + section: 'Enable Enhanced Gas Fee UI', + description: + "We've updated how gas estimation and customization works. Turn on if you'd like to use the new gas experience. Learn more", + route: `/settings/experimental#enable-advanced-gas`, + icon: 'fa fa-flask', + }, + { + /** TODO: Remove during TOKEN_DETECTION_V2 feature flag clean up */ + description: + 'We use third-party APIs to detect and display new tokens sent to your wallet. Turn off if you don’t want MetaMask to pull data from those services.', + icon: 'fa fa-flask', + route: '/settings/experimental#token-description', + section: 'Use Token Detection', + tab: 'Experimental', + }, ]; - expect(getSettingsRoutes(t)).toStrictEqual(settingsListExcepted); + for (let i = 0; i < expectedSettingsList.length; i++) { + expectedSettingsList[i].id = i + 1; + } + expect(getSettingsRoutes(t)).toStrictEqual(expectedSettingsList); }); it('should not get all settings', () => { - const settingsListExcepted = [ + const expectedSettingsList = [ { description: '', image: 'general-icon.svg', @@ -510,7 +501,7 @@ describe('Settings Search Utils', () => { tab: 'About', }, ]; - expect(getSettingsRoutes(t)).not.toStrictEqual(settingsListExcepted); + expect(getSettingsRoutes(t)).not.toStrictEqual(expectedSettingsList); }); }); @@ -542,7 +533,7 @@ describe('Settings Search Utils', () => { }); it('should get good experimental section number', () => { - expect(getSettingsSectionNumber(t, t('experimental'))).toStrictEqual(1); + expect(getSettingsSectionNumber(t, t('experimental'))).toStrictEqual(2); }); it('should get good about section number', () => { diff --git a/ui/helpers/utils/token-util.js b/ui/helpers/utils/token-util.js index a75db6da6..b934ed176 100644 --- a/ui/helpers/utils/token-util.js +++ b/ui/helpers/utils/token-util.js @@ -7,15 +7,14 @@ import { import { getTokenStandardAndDetails } from '../../store/actions'; import { ERC1155, ERC721 } from '../constants/common'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; +import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; import * as util from './util'; import { formatCurrency } from './confirm-tx.util'; -import { getTransactionData } from './transactions.util'; const DEFAULT_SYMBOL = ''; async function getSymbolFromContract(tokenAddress) { const token = util.getContractAtAddress(tokenAddress); - try { const result = await token.symbol(); return result[0]; @@ -136,7 +135,8 @@ export function calcTokenValue(value, decimals) { * @returns {string | undefined} A lowercase address string. */ export function getTokenAddressParam(tokenData = {}) { - const value = tokenData?.args?._to || tokenData?.args?.[0]; + const value = + tokenData?.args?._to || tokenData?.args?.to || tokenData?.args?.[0]; return value?.toString().toLowerCase(); } @@ -223,7 +223,7 @@ export async function getAssetDetails( transactionData, existingCollectibles, ) { - const tokenData = getTransactionData(transactionData); + const tokenData = parseStandardTokenTransactionData(transactionData); if (!tokenData) { throw new Error('Unable to detect valid token data'); } diff --git a/ui/helpers/utils/transactions.util.js b/ui/helpers/utils/transactions.util.js index 6bdf6f548..25a38593f 100644 --- a/ui/helpers/utils/transactions.util.js +++ b/ui/helpers/utils/transactions.util.js @@ -1,6 +1,4 @@ import { MethodRegistry } from 'eth-method-registry'; -import abi from 'human-standard-token-abi'; -import { ethers } from 'ethers'; import log from 'loglevel'; import { addHexPrefix } from '../../../app/scripts/lib/util'; @@ -14,8 +12,6 @@ import { addCurrencies } from '../../../shared/modules/conversion.utils'; import { readAddressAsContract } from '../../../shared/modules/contract-utils'; import fetchWithCache from './fetch-with-cache'; -const hstInterface = new ethers.utils.Interface(abi); - /** * @typedef EthersContractCall * @type object @@ -29,19 +25,6 @@ const hstInterface = new ethers.utils.Interface(abi); * representation of the function. */ -/** - * @param data - * @returns {EthersContractCall | undefined} - */ -export function getTransactionData(data) { - try { - return hstInterface.parseTransaction({ data }); - } catch (error) { - log.debug('Failed to parse transaction data.', error, data); - return undefined; - } -} - async function getMethodFrom4Byte(fourBytePrefix) { const fourByteResponse = await fetchWithCache( `https://www.4byte.directory/api/v1/signatures/?hex_signature=${fourBytePrefix}`, @@ -122,6 +105,7 @@ export function isTokenMethodAction(type) { TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER, TRANSACTION_TYPES.TOKEN_METHOD_APPROVE, TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM, + TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM, ].includes(type); } @@ -215,6 +199,9 @@ export function getTransactionTypeTitle(t, type, nativeCurrency = 'ETH') { case TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER_FROM: { return t('transferFrom'); } + case TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM: { + return t('safeTransferFrom'); + } case TRANSACTION_TYPES.TOKEN_METHOD_APPROVE: { return t('approve'); } diff --git a/ui/helpers/utils/transactions.util.test.js b/ui/helpers/utils/transactions.util.test.js index ce5225076..591e46d07 100644 --- a/ui/helpers/utils/transactions.util.test.js +++ b/ui/helpers/utils/transactions.util.test.js @@ -1,5 +1,4 @@ import { - TRANSACTION_TYPES, TRANSACTION_GROUP_STATUSES, TRANSACTION_STATUSES, TRANSACTION_ENVELOPE_TYPES, @@ -7,25 +6,6 @@ import { import * as utils from './transactions.util'; describe('Transactions utils', () => { - describe('getTransactionData', () => { - it('should return token data', () => { - const tokenData = utils.getTransactionData( - '0xa9059cbb00000000000000000000000050a9d56c2b8ba9a5c7f2c08c3d26e0499f23a7060000000000000000000000000000000000000000000000000000000000004e20', - ); - expect(tokenData).toStrictEqual(expect.anything()); - const { name, args } = tokenData; - expect(name).toStrictEqual(TRANSACTION_TYPES.TOKEN_METHOD_TRANSFER); - const to = args._to; - const value = args._value.toString(); - expect(to).toStrictEqual('0x50A9D56C2B8BA9A5c7f2C08C3d26E0499F23a706'); - expect(value).toStrictEqual('20000'); - }); - - it('should not throw errors when called without arguments', () => { - expect(() => utils.getTransactionData()).not.toThrow(); - }); - }); - describe('getStatusKey', () => { it('should return the correct status', () => { const tests = [ diff --git a/ui/hooks/gasFeeInput/useTransactionFunction.test.js b/ui/hooks/gasFeeInput/useTransactionFunction.test.js index f4c11dbf0..dcfee4d26 100644 --- a/ui/hooks/gasFeeInput/useTransactionFunction.test.js +++ b/ui/hooks/gasFeeInput/useTransactionFunction.test.js @@ -70,50 +70,53 @@ describe('useMaxPriorityFeePerGasInput', () => { expect(mock).toHaveBeenCalledTimes(1); }); - it('should invoke action updateTransaction with 10% increased fee when updateTransactionToTenPercentIncreasedGasFee callback is invoked', () => { - const mock = jest - .spyOn(Actions, 'updateTransaction') + it('should invoke action updateTransaction with 10% increased fee when updateTransactionToTenPercentIncreasedGasFee callback is invoked', async () => { + const mockUpdateGasFees = jest + .spyOn(Actions, 'updateTransactionGasFees') .mockImplementation(() => ({ type: '' })); + const { result } = renderUseTransactionFunctions(); - result.current.updateTransactionToTenPercentIncreasedGasFee(); - expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith({ - txParams: { - estimateSuggested: 'tenPercentIncreased', - estimateUsed: 'tenPercentIncreased', - gas: '5208', - gasLimit: '5208', - maxFeePerGas: '0x582c', - maxPriorityFeePerGas: '0x582c', - }, + await result.current.updateTransactionToTenPercentIncreasedGasFee(); + expect(mockUpdateGasFees).toHaveBeenCalledTimes(1); + expect(mockUpdateGasFees).toHaveBeenCalledWith(undefined, { + estimateSuggested: 'tenPercentIncreased', + estimateUsed: 'tenPercentIncreased', + gas: '5208', + gasLimit: '5208', + maxFeePerGas: '0x582c', + maxPriorityFeePerGas: '0x582c', + userEditedGasLimit: undefined, userFeeLevel: 'tenPercentIncreased', }); }); - it('should invoke action updateTransaction with estimate gas values fee when updateTransactionUsingEstimate callback is invoked', () => { - const mock = jest - .spyOn(Actions, 'updateTransaction') + it('should invoke action updateTransaction with estimate gas values fee when updateTransactionUsingEstimate callback is invoked', async () => { + const mockUpdateGasFees = jest + .spyOn(Actions, 'updateTransactionGasFees') .mockImplementation(() => ({ type: '' })); + const { result } = renderUseTransactionFunctions(); - result.current.updateTransactionUsingEstimate(GAS_RECOMMENDATIONS.LOW); - expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith({ - txParams: { - estimateSuggested: 'medium', - estimateUsed: 'low', - gas: '5208', - gasLimit: '5208', - maxFeePerGas: 'c570bd200', - maxPriorityFeePerGas: 'b2d05e00', - }, + await result.current.updateTransactionUsingEstimate( + GAS_RECOMMENDATIONS.LOW, + ); + expect(mockUpdateGasFees).toHaveBeenCalledTimes(1); + expect(mockUpdateGasFees).toHaveBeenCalledWith(undefined, { + estimateSuggested: 'medium', + estimateUsed: 'low', + gas: '5208', + gasLimit: '5208', + maxFeePerGas: 'c570bd200', + maxPriorityFeePerGas: 'b2d05e00', + userEditedGasLimit: undefined, userFeeLevel: 'low', }); }); - it('should invoke action updateTransaction with dappSuggestedValues values fee when updateTransactionUsingDAPPSuggestedValues callback is invoked', () => { - const mock = jest - .spyOn(Actions, 'updateTransaction') + it('should invoke action updateTransaction with dappSuggestedValues values fee when updateTransactionUsingDAPPSuggestedValues callback is invoked', async () => { + const mockUpdateGasFees = jest + .spyOn(Actions, 'updateTransactionGasFees') .mockImplementation(() => ({ type: '' })); + const { result } = renderUseTransactionFunctions({ transaction: { userFeeLevel: CUSTOM_GAS_ESTIMATE, @@ -123,21 +126,16 @@ describe('useMaxPriorityFeePerGasInput', () => { }, }, }); - result.current.updateTransactionUsingDAPPSuggestedValues(); - expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith({ - dappSuggestedGasFees: { - maxFeePerGas: '0x5028', - maxPriorityFeePerGas: '0x5028', - }, - txParams: { - estimateSuggested: 'medium', - estimateUsed: 'dappSuggested', - gas: '5208', - gasLimit: '5208', - maxFeePerGas: '0x5028', - maxPriorityFeePerGas: '0x5028', - }, + await result.current.updateTransactionUsingDAPPSuggestedValues(); + expect(mockUpdateGasFees).toHaveBeenCalledTimes(1); + expect(mockUpdateGasFees).toHaveBeenCalledWith(undefined, { + estimateSuggested: 'medium', + estimateUsed: 'dappSuggested', + gas: '5208', + gasLimit: '5208', + maxFeePerGas: '0x5028', + maxPriorityFeePerGas: '0x5028', + userEditedGasLimit: undefined, userFeeLevel: 'dappSuggested', }); }); diff --git a/ui/hooks/gasFeeInput/useTransactionFunctions.js b/ui/hooks/gasFeeInput/useTransactionFunctions.js index 967842026..616684386 100644 --- a/ui/hooks/gasFeeInput/useTransactionFunctions.js +++ b/ui/hooks/gasFeeInput/useTransactionFunctions.js @@ -11,8 +11,9 @@ import { createCancelTransaction, createSpeedUpTransaction, updateCustomSwapsEIP1559GasParams, + updatePreviousGasParams, updateSwapsUserFeeLevel, - updateTransaction as updateTransactionFn, + updateTransactionGasFees, } from '../../store/actions'; export const useTransactionFunctions = ({ @@ -49,7 +50,7 @@ export const useTransactionFunctions = ({ }, [editGasMode, transaction?.previousGas, transaction?.txParams]); const updateTransaction = useCallback( - ({ + async ({ estimateUsed, gasLimit, maxFeePerGas, @@ -87,7 +88,18 @@ export const useTransactionFunctions = ({ ); dispatch(updateCustomSwapsEIP1559GasParams(newGasSettings)); } else { - dispatch(updateTransactionFn(updatedTxMeta)); + newGasSettings.userEditedGasLimit = updatedTxMeta.userEditedGasLimit; + newGasSettings.userFeeLevel = updatedTxMeta.userFeeLevel; + + if (txMeta && txMeta.previousGas) { + await dispatch( + updatePreviousGasParams(updatedTxMeta.id, txMeta.previousGas), + ); + } + + await dispatch( + updateTransactionGasFees(updatedTxMeta.id, newGasSettings), + ); } }, [ diff --git a/ui/hooks/useAssetDetails.js b/ui/hooks/useAssetDetails.js index ac12ed494..ce9ef30ba 100644 --- a/ui/hooks/useAssetDetails.js +++ b/ui/hooks/useAssetDetails.js @@ -1,5 +1,6 @@ import { useState, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; +import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; import { getCollectibles, getTokens } from '../ducks/metamask/metamask'; import { ERC1155, ERC721, ERC20 } from '../helpers/constants/common'; import { @@ -8,14 +9,12 @@ import { getTokenAddressParam, getTokenValueParam, } from '../helpers/utils/token-util'; -import { getTransactionData } from '../helpers/utils/transactions.util'; import { getTokenList } from '../selectors'; import { hideLoadingIndication, showLoadingIndication } from '../store/actions'; import { usePrevious } from './usePrevious'; export function useAssetDetails(tokenAddress, userAddress, transactionData) { const dispatch = useDispatch(); - // state selectors const tokens = useSelector(getTokens); const collectibles = useSelector(getCollectibles); @@ -84,7 +83,7 @@ export function useAssetDetails(tokenAddress, userAddress, transactionData) { balance, decimals: currentAssetDecimals, } = currentAsset; - const tokenData = getTransactionData(transactionData); + const tokenData = parseStandardTokenTransactionData(transactionData); assetStandard = standard; assetAddress = tokenAddress; tokenSymbol = symbol; diff --git a/ui/hooks/useEventFragment.js b/ui/hooks/useEventFragment.js index 2e8d9ec0d..2447756ca 100644 --- a/ui/hooks/useEventFragment.js +++ b/ui/hooks/useEventFragment.js @@ -7,7 +7,7 @@ import { createEventFragment, updateEventFragment, } from '../store/actions'; -import { useMetaMetricsContext } from './useMetricEvent'; +import { useSegmentContext } from './useSegmentContext'; /** * Retrieves a fragment from memory or initializes new fragment if one does not @@ -59,7 +59,7 @@ export function useEventFragment(existingId, fragmentOptions = {}) { } }, [fragment, fragmentOptions]); - const context = useMetaMetricsContext(); + const context = useSegmentContext(); /** * trackSuccess is used to close a fragment with the affirmative action. This diff --git a/ui/hooks/useEventFragment.test.js b/ui/hooks/useEventFragment.test.js index f40febd82..2bef8c690 100644 --- a/ui/hooks/useEventFragment.test.js +++ b/ui/hooks/useEventFragment.test.js @@ -13,8 +13,8 @@ jest.mock('../store/actions', () => ({ createEventFragment: jest.fn(), })); -jest.mock('./useMetricEvent', () => ({ - useMetaMetricsContext: jest.fn(() => ({ page: '/' })), +jest.mock('./useSegmentContext', () => ({ + useSegmentContext: jest.fn(() => ({ page: '/' })), })); jest.mock('react-redux', () => ({ diff --git a/ui/hooks/useMetricEvent.js b/ui/hooks/useMetricEvent.js deleted file mode 100644 index f6e44574a..000000000 --- a/ui/hooks/useMetricEvent.js +++ /dev/null @@ -1,86 +0,0 @@ -import { useContext, useCallback } from 'react'; -import { useSelector } from 'react-redux'; -import { useRouteMatch } from 'react-router-dom'; -import { MetaMetricsContext } from '../contexts/metametrics'; -import { MetaMetricsContext as NewMetaMetricsContext } from '../contexts/metametrics.new'; -import { PATH_NAME_MAP } from '../helpers/constants/routes'; -import { txDataSelector } from '../selectors'; -import { useEqualityCheck } from './useEqualityCheck'; - -// Type imports -/** - * @typedef {import('../contexts/metametrics.new').UIMetricsEventPayload} UIMetricsEventPayload - * @typedef {import('../../shared/constants/metametrics').MetaMetricsEventOptions} MetaMetricsEventOptions - */ - -export function useMetricEvent(config = {}, overrides = {}) { - const metricsEvent = useContext(MetaMetricsContext); - const trackEvent = useCallback(() => metricsEvent(config, overrides), [ - config, - metricsEvent, - overrides, - ]); - return trackEvent; -} - -/** - * track a metametrics event using segment - * e.g metricsEvent({ event: 'Unlocked MetaMask', category: 'Navigation' }) - * - * @param {UIMetricsEventPayload} payload - payload of the event to track - * @param {MetaMetricsEventOptions} options - options for handling/routing event - * @returns {() => Promise} function to execute the tracking event - */ -export function useNewMetricEvent(payload, options) { - const memoizedPayload = useEqualityCheck(payload); - const memoizedOptions = useEqualityCheck(options); - const metricsEvent = useContext(NewMetaMetricsContext); - - return useCallback(() => metricsEvent(memoizedPayload, memoizedOptions), [ - metricsEvent, - memoizedPayload, - memoizedOptions, - ]); -} - -const PATHS_TO_CHECK = Object.keys(PATH_NAME_MAP); - -/** - * Returns the current page if it matches our route map, as well as the origin - * if there is a confirmation that was triggered by a dapp. These values are - * not required but add valuable context to events, and should be included in - * the context object on the event payload. - * - * @returns {{ - * page?: MetaMetricsPageObject - * referrer?: MetaMetricsReferrerObject - * }} - */ -export function useMetaMetricsContext() { - const match = useRouteMatch({ - path: PATHS_TO_CHECK, - exact: true, - strict: true, - }); - const txData = useSelector(txDataSelector) || {}; - const confirmTransactionOrigin = txData.origin; - - const referrer = confirmTransactionOrigin - ? { - url: confirmTransactionOrigin, - } - : undefined; - - const page = match - ? { - path: match.path, - title: PATH_NAME_MAP[match.path], - url: match.path, - } - : undefined; - - return { - page, - referrer, - }; -} diff --git a/ui/hooks/usePermissionDescriptions.js b/ui/hooks/usePermissionDescriptions.js deleted file mode 100644 index 054f47bfe..000000000 --- a/ui/hooks/usePermissionDescriptions.js +++ /dev/null @@ -1,98 +0,0 @@ -import { useMemo } from 'react'; -import { - RestrictedMethods, - ///: BEGIN:ONLY_INCLUDE_IN(flask) - EndowmentPermissions, - PermissionNamespaces, - ///: END:ONLY_INCLUDE_IN -} from '../../shared/constants/permissions'; -///: BEGIN:ONLY_INCLUDE_IN(flask) -import { coinTypeToProtocolName } from '../helpers/utils/util'; -///: END:ONLY_INCLUDE_IN -import { useI18nContext } from './useI18nContext'; - -const UNKNOWN_PERMISSION = Symbol('unknown'); - -/** - * @typedef {Object} PermissionLabelObject - * @property {string} label - The text label. - * @property {string} leftIcon - The left icon. - * @property {string} rightIcon - The right icon. - */ - -/** - * @returns {(permissionName:string) => PermissionLabelObject} - */ -export const usePermissionDescriptions = () => { - const t = useI18nContext(); - - return useMemo(() => { - const permissionDescriptions = { - [RestrictedMethods.eth_accounts]: { - leftIcon: 'fas fa-eye', - label: t('permission_ethereumAccounts'), - rightIcon: null, - }, - ///: BEGIN:ONLY_INCLUDE_IN(flask) - [RestrictedMethods.snap_confirm]: { - leftIcon: 'fas fa-user-check', - label: t('permission_customConfirmation'), - rightIcon: null, - }, - [RestrictedMethods['snap_getBip44Entropy_*']]: (permissionName) => { - const coinType = permissionName.split('_').slice(-1); - return { - leftIcon: 'fas fa-door-open', - label: t('permission_manageBip44Keys', [ - coinTypeToProtocolName(coinType) || - `${coinType} (Unrecognized protocol)`, - ]), - rightIcon: null, - }; - }, - [RestrictedMethods.snap_manageState]: { - leftIcon: 'fas fa-download', - label: t('permission_manageState'), - rightIcon: null, - }, - [RestrictedMethods['wallet_snap_*']]: (permissionName) => { - const snapId = permissionName.split('_').slice(-1); - return { - leftIcon: 'fas fa-bolt', - label: t('permission_accessSnap', [snapId]), - rightIcon: null, - }; - }, - [EndowmentPermissions['endowment:network-access']]: { - leftIcon: 'fas fa-wifi', - label: t('permission_accessNetwork'), - rightIcon: null, - }, - ///: END:ONLY_INCLUDE_IN - [UNKNOWN_PERMISSION]: (permissionName) => { - return { - leftIcon: 'fas fa-times-circle', - label: t('permission_unknown', [permissionName ?? 'undefined']), - rightIcon: null, - }; - }, - }; - - return (permissionName) => { - let value = permissionDescriptions[UNKNOWN_PERMISSION]; - - if (Object.hasOwnProperty.call(permissionDescriptions, permissionName)) { - value = permissionDescriptions[permissionName]; - } - ///: BEGIN:ONLY_INCLUDE_IN(flask) - for (const namespace of Object.keys(PermissionNamespaces)) { - if (permissionName.startsWith(namespace)) { - value = permissionDescriptions[PermissionNamespaces[namespace]]; - } - } - ///: END:ONLY_INCLUDE_IN - - return typeof value === 'function' ? value(permissionName) : value; - }; - }, [t]); -}; diff --git a/ui/hooks/useSegmentContext.js b/ui/hooks/useSegmentContext.js new file mode 100644 index 000000000..4b6ad6def --- /dev/null +++ b/ui/hooks/useSegmentContext.js @@ -0,0 +1,46 @@ +import { useSelector } from 'react-redux'; +import { useRouteMatch } from 'react-router-dom'; +import { PATH_NAME_MAP } from '../helpers/constants/routes'; +import { txDataSelector } from '../selectors'; + +const PATHS_TO_CHECK = Object.keys(PATH_NAME_MAP); + +/** + * Returns the current page if it matches our route map, as well as the origin + * if there is a confirmation that was triggered by a dapp. These values are + * not required but add valuable context to events, and should be included in + * the context object on the event payload. + * + * @returns {{ + * page?: MetaMetricsPageObject + * referrer?: MetaMetricsReferrerObject + * }} + */ +export function useSegmentContext() { + const match = useRouteMatch({ + path: PATHS_TO_CHECK, + exact: true, + strict: true, + }); + const txData = useSelector(txDataSelector) || {}; + const confirmTransactionOrigin = txData.origin; + + const referrer = confirmTransactionOrigin + ? { + url: confirmTransactionOrigin, + } + : undefined; + + const page = match + ? { + path: match.path, + title: PATH_NAME_MAP[match.path], + url: match.path, + } + : undefined; + + return { + page, + referrer, + }; +} diff --git a/ui/hooks/useTokenData.js b/ui/hooks/useTokenData.js index d1d7e1ffe..654bb50ff 100644 --- a/ui/hooks/useTokenData.js +++ b/ui/hooks/useTokenData.js @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { getTransactionData } from '../helpers/utils/transactions.util'; +import { parseStandardTokenTransactionData } from '../../shared/modules/transaction.utils'; /** * useTokenData @@ -19,6 +19,6 @@ export function useTokenData(transactionData, isTokenTransaction = true) { if (!isTokenTransaction || !transactionData) { return null; } - return getTransactionData(transactionData); + return parseStandardTokenTransactionData(transactionData); }, [isTokenTransaction, transactionData]); } diff --git a/ui/hooks/useTokenDisplayValue.test.js b/ui/hooks/useTokenDisplayValue.test.js index 9be126f81..26e2a4374 100644 --- a/ui/hooks/useTokenDisplayValue.test.js +++ b/ui/hooks/useTokenDisplayValue.test.js @@ -1,7 +1,7 @@ import { renderHook } from '@testing-library/react-hooks'; import sinon from 'sinon'; import * as tokenUtil from '../helpers/utils/token-util'; -import * as txUtil from '../helpers/utils/transactions.util'; +import * as txUtil from '../../shared/modules/transaction.utils'; import { useTokenDisplayValue } from './useTokenDisplayValue'; const tests = [ @@ -122,9 +122,12 @@ describe('useTokenDisplayValue', () => { describe(`when input is decimals: ${token.decimals} and value: ${tokenValue}`, () => { it(`should return ${displayValue} as displayValue`, () => { const getTokenValueStub = sinon.stub(tokenUtil, 'getTokenValueParam'); - const getTokenDataStub = sinon.stub(txUtil, 'getTransactionData'); + const parseStandardTokenTransactionDataStub = sinon.stub( + txUtil, + 'parseStandardTokenTransactionData', + ); - getTokenDataStub.callsFake(() => tokenData); + parseStandardTokenTransactionDataStub.callsFake(() => tokenData); getTokenValueStub.callsFake(() => tokenValue); const { result } = renderHook(() => diff --git a/ui/hooks/useUserPreferencedCurrency.js b/ui/hooks/useUserPreferencedCurrency.js index 79774e925..2559a29c3 100644 --- a/ui/hooks/useUserPreferencedCurrency.js +++ b/ui/hooks/useUserPreferencedCurrency.js @@ -44,7 +44,7 @@ export function useUserPreferencedCurrency(type, opts = {}) { getPreferences, shallowEqual, ); - const showFiat = useSelector(getShouldShowFiat); + const showFiat = useSelector(getShouldShowFiat) || opts.showFiatOverride; const currentCurrency = useSelector(getCurrentCurrency); let currency, numberOfDecimals; diff --git a/ui/index.js b/ui/index.js index 3fcca28fd..5c90d00a1 100644 --- a/ui/index.js +++ b/ui/index.js @@ -3,6 +3,8 @@ import log from 'loglevel'; import { clone } from 'lodash'; import React from 'react'; import { render } from 'react-dom'; +import browser from 'webextension-polyfill'; + import { getEnvironmentType } from '../app/scripts/lib/util'; import { ALERT_TYPES } from '../shared/constants/alerts'; import { SENTRY_STATE } from '../app/scripts/lib/setupSentry'; @@ -205,15 +207,16 @@ function setupDebuggingHelpers(store) { window.logStateString = async function (cb) { const state = await window.getCleanAppState(); - global.platform.getPlatformInfo((err, platform) => { - if (err) { + browser.runtime + .getPlatformInfo() + .then((platform) => { + state.platform = platform; + const stateString = JSON.stringify(state, null, 2); + cb(null, stateString); + }) + .catch((err) => { cb(err); - return; - } - state.platform = platform; - const stateString = JSON.stringify(state, null, 2); - cb(null, stateString); - }); + }); }; window.logState = function (toClipboard) { diff --git a/ui/pages/add-collectible/add-collectible.js b/ui/pages/add-collectible/add-collectible.js index bea9d2046..c5192cec3 100644 --- a/ui/pages/add-collectible/add-collectible.js +++ b/ui/pages/add-collectible/add-collectible.js @@ -103,7 +103,7 @@ export default function AddCollectible() { @@ -51,7 +42,15 @@ export default function NativeAsset({ nativeCurrency }) { { - blockExplorerLinkClickedEvent(); + trackEvent({ + event: 'Clicked Block Explorer Link', + category: 'Navigation', + properties: { + link_type: 'Account Tracker', + action: 'Asset Options', + block_explorer_domain: getURLHostName(accountLink), + }, + }); global.platform.openTab({ url: accountLink, }); diff --git a/ui/pages/asset/components/token-asset.js b/ui/pages/asset/components/token-asset.js index a719fb8b6..6ccf944c4 100644 --- a/ui/pages/asset/components/token-asset.js +++ b/ui/pages/asset/components/token-asset.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; @@ -16,7 +16,7 @@ import { } from '../../../helpers/constants/routes'; import { getURLHostName } from '../../../helpers/utils/util'; import { showModal } from '../../../store/actions'; -import { useNewMetricEvent } from '../../../hooks/useMetricEvent'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; import AssetNavigation from './asset-navigation'; import AssetOptions from './asset-options'; @@ -35,16 +35,7 @@ export default function TokenAsset({ token }) { selectedAddress, rpcPrefs, ); - - const blockExplorerLinkClickedEvent = useNewMetricEvent({ - category: 'Navigation', - event: 'Clicked Block Explorer Link', - properties: { - link_type: 'Token Tracker', - action: 'Token Options', - block_explorer_domain: getURLHostName(tokenTrackerLink), - }, - }); + const trackEvent = useContext(MetaMetricsContext); return ( <> @@ -61,7 +52,15 @@ export default function TokenAsset({ token }) { } isEthNetwork={!rpcPrefs.blockExplorerUrl} onClickBlockExplorer={() => { - blockExplorerLinkClickedEvent(); + trackEvent({ + event: 'Clicked Block Explorer Link', + category: 'Navigation', + properties: { + link_type: 'Token Tracker', + action: 'Token Options', + block_explorer_domain: getURLHostName(tokenTrackerLink), + }, + }); global.platform.openTab({ url: tokenTrackerLink }); }} onViewAccountDetails={() => { diff --git a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.js b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.js index cb59f95d6..31c58c13b 100644 --- a/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.js +++ b/ui/pages/confirm-add-suggested-token/confirm-add-suggested-token.js @@ -13,6 +13,7 @@ import ZENDESK_URLS from '../../helpers/constants/zendesk-url'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; import { getSuggestedAssets } from '../../selectors'; import { rejectWatchAsset, acceptWatchAsset } from '../../store/actions'; +import { TOKEN_STANDARDS } from '../../helpers/constants/common'; function getTokenName(name, symbol) { return name === undefined ? symbol : `${name} (${symbol})`; @@ -120,6 +121,7 @@ const ConfirmAddSuggestedToken = () => { token_decimal_precision: asset.decimals, unlisted: asset.unlisted, source: 'dapp', + token_standard: TOKEN_STANDARDS.ERC20, }, }); }), diff --git a/ui/pages/confirm-add-suggested-token/index.scss b/ui/pages/confirm-add-suggested-token/index.scss index 714e47286..de04ed6bf 100644 --- a/ui/pages/confirm-add-suggested-token/index.scss +++ b/ui/pages/confirm-add-suggested-token/index.scss @@ -5,7 +5,7 @@ @include H7; display: inline; - color: var(--primary-blue); + color: var(--color-primary-default); padding-left: 0; } diff --git a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js index 1f349d6ad..c238d9106 100644 --- a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js +++ b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js @@ -226,7 +226,7 @@ export default class ConfirmApproveContent extends Component { : t('copyToClipboard') } > - +
@@ -286,7 +286,7 @@ export default class ConfirmApproveContent extends Component { : t('copyToClipboard') } > - +
@@ -327,7 +327,7 @@ export default class ConfirmApproveContent extends Component {
{this.renderApproveContentCard({ - symbol: , + symbol: , title: t('permissionRequest'), content: this.renderERC20PermissionContent(), showEdit: true, @@ -358,7 +358,7 @@ export default class ConfirmApproveContent extends Component {
{this.renderApproveContentCard({ - symbol: , + symbol: , title: t('permissionRequest'), content: this.renderERC721OrERC1155PermissionContent(), showEdit: false, @@ -541,7 +541,7 @@ export default class ConfirmApproveContent extends Component { : t('copyToClipboard') } > - + diff --git a/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.stories.js b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.stories.js new file mode 100644 index 000000000..de304a1c9 --- /dev/null +++ b/ui/pages/confirm-approve/confirm-approve-content/confirm-approve-content.stories.js @@ -0,0 +1,141 @@ +import React from 'react'; +import { ERC20 } from '../../../helpers/constants/common'; +import ConfirmApproveContent from '.'; + +export default { + title: 'Pages/ConfirmApprove/ConfirmApproveContent', + id: __filename, + component: ConfirmApproveContent, + argTypes: { + decimals: { + control: 'number', + }, + tokenAmount: { + control: 'text', + }, + customTokenAmount: { + control: 'text', + }, + tokenSymbol: { + control: 'text', + }, + siteImage: { + control: 'text', + }, + showCustomizeGasModal: { + action: 'showCustomizeGasModal', + }, + showEditApprovalPermissionModal: { + action: 'showEditApprovalPermissionModal', + }, + origin: { + control: 'text', + }, + setCustomAmount: { + action: 'setCustomAmount', + }, + tokenBalance: { + control: 'text', + }, + data: { + control: 'text', + }, + toAddress: { + control: 'text', + }, + currentCurrency: { + control: 'text', + }, + nativeCurrency: { + control: 'text', + }, + fiatTransactionTotal: { + control: 'text', + }, + ethTransactionTotal: { + control: 'text', + }, + useNonceField: { + control: 'boolean', + }, + customNonceValue: { + control: 'text', + }, + updateCustomNonce: { + action: 'updateCustomNonce', + }, + getNextNonce: { + action: 'getNextNonce', + }, + nextNonce: { + control: 'number', + }, + showCustomizeNonceModal: { + action: 'showCustomizeNonceModal', + }, + warning: { + control: 'text', + }, + txData: { + control: 'object', + }, + fromAddressIsLedger: { + control: 'boolean', + }, + chainId: { + control: 'text', + }, + rpcPrefs: { + control: 'object', + }, + isContract: { + control: 'boolean', + }, + hexTransactionTotal: { + control: 'text', + }, + isMultiLayerFeeNetwork: { + control: 'boolean', + }, + supportsEIP1559V2: { + control: 'boolean', + }, + assetName: { + control: 'text', + }, + tokenId: { + control: 'text', + }, + assetStandard: { + control: 'text', + }, + }, + args: { + decimals: 16, + siteImage: 'https://metamask.github.io/test-dapp/metamask-fox.svg', + customTokenAmount: '10', + tokenAmount: '10', + origin: 'https://metamask.github.io/test-dapp/', + tokenSymbol: 'TST', + assetStandard: ERC20, + tokenImage: 'https://metamask.github.io/test-dapp/metamask-fox.svg', + tokenBalance: '15', + data: + '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170', + toAddress: '0x9bc5baf874d2da8d216ae9f137804184ee5afef4', + currentCurrency: 'TST', + nativeCurrency: 'ETH', + ethTransactionTotal: '20', + fiatTransactionTotal: '10', + useNonceField: true, + nextNonce: 1, + customNonceValue: '2', + chainId: '1337', + rpcPrefs: {}, + isContract: true, + }, +}; + +export const DefaultStory = (args) => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/pages/confirm-approve/confirm-approve-content/index.scss b/ui/pages/confirm-approve/confirm-approve-content/index.scss index 7ae12ea06..0f2593ee4 100644 --- a/ui/pages/confirm-approve/confirm-approve-content/index.scss +++ b/ui/pages/confirm-approve/confirm-approve-content/index.scss @@ -15,7 +15,7 @@ width: 65%; margin-top: 16px; padding: 12px 12px 14px 12px; - border: 1px solid var(--ui-1); + border: 1px solid var(--color-border-default); box-sizing: border-box; border-radius: 100px; align-items: center; @@ -47,7 +47,7 @@ display: flex; height: 27px; padding: 12px 12px 14px 12px; - background-color: var(--ui-1); + background-color: var(--color-background-alternative); border-radius: 100px; align-items: center; justify-content: center; @@ -56,7 +56,7 @@ &__siteimage-identicon { width: 33px; height: 33px; - border: solid #fff; + border: solid var(--color-border-muted); } &__address-identicon { @@ -68,11 +68,9 @@ padding: 0 0 0 8px; } - &__etherscan-link { - img { - width: 9px; - height: 9px; - } + &__etherscan-link img { + width: 9px; + height: 9px; } .app-header__logo-container { @@ -113,7 +111,7 @@ margin-top: 16px; margin-bottom: 16px; - color: #6a737d; + color: var(--color-text-alternative); text-align: center; padding-left: 24px; padding-right: 24px; @@ -123,7 +121,7 @@ &__card--no-border { display: flex; flex-flow: column; - border-bottom: 1px solid #d2d8dd; + border-bottom: 1px solid var(--color-border-default); position: relative; padding-left: 24px; padding-right: 24px; @@ -137,7 +135,7 @@ &__thin-text { @include H7; - color: #6a737d; + color: var(--color-text-alternative); } } @@ -204,7 +202,7 @@ div { width: 22px; height: 2px; - background: var(--primary-blue); + background: var(--color-primary-default); position: absolute; } @@ -220,9 +218,9 @@ &__circle { width: 14px; height: 14px; - border: 2px solid var(--primary-blue); + border: 2px solid var(--color-primary-default); border-radius: 50%; - background: white; + background: var(--color-background-default); position: absolute; } } @@ -252,7 +250,7 @@ @include H4; font-weight: bold; - color: #000; + color: var(--color-text-default); } &__secondary-fee, @@ -260,7 +258,7 @@ @include H6; font-weight: normal; - color: #8c8e94; + color: var(--color-text-muted); } &__labelled-fee { @@ -288,7 +286,7 @@ i { margin-left: 6px; display: flex; - color: #3099f2; + color: var(--color-primary-default); align-items: center; } } @@ -303,7 +301,7 @@ flex-flow: row; padding-top: 15px; padding-bottom: 30px; - border-bottom: 1px solid #d2d8dd; + border-bottom: 1px solid var(--color-border-muted); width: 100%; justify-content: center; padding-left: 24px; @@ -313,21 +311,21 @@ &__large-text { @include H4; - color: #24292e; + color: var(--color-text-default); } &__medium-link-text { @include H6; font-weight: 500; - color: var(--primary-blue); + color: var(--color-primary-default); } &__medium-text, &__label { @include H6; - color: #24292e; + color: var(--color-text-default); } &__label { @@ -340,11 +338,11 @@ &__info-row { @include H7; - color: #6a737d; + color: var(--color-text-alternative); } &__small-blue-text { - color: var(--primary-blue); + color: var(--color-primary-default); } &__info-row { @@ -395,7 +393,7 @@ margin-top: 5px; margin-bottom: 6px; padding: 12px 12px 14px 12px; - border: 1px solid #bbc0c5; + border: 1px solid var(--color-border-muted); box-sizing: border-box; border-radius: 6px; align-items: center; diff --git a/ui/pages/confirm-approve/confirm-approve.util.js b/ui/pages/confirm-approve/confirm-approve.util.js index 7bc3ea550..022dc108a 100644 --- a/ui/pages/confirm-approve/confirm-approve.util.js +++ b/ui/pages/confirm-approve/confirm-approve.util.js @@ -1,16 +1,16 @@ import { TRANSACTION_TYPES } from '../../../shared/constants/transaction'; +import { parseStandardTokenTransactionData } from '../../../shared/modules/transaction.utils'; import { decimalToHex } from '../../helpers/utils/conversions.util'; import { calcTokenValue, getTokenAddressParam, } from '../../helpers/utils/token-util'; -import { getTransactionData } from '../../helpers/utils/transactions.util'; export function getCustomTxParamsData( data, { customPermissionAmount, decimals }, ) { - const tokenData = getTransactionData(data); + const tokenData = parseStandardTokenTransactionData(data); if (!tokenData) { throw new Error('Invalid data'); diff --git a/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js b/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js index 950497837..917af3af9 100644 --- a/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js +++ b/ui/pages/confirm-decrypt-message/confirm-decrypt-message.component.js @@ -15,7 +15,7 @@ import { conversionUtil } from '../../../shared/modules/conversion.utils'; export default class ConfirmDecryptMessage extends Component { static contextTypes = { t: PropTypes.func.isRequired, - metricsEvent: PropTypes.func.isRequired, + trackEvent: PropTypes.func.isRequired, }; static propTypes = { @@ -44,11 +44,12 @@ export default class ConfirmDecryptMessage extends Component { copyMessage = () => { copyToClipboard(this.state.rawMessage); - this.context.metricsEvent({ - eventOpts: { - category: 'Messages', + this.context.trackEvent({ + category: 'Messages', + event: 'Copy', + properties: { action: 'Decrypt Message Copy', - name: 'Copy', + legacy_event: true, }, }); this.setState({ hasCopied: true }); @@ -177,15 +178,13 @@ export default class ConfirmDecryptMessage extends Component { {hasError ? errorMessage : ''}
- -
- {t('decryptMetamask')} +
+ +
+ {t('decryptMetamask')} +
@@ -232,7 +233,7 @@ export default class ConfirmDecryptMessage extends Component {
{t('decryptCopy')}
- +
) : ( @@ -251,7 +252,7 @@ export default class ConfirmDecryptMessage extends Component { mostRecentOverviewPage, txData, } = this.props; - const { metricsEvent, t } = this.context; + const { trackEvent, t } = this.context; return (
@@ -261,11 +262,12 @@ export default class ConfirmDecryptMessage extends Component { className="request-decrypt-message__footer__cancel-button" onClick={async (event) => { await cancelDecryptMessage(txData, event); - metricsEvent({ - eventOpts: { - category: 'Messages', + trackEvent({ + category: 'Messages', + event: 'Cancel', + properties: { action: 'Decrypt Message Request', - name: 'Cancel', + legacy_event: true, }, }); clearConfirmTransaction(); @@ -280,11 +282,12 @@ export default class ConfirmDecryptMessage extends Component { className="request-decrypt-message__footer__sign-button" onClick={async (event) => { await decryptMessage(txData, event); - metricsEvent({ - eventOpts: { - category: 'Messages', + trackEvent({ + category: 'Messages', + event: 'Confirm', + properties: { action: 'Decrypt Message Request', - name: 'Confirm', + legacy_event: true, }, }); clearConfirmTransaction(); diff --git a/ui/pages/confirm-decrypt-message/confirm-decrypt-message.scss b/ui/pages/confirm-decrypt-message/confirm-decrypt-message.scss index 7227a3552..a619e5837 100644 --- a/ui/pages/confirm-decrypt-message/confirm-decrypt-message.scss +++ b/ui/pages/confirm-decrypt-message/confirm-decrypt-message.scss @@ -2,7 +2,7 @@ &__container { width: 380px; border-radius: 8px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); display: flex; flex-flow: column nowrap; @@ -54,7 +54,7 @@ &__header-background { position: absolute; - background-color: var(--athens-grey); + background-color: var(--color-background-alternative); z-index: 2; width: 100%; height: 100%; @@ -63,7 +63,7 @@ &__header__text { @include H3; - color: #5b5d67; + color: var(--color-text-alternative); z-index: 3; text-align: center; } @@ -77,7 +77,7 @@ &__header__tip { height: 25px; width: 25px; - background: var(--athens-grey); + background: var(--color-background-alternative); transform: rotate(45deg); position: absolute; bottom: -8px; @@ -92,7 +92,7 @@ } &__account { - color: var(--dusty-gray); + color: var(--color-text-alternative); margin-left: 17px; } @@ -104,7 +104,7 @@ @include H7; height: 22px; - background-color: var(--white); + background-color: var(--color-background-default); width: 124px; .account-list-item { @@ -124,7 +124,7 @@ } &__balance { - color: var(--dusty-gray); + color: var(--color-text-alternative); margin-right: 17px; width: 124px; } @@ -166,7 +166,7 @@ overflow-wrap: break-word; margin: 20px; overflow: hidden; - border: 1px solid #dedede; + border: 1px solid var(--color-border-alternative); padding: 5px; border-radius: 5px; position: relative; @@ -178,7 +178,7 @@ } &-cover { - background-color: white; + background-color: var(--color-background-default); opacity: 0.75; position: absolute; height: 100%; @@ -193,13 +193,29 @@ top: 0; cursor: pointer; - img { - padding: 5px; - background-color: #fff; - left: calc(50% - 24px); + &__container { + padding: 16px; + background-color: var(--color-background-default); position: absolute; - top: calc(50% - 34px); + left: 50%; + top: 50%; border-radius: 3px; + transform: translate(-50%, calc(-50%)); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + + &__icon { + color: var(--color-icon-default); + display: flex; + margin-bottom: 16px; + } + + &__text { + @include H7; + } } &--pressed { @@ -207,18 +223,6 @@ } } - &-lock-text { - @include H7; - - width: 200px; - position: absolute; - top: calc(50% + 5px); - text-align: center; - left: calc(50% - 100px); - background-color: white; - border-radius: 3px; - } - &-copy { @include H7; @@ -248,7 +252,7 @@ justify-content: center; position: relative; flex: 0 0 auto; - border-top: 1px solid var(--geyser); + border-top: 1px solid var(--color-border-default); padding: 1.6rem; button { @@ -279,8 +283,8 @@ height: 48px; &--default { - background-color: #777a87; - color: white; + background-color: var(--color-background-alternative); + color: var(--color-text-default); width: 48px; height: 48px; border-radius: 24px; diff --git a/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js b/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js index 3ba59b5ab..66447378f 100644 --- a/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js +++ b/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.component.js @@ -10,7 +10,7 @@ import { conversionUtil } from '../../../shared/modules/conversion.utils'; export default class ConfirmEncryptionPublicKey extends Component { static contextTypes = { t: PropTypes.func.isRequired, - metricsEvent: PropTypes.func.isRequired, + trackEvent: PropTypes.func.isRequired, }; static propTypes = { @@ -154,7 +154,7 @@ export default class ConfirmEncryptionPublicKey extends Component { mostRecentOverviewPage, txData, } = this.props; - const { t, metricsEvent } = this.context; + const { t, trackEvent } = this.context; return (
@@ -164,11 +164,12 @@ export default class ConfirmEncryptionPublicKey extends Component { className="request-encryption-public-key__footer__cancel-button" onClick={async (event) => { await cancelEncryptionPublicKey(txData, event); - metricsEvent({ - eventOpts: { - category: 'Messages', + trackEvent({ + category: 'Messages', + event: 'Cancel', + properties: { action: 'Encryption public key Request', - name: 'Cancel', + legacy_event: true, }, }); clearConfirmTransaction(); @@ -183,11 +184,12 @@ export default class ConfirmEncryptionPublicKey extends Component { className="request-encryption-public-key__footer__sign-button" onClick={async (event) => { await encryptionPublicKey(txData, event); - this.context.metricsEvent({ - eventOpts: { - category: 'Messages', + this.context.trackEvent({ + category: 'Messages', + event: 'Confirm', + properties: { action: 'Encryption public key Request', - name: 'Confirm', + legacy_event: true, }, }); clearConfirmTransaction(); diff --git a/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.scss b/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.scss index e47c6cb50..caed0982a 100644 --- a/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.scss +++ b/ui/pages/confirm-encryption-public-key/confirm-encryption-public-key.scss @@ -2,7 +2,7 @@ &__container { width: 380px; border-radius: 8px; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08); display: flex; flex-flow: column nowrap; @@ -54,7 +54,7 @@ &__header-background { position: absolute; - background-color: var(--athens-grey); + background-color: var(--color-background-alternative); z-index: 2; width: 100%; height: 100%; @@ -63,7 +63,7 @@ &__header__text { @include H3; - color: #5b5d67; + color: var(--color-text-alternative); z-index: 3; text-align: center; } @@ -77,7 +77,7 @@ &__header__tip { height: 25px; width: 25px; - background: var(--athens-grey); + background: var(--color-background-alternative); transform: rotate(45deg); position: absolute; bottom: -8px; @@ -92,7 +92,7 @@ } &__account { - color: var(--dusty-gray); + color: var(--color-text-alternative); margin-left: 17px; } @@ -104,7 +104,7 @@ @include H7; height: 22px; - background-color: var(--white); + background-color: var(--color-background-default); width: 124px; .account-list-item { @@ -124,7 +124,7 @@ } &__balance { - color: var(--dusty-gray); + color: var(--color-text-alternative); margin-right: 17px; width: 124px; } @@ -162,7 +162,7 @@ width: 100%; padding-left: 20px; padding-right: 20px; - color: var(--dusty-gray); + color: var(--color-text-alternative); } &__footer { @@ -174,7 +174,7 @@ justify-content: center; position: relative; flex: 0 0 auto; - border-top: 1px solid var(--geyser); + border-top: 1px solid var(--color-border-muted); padding: 1.6rem; button { @@ -205,8 +205,8 @@ height: 48px; &--default { - background-color: #777a87; - color: white; + background-color: var(--color-background-alternative); + color: var(--color-text-alternative); width: 48px; height: 48px; border-radius: 24px; diff --git a/ui/pages/confirm-import-token/confirm-import-token.js b/ui/pages/confirm-import-token/confirm-import-token.js index 07ab83ef9..3a0a33811 100644 --- a/ui/pages/confirm-import-token/confirm-import-token.js +++ b/ui/pages/confirm-import-token/confirm-import-token.js @@ -13,6 +13,7 @@ import { MetaMetricsContext as NewMetaMetricsContext } from '../../contexts/meta import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { getPendingTokens } from '../../ducks/metamask/metamask'; import { addTokens, clearPendingTokens } from '../../store/actions'; +import { TOKEN_STANDARDS } from '../../helpers/constants/common'; const getTokenName = (name, symbol) => { return name === undefined ? symbol : `${name} (${symbol})`; @@ -43,6 +44,7 @@ const ConfirmImportToken = () => { token_decimal_precision: pendingToken.decimals, unlisted: pendingToken.unlisted, source: pendingToken.isCustom ? 'custom' : 'list', + token_standard: TOKEN_STANDARDS.ERC20, }, }); }); diff --git a/ui/pages/confirm-send-ether/confirm-send-ether.container.js b/ui/pages/confirm-send-ether/confirm-send-ether.container.js index a637fbb12..eb794b6ce 100644 --- a/ui/pages/confirm-send-ether/confirm-send-ether.container.js +++ b/ui/pages/confirm-send-ether/confirm-send-ether.container.js @@ -1,8 +1,9 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withRouter } from 'react-router-dom'; -import { ASSET_TYPES, editTransaction } from '../../ducks/send'; +import { editTransaction } from '../../ducks/send'; import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'; +import { ASSET_TYPES } from '../../../shared/constants/transaction'; import ConfirmSendEther from './confirm-send-ether.component'; const mapStateToProps = (state) => { diff --git a/ui/pages/confirm-send-token/confirm-send-token.container.js b/ui/pages/confirm-send-token/confirm-send-token.container.js index d823e25aa..d8a498424 100644 --- a/ui/pages/confirm-send-token/confirm-send-token.container.js +++ b/ui/pages/confirm-send-token/confirm-send-token.container.js @@ -3,8 +3,9 @@ import { compose } from 'redux'; import { withRouter } from 'react-router-dom'; import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'; import { showSendTokenPage } from '../../store/actions'; -import { ASSET_TYPES, editTransaction } from '../../ducks/send'; +import { editTransaction } from '../../ducks/send'; import { sendTokenTokenAmountAndToAddressSelector } from '../../selectors'; +import { ASSET_TYPES } from '../../../shared/constants/transaction'; import ConfirmSendToken from './confirm-send-token.component'; const mapStateToProps = (state) => { diff --git a/ui/pages/confirm-send-token/confirm-send-token.js b/ui/pages/confirm-send-token/confirm-send-token.js index a03fb9835..8d40d36c9 100644 --- a/ui/pages/confirm-send-token/confirm-send-token.js +++ b/ui/pages/confirm-send-token/confirm-send-token.js @@ -4,7 +4,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import ConfirmTokenTransactionBase from '../confirm-token-transaction-base/confirm-token-transaction-base'; import { SEND_ROUTE } from '../../helpers/constants/routes'; -import { ASSET_TYPES, editTransaction } from '../../ducks/send'; +import { editTransaction } from '../../ducks/send'; import { contractExchangeRateSelector, getCurrentCurrency, @@ -16,6 +16,7 @@ import { import { ERC20, ERC721 } from '../../helpers/constants/common'; import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck'; import { showSendTokenPage } from '../../store/actions'; +import { ASSET_TYPES } from '../../../shared/constants/transaction'; export default function ConfirmSendToken({ assetStandard, diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index adbb684c7..097aa7f25 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -70,7 +70,7 @@ const renderHeartBeatIfNotInTest = () => export default class ConfirmTransactionBase extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -146,6 +146,7 @@ export default class ConfirmTransactionBase extends Component { isMultiLayerFeeNetwork: PropTypes.bool, eip1559V2Enabled: PropTypes.bool, showBuyModal: PropTypes.func, + isBuyableChain: PropTypes.bool, }; state = { @@ -276,13 +277,12 @@ export default class ConfirmTransactionBase extends Component { methodData = {}, } = this.props; - this.context.metricsEvent({ - eventOpts: { - category: 'Transactions', + this.context.trackEvent({ + category: 'Transactions', + event: 'User clicks "Edit" on gas', + properties: { action: 'Confirm Screen', - name: 'User clicks "Edit" on gas', - }, - customVariables: { + legacy_event: true, recipientKnown: null, functionType: actionKey || @@ -326,6 +326,7 @@ export default class ConfirmTransactionBase extends Component { isMultiLayerFeeNetwork, nativeCurrency, showBuyModal, + isBuyableChain, } = this.props; const { t } = this.context; const { userAcknowledgedGasMissing } = this.state; @@ -448,9 +449,7 @@ export default class ConfirmTransactionBase extends Component { detailTitle={ txData.dappSuggestedGasFees ? ( <> - {isMultiLayerFeeNetwork - ? t('transactionDetailLayer2GasHeading') - : t('transactionDetailGasHeading')} + {t('transactionDetailGasHeading')} ) : ( <> - {isMultiLayerFeeNetwork - ? t('transactionDetailLayer2GasHeading') - : t('transactionDetailGasHeading')} + {t('transactionDetailGasHeading')} @@ -490,18 +487,15 @@ export default class ConfirmTransactionBase extends Component { ) } - detailTitleColor={COLORS.BLACK} detailText={ - !isMultiLayerFeeNetwork && ( -
- {renderHeartBeatIfNotInTest()} - -
- ) +
+ {renderHeartBeatIfNotInTest()} + +
} detailTotal={
@@ -510,30 +504,28 @@ export default class ConfirmTransactionBase extends Component { type={PRIMARY} value={hexMinimumTransactionFee} hideLabel={!useNativeCurrencyAsPrimaryCurrency} - numberOfDecimals={isMultiLayerFeeNetwork ? 18 : 6} + numberOfDecimals={6} />
} subText={ - !isMultiLayerFeeNetwork && ( - <> - - {t('editGasSubTextFeeLabel')} - -
- {renderHeartBeatIfNotInTest()} - -
- - ) + <> + + {t('editGasSubTextFeeLabel')} + +
+ {renderHeartBeatIfNotInTest()} + +
+ } subTitle={ <> @@ -586,21 +578,25 @@ export default class ConfirmTransactionBase extends Component { this.setUserAcknowledgedGasMissing() } userAcknowledgedGasMissing={userAcknowledgedGasMissing} - chainId={txData.chainId} nativeCurrency={nativeCurrency} networkName={networkName} showBuyModal={showBuyModal} type={txData.type} + isBuyableChain={isBuyableChain} /> this.handleEditGas() + renderSimulationFailureWarning || isMultiLayerFeeNetwork + ? null + : () => this.handleEditGas() } rows={[ renderSimulationFailureWarning && simulationFailureWarning(), - !renderSimulationFailureWarning && renderGasDetailsItem(), + !renderSimulationFailureWarning && + !isMultiLayerFeeNetwork && + renderGasDetailsItem(), !renderSimulationFailureWarning && isMultiLayerFeeNetwork && ( ); @@ -940,14 +935,13 @@ export default class ConfirmTransactionBase extends Component { getNextNonce, tryReverseResolveAddress, } = this.props; - const { metricsEvent } = this.context; - metricsEvent({ - eventOpts: { - category: 'Transactions', + const { trackEvent } = this.context; + trackEvent({ + category: 'Transactions', + event: 'Confirm: Started', + properties: { action: 'Confirm Screen', - name: 'Confirm: Started', - }, - customVariables: { + legacy_event: true, origin, }, }); diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index e5c325242..593d2bafe 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -33,16 +33,18 @@ import { getTokenList, getIsMultiLayerFeeNetwork, getEIP1559V2Enabled, + getIsBuyableChain, } from '../../selectors'; import { getMostRecentOverviewPage } from '../../ducks/history/history'; import { isAddressLedger, - updateTransactionGasFees, + updateGasFees, getIsGasEstimatesLoading, getNativeCurrency, } from '../../ducks/metamask/metamask'; import { + parseStandardTokenTransactionData, transactionMatchesNetwork, txParamsAreDappSuggested, } from '../../../shared/modules/transaction.utils'; @@ -52,6 +54,7 @@ import { getGasLoadingAnimationIsShowing } from '../../ducks/app/app'; import { isLegacyTransaction } from '../../helpers/utils/transactions.util'; import { CUSTOM_GAS_ESTIMATE } from '../../../shared/constants/gas'; import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils'; +import { getTokenAddressParam } from '../../helpers/utils/token-util'; import ConfirmTransactionBase from './confirm-transaction-base.component'; let customNonceValue = ''; @@ -74,7 +77,7 @@ const mapStateToProps = (state, ownProps) => { const isGasEstimatesLoading = getIsGasEstimatesLoading(state); const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state); - + const isBuyableChain = getIsBuyableChain(state); const { confirmTransaction, metamask } = state; const { ensResolutionsByAddress, @@ -104,9 +107,12 @@ const mapStateToProps = (state, ownProps) => { } = (transaction && transaction.txParams) || txParams; const accounts = getMetaMaskAccounts(state); + const transactionData = parseStandardTokenTransactionData(data); + const tokenToAddress = getTokenAddressParam(transactionData); + const { balance } = accounts[fromAddress]; const { name: fromName } = identities[fromAddress]; - const toAddress = propsToAddress || txParamsToAddress; + const toAddress = propsToAddress || tokenToAddress || txParamsToAddress; const tokenList = getTokenList(state); const useTokenDetection = getUseTokenDetection(state); @@ -248,6 +254,7 @@ const mapStateToProps = (state, ownProps) => { isMultiLayerFeeNetwork, chainId, eip1559V2Enabled, + isBuyableChain, }; }; @@ -280,7 +287,7 @@ export const mapDispatchToProps = (dispatch) => { setDefaultHomeActiveTabName: (tabName) => dispatch(setDefaultHomeActiveTabName(tabName)), updateTransactionGasFees: (gasFees) => { - dispatch(updateTransactionGasFees({ ...gasFees, expectHexWei: true })); + dispatch(updateGasFees({ ...gasFees, expectHexWei: true })); }, showBuyModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER' })), }; diff --git a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js index bbb1883ab..c47530242 100644 --- a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js +++ b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js @@ -12,12 +12,11 @@ import Button from '../../../components/ui/button'; import Typography from '../../../components/ui/typography'; import { TYPOGRAPHY } from '../../../helpers/constants/design-system'; import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction'; -import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network'; const TransactionAlerts = ({ userAcknowledgedGasMissing, setUserAcknowledgedGasMissing, - chainId, + isBuyableChain, nativeCurrency, networkName, showBuyModal, @@ -43,7 +42,7 @@ const TransactionAlerts = ({ } useIcon - iconFillColor="#d73a49" + iconFillColor="var(--color-error-default)" type="danger" primaryActionV2={ userAcknowledgedGasMissing === true @@ -92,46 +91,40 @@ const TransactionAlerts = ({ } useIcon - iconFillColor="#f8c000" + iconFillColor="var(--color-warning-default)" type="warning" /> )} - {balanceError && - chainId === MAINNET_CHAIN_ID && - type === TRANSACTION_TYPES.DEPLOY_CONTRACT ? ( + {balanceError && type === TRANSACTION_TYPES.DEPLOY_CONTRACT ? ( - {t('insufficientCurrency', [nativeCurrency, networkName])}{' '} - {' '} - {t('orDeposit')} - + isBuyableChain ? ( + + {t('insufficientCurrencyBuyOrDeposit', [ + nativeCurrency, + networkName, + , + ])} + + ) : ( + + {t('insufficientCurrencyDeposit', [ + nativeCurrency, + networkName, + ])} + + ) } useIcon - iconFillColor="#d73a49" - type="danger" - /> - ) : null} - {balanceError && - chainId !== MAINNET_CHAIN_ID && - type === TRANSACTION_TYPES.DEPLOY_CONTRACT ? ( - - {t('insufficientCurrency', [nativeCurrency, networkName])} - {t('buyOther', [nativeCurrency])} - - } - useIcon - iconFillColor="#d73a49" + iconFillColor="var(--color-error-default)" type="danger" /> ) : null} @@ -149,7 +142,7 @@ const TransactionAlerts = ({ } useIcon - iconFillColor="#f8c000" + iconFillColor="var(--color-warning-default)" type="warning" /> )} @@ -165,7 +158,7 @@ const TransactionAlerts = ({ } - iconFillColor="#f8c000" + iconFillColor="var(--color-warning-default)" type="warning" useIcon /> @@ -177,11 +170,11 @@ const TransactionAlerts = ({ TransactionAlerts.propTypes = { userAcknowledgedGasMissing: PropTypes.bool, setUserAcknowledgedGasMissing: PropTypes.func, - chainId: PropTypes.string, nativeCurrency: PropTypes.string, networkName: PropTypes.string, showBuyModal: PropTypes.func, type: PropTypes.string, + isBuyableChain: PropTypes.bool, }; export default TransactionAlerts; diff --git a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.scss b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.scss index 90bcdc71c..564fdf409 100644 --- a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.scss +++ b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.scss @@ -15,7 +15,7 @@ &__pending-transactions { & a { - color: var(--primary-1); + color: var(--color-primary-default); } } diff --git a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js index 7e9882d65..db58cc640 100644 --- a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js +++ b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.component.js @@ -13,6 +13,7 @@ import { SIGNATURE_REQUEST_PATH, DECRYPT_MESSAGE_REQUEST_PATH, ENCRYPTION_PUBLIC_KEY_REQUEST_PATH, + CONFIRM_SAFE_TRANSFER_FROM_PATH, } from '../../helpers/constants/routes'; import { MESSAGE_TYPE } from '../../../shared/constants/app'; import { TRANSACTION_TYPES } from '../../../shared/constants/transaction'; @@ -50,6 +51,10 @@ export default class ConfirmTransactionSwitch extends Component { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TRANSFER_FROM_PATH}`; return ; } + case TRANSACTION_TYPES.TOKEN_METHOD_SAFE_TRANSFER_FROM: { + const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_SAFE_TRANSFER_FROM_PATH}`; + return ; + } default: { const pathname = `${CONFIRM_TRANSACTION_ROUTE}/${id}${CONFIRM_TOKEN_METHOD_PATH}`; return ; diff --git a/ui/pages/confirm-transaction/confirm-token-transaction-switch.js b/ui/pages/confirm-transaction/confirm-token-transaction-switch.js index ea06c0d0b..036f2b149 100644 --- a/ui/pages/confirm-transaction/confirm-token-transaction-switch.js +++ b/ui/pages/confirm-transaction/confirm-token-transaction-switch.js @@ -4,6 +4,7 @@ import { useSelector } from 'react-redux'; import { Switch, Route } from 'react-router-dom'; import { CONFIRM_APPROVE_PATH, + CONFIRM_SAFE_TRANSFER_FROM_PATH, CONFIRM_SEND_TOKEN_PATH, CONFIRM_TRANSACTION_ROUTE, CONFIRM_TRANSFER_FROM_PATH, @@ -88,6 +89,29 @@ export default function ConfirmTokenTransactionSwitch({ transaction }) { /> )} /> + ( + + )} + /> ) : null} diff --git a/ui/pages/confirmation/confirmation.scss b/ui/pages/confirmation/confirmation.scss index 482f1c210..6c930ec4c 100644 --- a/ui/pages/confirmation/confirmation.scss +++ b/ui/pages/confirmation/confirmation.scss @@ -5,7 +5,7 @@ width: 100%; height: 100%; position: relative; - background: white; + background: var(--color-background-default); display: grid; flex-direction: column; grid-template-columns: 1fr; @@ -16,7 +16,7 @@ 'footer'; a { - color: var(--primary-1); + color: var(--color-primary-default); } &__content { @@ -32,19 +32,19 @@ @include H7; grid-area: navigation; - background-color: var(--Grey-000); - border-bottom: 1px solid var(--geyser); + background-color: var(--color-background-alternative); + border-bottom: 1px solid var(--color-border-muted); padding: 6px 16px 5px 16px; - color: var(--Grey-500); + color: var(--color-text-alternative); display: grid; grid-template-columns: 1fr minmax(0, auto) minmax(0, auto); align-items: center; } &__navigation-button { - background-color: white; + background-color: var(--color-background-default); border-radius: 100px; - color: var(--Grey-500); + color: var(--color-text-alternative); font-size: $font-size-h6; height: 20px; width: 20px; @@ -52,8 +52,8 @@ &:disabled { cursor: not-allowed; - background-color: var(--Grey-100); - color: var(--Grey-300); + background-color: var(--color-background-alternative); + color: var(--color-text-muted); } } diff --git a/ui/pages/confirmation/templates/flask/snap-confirm/index.scss b/ui/pages/confirmation/templates/flask/snap-confirm/index.scss index 7fed11cd3..fdac1698a 100644 --- a/ui/pages/confirmation/templates/flask/snap-confirm/index.scss +++ b/ui/pages/confirmation/templates/flask/snap-confirm/index.scss @@ -1,7 +1,7 @@ .snap-confirm { padding: 16px 32px; - border-top: 1px solid var(--Grey-100); - border-bottom: 1px solid var(--Grey-100); + border-top: 1px solid var(--color-border-muted); + border-bottom: 1px solid var(--color-border-muted); margin: 24px 0 16px 0; .text { diff --git a/ui/pages/connected-accounts/index.scss b/ui/pages/connected-accounts/index.scss index 764ac4d97..3d1d11308 100644 --- a/ui/pages/connected-accounts/index.scss +++ b/ui/pages/connected-accounts/index.scss @@ -4,7 +4,7 @@ a:hover { @include H6; - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; } } diff --git a/ui/pages/connected-sites/index.scss b/ui/pages/connected-sites/index.scss index cb5cbf4d2..1c39ea83a 100644 --- a/ui/pages/connected-sites/index.scss +++ b/ui/pages/connected-sites/index.scss @@ -28,7 +28,7 @@ a:hover { @include H6; - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; } } diff --git a/ui/pages/create-account/connect-hardware/account-list.js b/ui/pages/create-account/connect-hardware/account-list.js index dcfda41fe..183c723c9 100644 --- a/ui/pages/create-account/connect-hardware/account-list.js +++ b/ui/pages/create-account/connect-hardware/account-list.js @@ -152,7 +152,10 @@ class AccountList extends Component { rel="noopener noreferrer" title={this.context.t('etherscanView')} > - +
); diff --git a/ui/pages/create-account/connect-hardware/account-list.stories.js b/ui/pages/create-account/connect-hardware/account-list.stories.js index 28b2639de..49a9695b0 100644 --- a/ui/pages/create-account/connect-hardware/account-list.stories.js +++ b/ui/pages/create-account/connect-hardware/account-list.stories.js @@ -1,71 +1,92 @@ -import React, { useState } from 'react'; -import { Provider } from 'react-redux'; -import { action } from '@storybook/addon-actions'; -import configureStore from '../../../store/store'; -import testData from '../../../../.storybook/test-data'; - +import React from 'react'; import AccountList from './account-list'; -const store = configureStore(testData); - export default { title: 'Pages/CreateAccount/ConnectHardware/AccountList', id: __filename, - decorators: [(story) => {story()}], -}; - -global.platform = { - openTab: () => action('Open Tab')(), -}; - -export const DefaultStory = () => { - const [selectedAccounts, setSelectedAccounts] = useState([ - { - name: 'This is a Really Long Account Name', - address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', - index: 0, - balance: '0x176e5b6f173ebe66', + argTypes: { + onPathChange: { + action: 'onPathChange', }, - ]); - const { metamask } = store.getState(); - const { accountArray, connectedAccounts } = metamask; - const LEDGER_LIVE_PATH = `m/44'/60'/0'/0/0`; - const MEW_PATH = `m/44'/60'/0'`; - const BIP44_PATH = `m/44'/60'/0'/0`; - - const HD_PATHS = [ - { name: 'Ledger Live', value: LEDGER_LIVE_PATH }, - { name: 'Legacy (MEW / MyCrypto)', value: MEW_PATH }, - { name: `BIP44 Standard (e.g. MetaMask, Trezor)`, value: BIP44_PATH }, - ]; - - const onAccountChange = (account) => { - let accounts = []; - if (selectedAccounts.includes(account)) { - accounts = selectedAccounts.filter((acc) => account !== acc); - } else { - accounts.push(account); - } - setSelectedAccounts(accounts); - }; - - return ( - undefined} - selectedPath="/" - device="null" - accounts={accountArray} - connectedAccounts={connectedAccounts} - onAccountChange={onAccountChange} - onForgetDevice={() => action('On Forget Device')()} - getPage={() => action('Get Page')()} - selectedAccounts={selectedAccounts} - hdPaths={HD_PATHS} - onCancel={() => action('On Cancel')()} - onUnlockAccounts={() => action('On Unlock Accounts')()} - onAccountRestriction={() => action('On Account Restriction')()} - /> - ); + selectedPath: 'selectedPath', + device: 'device', + accounts: { + control: 'array', + }, + connectedAccounts: { + control: 'array', + }, + onAccountChange: { + action: 'onAccountChange', + }, + onForgetDevice: { + action: 'onForgetDevice', + }, + getPage: { + action: 'getPage', + }, + chainId: { + control: 'text', + }, + rpcPrefs: { + control: 'object', + }, + selectedAccounts: { + control: 'array', + }, + onUnlockAccounts: { + action: 'onUnlockAccounts', + }, + onCancel: { + action: 'onCancel', + }, + onAccountRestriction: { + action: 'onAccountRestriction', + }, + hdPaths: { + control: 'array', + }, + }, + args: { + selectedPath: 'selectedPath', + device: 'device', + accounts: [ + { + name: 'This is a Really Long Account Name', + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + index: 0, + balance: '0x176e5b6f173ebe66', + }, + ], + connectedAccounts: [ + { + name: 'This is a Really Long Account Name', + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + index: 0, + balance: '0x176e5b6f173ebe66', + }, + ], + chainId: 'chainId', + rpcPrefs: {}, + selectedAccounts: [ + { + name: 'This is a Really Long Account Name', + address: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + index: 0, + balance: '0x176e5b6f173ebe66', + }, + ], + hdPaths: [ + { name: 'Ledger Live', value: `m/44'/60'/0'/0/0` }, + { name: 'Legacy (MEW / MyCrypto)', value: `m/44'/60'/0'` }, + { + name: `BIP44 Standard (e.g. MetaMask, Trezor)`, + value: `m/44'/60'/0'/0`, + }, + ], + }, }; +export const DefaultStory = (args) => ; + DefaultStory.storyName = 'Default'; diff --git a/ui/pages/create-account/connect-hardware/index.js b/ui/pages/create-account/connect-hardware/index.js index 0e784c7c5..decf331ac 100644 --- a/ui/pages/create-account/connect-hardware/index.js +++ b/ui/pages/create-account/connect-hardware/index.js @@ -253,23 +253,23 @@ class ConnectHardwareForm extends Component { description, ) .then((_) => { - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: `Connected Account with: ${device}`, + properties: { action: 'Connected Hardware Wallet', - name: `Connected Account with: ${device}`, + legacy_event: true, }, }); history.push(mostRecentOverviewPage); }) .catch((e) => { - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: 'Error connecting hardware wallet', + properties: { action: 'Connected Hardware Wallet', - name: 'Error connecting hardware wallet', - }, - customVariables: { + legacy_event: true, error: e.message, }, }); @@ -414,7 +414,7 @@ const mapDispatchToProps = (dispatch) => { ConnectHardwareForm.contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; export default connect( diff --git a/ui/pages/create-account/connect-hardware/index.scss b/ui/pages/create-account/connect-hardware/index.scss index de0d1dd38..86517efe4 100644 --- a/ui/pages/create-account/connect-hardware/index.scss +++ b/ui/pages/create-account/connect-hardware/index.scss @@ -19,7 +19,7 @@ &__msg { @include H6; - color: #9b9b9b; + color: var(--color-text-muted); margin-top: 10px; margin-bottom: 20px; } @@ -42,8 +42,8 @@ } &__btn { - background: #fbfbfb; - border: 1px solid #e5e5e5; + background: var(--color-background-alternative); + border: 1px solid var(--color-border-muted); height: 100px; width: 150px; flex: 1; @@ -60,7 +60,7 @@ } &__btn.selected { - border-color: #00a8ee; + border-color: var(--color-primary-default); width: 149px; } @@ -110,7 +110,7 @@ &__msg { @include H6; - color: #9b9b9b; + color: var(--color-text-muted); margin-top: 10px; margin-bottom: 15px; @@ -118,13 +118,13 @@ @include H6; text-align: center; - color: var(--primary-1); + color: var(--color-primary-default); background: unset; } } &__error { - color: #f7861c; + color: var(--color-secondary-default); margin: 20px 20px 10px; display: block; text-align: center; @@ -153,7 +153,7 @@ margin-bottom: 23px; align-self: flex-start; - color: var(--scorpion); + color: var(--color-text-muted); font-weight: bold; display: flex; flex: 1; @@ -164,7 +164,7 @@ margin-bottom: 23px; align-self: flex-end; - color: var(--scorpion); + color: var(--color-text-muted); display: flex; } @@ -175,18 +175,12 @@ display: flex; padding-left: 10px; padding-right: 10px; - } - - &__item:nth-of-type(even) { - background-color: #fbfbfb; - } - - &__item:nth-of-type(odd) { - background: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid var(--color-border-muted); + background-color: var(--color-background-default); } &__item:hover { - background-color: rgba(0, 0, 0, 0.06); + background-color: var(--color-background-alternative); } &__item__index { @@ -236,10 +230,10 @@ &__button { @include H6; - background: #fff; + background: var(--color-background-default); height: 19px; display: flex; - color: #33a4e7; + color: var(--color-primary-default); border: none; min-width: 46px; margin-right: 0; @@ -292,7 +286,7 @@ a { @include H6; - color: #2f9ae0; + color: var(--color-primary-default); cursor: pointer; } } diff --git a/ui/pages/create-account/connect-hardware/select-hardware.js b/ui/pages/create-account/connect-hardware/select-hardware.js index be4d1326b..fc058ae45 100644 --- a/ui/pages/create-account/connect-hardware/select-hardware.js +++ b/ui/pages/create-account/connect-hardware/select-hardware.js @@ -2,6 +2,11 @@ import classnames from 'classnames'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import Button from '../../../components/ui/button'; +import LogoLedger from '../../../components/ui/logo/logo-ledger'; +import LogoQRBased from '../../../components/ui/logo/logo-qr-based'; +import LogoTrezor from '../../../components/ui/logo/logo-trezor'; +import LogoLattice from '../../../components/ui/logo/logo-lattice'; + import { DEVICE_NAMES, LEDGER_TRANSPORT_TYPES, @@ -37,11 +42,7 @@ export default class SelectHardware extends Component { })} onClick={(_) => this.setState({ selectedDevice: DEVICE_NAMES.TREZOR })} > - Trezor + ); } @@ -54,11 +55,7 @@ export default class SelectHardware extends Component { })} onClick={(_) => this.setState({ selectedDevice: DEVICE_NAMES.LATTICE })} > - + ); } @@ -71,11 +68,7 @@ export default class SelectHardware extends Component { })} onClick={(_) => this.setState({ selectedDevice: DEVICE_NAMES.LEDGER })} > - Ledger + ); } @@ -88,11 +81,7 @@ export default class SelectHardware extends Component { })} onClick={(_) => this.setState({ selectedDevice: DEVICE_NAMES.QR })} > - QRCode + ); } diff --git a/ui/pages/create-account/import-account/index.scss b/ui/pages/create-account/import-account/index.scss index 9015f1f35..d40af3bfb 100644 --- a/ui/pages/create-account/import-account/index.scss +++ b/ui/pages/create-account/import-account/index.scss @@ -1,7 +1,7 @@ .new-account-info-link { cursor: pointer; text-decoration: underline; - color: var(--primary-blue); + color: var(--color-primary-default); margin-inline-start: 4px; } @@ -26,7 +26,7 @@ &__select-label { @include Paragraph; - color: var(--scorpion); + color: var(--color-text-muted); } &__select { @@ -45,7 +45,7 @@ &__instruction { @include Paragraph; - color: var(--scorpion); + color: var(--color-text-muted); align-self: flex-start; } @@ -57,7 +57,7 @@ } &__help-link { - color: var(--primary-blue); + color: var(--color-primary-default); } &__input-password { diff --git a/ui/pages/create-account/import-account/json.js b/ui/pages/create-account/import-account/json.js index fbb237c4a..217e682ae 100644 --- a/ui/pages/create-account/import-account/json.js +++ b/ui/pages/create-account/import-account/json.js @@ -116,21 +116,23 @@ class JsonImportSubview extends Component { .then(({ selectedAddress }) => { if (selectedAddress) { history.push(mostRecentOverviewPage); - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: 'Imported Account with JSON', + properties: { action: 'Import Account', - name: 'Imported Account with JSON', + legacy_event: true, }, }); displayWarning(null); } else { displayWarning(t('importAccountError')); - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: 'Error importing JSON', + properties: { action: 'Import Account', - name: 'Error importing JSON', + legacy_event: true, }, }); setSelectedAddress(firstAddress); @@ -179,7 +181,7 @@ const mapDispatchToProps = (dispatch) => { JsonImportSubview.contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; export default compose( diff --git a/ui/pages/create-account/import-account/private-key.js b/ui/pages/create-account/import-account/private-key.js index 08495cf23..99d0e3b48 100644 --- a/ui/pages/create-account/import-account/private-key.js +++ b/ui/pages/create-account/import-account/private-key.js @@ -11,7 +11,7 @@ import { getMostRecentOverviewPage } from '../../../ducks/history/history'; class PrivateKeyImportView extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -43,22 +43,24 @@ class PrivateKeyImportView extends Component { importNewAccount('Private Key', [privateKey]) .then(({ selectedAddress }) => { if (selectedAddress) { - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: 'Imported Account with Private Key', + properties: { action: 'Import Account', - name: 'Imported Account with Private Key', + legacy_event: true, }, }); history.push(mostRecentOverviewPage); displayWarning(null); } else { displayWarning(t('importAccountError')); - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: 'Error importing with Private Key', + properties: { action: 'Import Account', - name: 'Error importing with Private Key', + legacy_event: true, }, }); setSelectedAddress(firstAddress); @@ -88,7 +90,7 @@ class PrivateKeyImportView extends Component { return (
- + {this.context.t('pastePrivateKey')}
diff --git a/ui/pages/create-account/index.scss b/ui/pages/create-account/index.scss index 0f01587a9..962160c43 100644 --- a/ui/pages/create-account/index.scss +++ b/ui/pages/create-account/index.scss @@ -3,7 +3,7 @@ .new-account { width: 375px; - background-color: #fff; + background-color: var(--color-background-default); box-shadow: 0 0 7px 0 rgba(0, 0, 0, 0.08); z-index: 25; height: unset; @@ -21,13 +21,13 @@ &__header { display: flex; flex-flow: column; - border-bottom: 1px solid var(--geyser); + border-bottom: 1px solid var(--color-border-muted); } &__title { @include H2; - color: var(--tundora); + color: var(--color-text-alternative); font-weight: 500; margin-top: 22px; margin-left: 29px; @@ -43,19 +43,19 @@ height: 54px; padding: 15px 10px; - color: var(--dusty-gray); + color: var(--color-text-muted); text-align: center; cursor: pointer; } &__tab:hover { - color: var(--black); + color: var(--color-text-default); border-bottom: none; } &__selected { - color: var(--primary-blue); - border-bottom: 3px solid var(--primary-blue); + color: var(--color-primary-default); + border-bottom: 3px solid var(--color-primary-default); cursor: initial; pointer-events: none; } @@ -72,7 +72,7 @@ &__input-label { @include Paragraph; - color: var(--scorpion); + color: var(--color-text-alternative); align-self: flex-start; } @@ -81,15 +81,15 @@ height: 54px; width: 315.84px; - border: 1px solid var(--geyser); + border: 1px solid var(--color-border-muted); border-radius: 4px; - background-color: var(--white); - color: var(--scorpion); + background-color: var(--color-background-default); + color: var(--color-text-muted); margin-top: 15px; padding: 0 20px; &__error { - border: 1px solid var(--error-3); + border: 1px solid var(--color-error-alternative); } } @@ -97,7 +97,7 @@ @include H7; left: 8px; - color: var(--red); + color: var(--color-error-default); } &__error-amount { diff --git a/ui/pages/create-account/new-account.component.js b/ui/pages/create-account/new-account.component.js index 3908bf40b..336d577eb 100644 --- a/ui/pages/create-account/new-account.component.js +++ b/ui/pages/create-account/new-account.component.js @@ -27,23 +27,23 @@ export default class NewAccountCreateForm extends Component { const createClick = (_) => { createAccount(newAccountName || defaultAccountName) .then(() => { - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: 'Added New Account', + properties: { action: 'Add New Account', - name: 'Added New Account', + legacy_event: true, }, }); history.push(mostRecentOverviewPage); }) .catch((e) => { - this.context.metricsEvent({ - eventOpts: { - category: 'Accounts', + this.context.trackEvent({ + category: 'Accounts', + event: 'Error', + properties: { action: 'Add New Account', - name: 'Error', - }, - customVariables: { + legacy_event: true, errorMessage: e.message, }, }); @@ -120,5 +120,5 @@ NewAccountCreateForm.propTypes = { NewAccountCreateForm.contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; diff --git a/ui/pages/error/index.scss b/ui/pages/error/index.scss index 517c43e1e..9db7c3d92 100644 --- a/ui/pages/error/index.scss +++ b/ui/pages/error/index.scss @@ -36,10 +36,10 @@ &__stack { overflow-x: auto; - background-color: #eee; + background-color: var(--color-background-alternative); } &__link-text { - color: #037dd6; + color: var(--color-primary-default); } } diff --git a/ui/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js b/ui/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js index 42246775c..2d611159c 100644 --- a/ui/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js +++ b/ui/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.js @@ -9,7 +9,7 @@ import CreateNewVault from '../../../../components/app/create-new-vault'; export default class ImportWithSeedPhrase extends PureComponent { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -21,13 +21,12 @@ export default class ImportWithSeedPhrase extends PureComponent { UNSAFE_componentWillMount() { this._onBeforeUnload = () => - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Close window on import screen', + properties: { action: 'Import Seed Phrase', - name: 'Close window on import screen', - }, - customVariables: { + legacy_event: true, errorLabel: 'Seed Phrase Error', errorMessage: this.state.seedPhraseError, }, @@ -48,11 +47,12 @@ export default class ImportWithSeedPhrase extends PureComponent { } = this.props; await onSubmit(password, seedPhrase); - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Import Complete', + properties: { action: 'Import Seed Phrase', - name: 'Import Complete', + legacy_event: true, }, }); @@ -70,11 +70,12 @@ export default class ImportWithSeedPhrase extends PureComponent { { e.preventDefault(); - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Go Back from Onboarding Import', + properties: { action: 'Import Seed Phrase', - name: 'Go Back from Onboarding Import', + legacy_event: true, }, }); this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE); diff --git a/ui/pages/first-time-flow/create-password/new-account/new-account.component.js b/ui/pages/first-time-flow/create-password/new-account/new-account.component.js index a392bc295..cc904a92e 100644 --- a/ui/pages/first-time-flow/create-password/new-account/new-account.component.js +++ b/ui/pages/first-time-flow/create-password/new-account/new-account.component.js @@ -9,7 +9,7 @@ import TextField from '../../../../components/ui/text-field'; export default class NewAccount extends PureComponent { static contextTypes = { - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, t: PropTypes.func, }; @@ -100,11 +100,12 @@ export default class NewAccount extends PureComponent { try { await onSubmit(password); - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Submit Password', + properties: { action: 'Create Password', - name: 'Submit Password', + legacy_event: true, }, }); @@ -115,11 +116,12 @@ export default class NewAccount extends PureComponent { }; toggleTermsCheck = () => { - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Check ToS', + properties: { action: 'Create Password', - name: 'Check ToS', + legacy_event: true, }, }); @@ -150,11 +152,12 @@ export default class NewAccount extends PureComponent { { e.preventDefault(); - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Go Back from Onboarding Create', + properties: { action: 'Create Password', - name: 'Go Back from Onboarding Create', + legacy_event: true, }, }); this.props.history.push(INITIALIZE_SELECT_ACTION_ROUTE); diff --git a/ui/pages/first-time-flow/end-of-flow/end-of-flow.component.js b/ui/pages/first-time-flow/end-of-flow/end-of-flow.component.js index e35e5af37..25beb844d 100644 --- a/ui/pages/first-time-flow/end-of-flow/end-of-flow.component.js +++ b/ui/pages/first-time-flow/end-of-flow/end-of-flow.component.js @@ -10,7 +10,7 @@ import { returnToOnboardingInitiator } from '../onboarding-initiator-util'; export default class EndOfFlowScreen extends PureComponent { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -34,11 +34,12 @@ export default class EndOfFlowScreen extends PureComponent { async _onOnboardingComplete() { const { setCompletedOnboarding, completionMetaMetricsName } = this.props; await setCompletedOnboarding(); - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: completionMetaMetricsName, + properties: { action: 'Onboarding Complete', - name: completionMetaMetricsName, + legacy_event: true, }, }); } diff --git a/ui/pages/first-time-flow/end-of-flow/index.scss b/ui/pages/first-time-flow/end-of-flow/index.scss index d8788048b..94aa971ef 100644 --- a/ui/pages/first-time-flow/end-of-flow/index.scss +++ b/ui/pages/first-time-flow/end-of-flow/index.scss @@ -1,5 +1,5 @@ .end-of-flow { - color: black; + color: var(--color-text-default); font-style: normal; .app-header__logo-container { diff --git a/ui/pages/first-time-flow/index.scss b/ui/pages/first-time-flow/index.scss index 93c52fbde..4ed5ae444 100644 --- a/ui/pages/first-time-flow/index.scss +++ b/ui/pages/first-time-flow/index.scss @@ -7,7 +7,7 @@ .first-time-flow { width: 100%; - background-color: var(--white); + background-color: var(--color-background-default); display: flex; justify-content: center; @@ -43,7 +43,7 @@ @include H1; margin-bottom: 24px; - color: black; + color: var(--color-text-default); } &__input { @@ -52,7 +52,7 @@ &__text-block { margin-bottom: 24px; - color: black; + color: var(--color-text-default); @media screen and (max-width: $break-small) { @include H6; @@ -74,8 +74,8 @@ } &__checkbox { - background: #fff; - border: 1px solid #cdcdcd; + background: var(--color-background-default); + border: 1px solid var(--color-border-muted); box-sizing: border-box; height: 34px; width: 34px; @@ -84,23 +84,23 @@ align-items: center; &:hover { - border: 1.5px solid #2f9ae0; + border: 1.5px solid var(--color-primary-alternative); } .fa-check { - color: #2f9ae0; + color: var(--color-primary-default); } } &__checkbox-label { @include H4; - color: #939090; + color: var(--color-text-alternative); margin-left: 18px; } &__link-text { - color: var(--primary-blue); + color: var(--color-primary-default); } } diff --git a/ui/pages/first-time-flow/metametrics-opt-in/index.scss b/ui/pages/first-time-flow/metametrics-opt-in/index.scss index 69695129b..de905adae 100644 --- a/ui/pages/first-time-flow/metametrics-opt-in/index.scss +++ b/ui/pages/first-time-flow/metametrics-opt-in/index.scss @@ -3,7 +3,7 @@ width: 100%; a { - color: #2f9ae0bf; + color: var(--color-primary-default); } &__main { @@ -11,7 +11,7 @@ flex-direction: column; margin-left: 26.26%; margin-right: 28%; - color: black; + color: var(--color-text-default); @media screen and (max-width: $break-small) { justify-content: center; @@ -35,7 +35,7 @@ margin-top: 25px; .fa-bar-chart { - color: #c4c4c4; + color: var(--color-icon-default); } } @@ -61,12 +61,12 @@ .fa-check { margin-right: 12px; - color: #1acc56; + color: var(--color-success-default); } .fa-times { margin-right: 12px; - color: #d0021b; + color: var(--color-error-default); } } @@ -93,7 +93,7 @@ &__bottom-text { margin-top: 10px; - color: #9a9a9a; + color: var(--color-text-alternative); } &__content { diff --git a/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js b/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js index 883f086d5..cee0342ce 100644 --- a/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js +++ b/ui/pages/first-time-flow/metametrics-opt-in/metametrics-opt-in.component.js @@ -13,12 +13,12 @@ export default class MetaMetricsOptIn extends Component { }; static contextTypes = { - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, t: PropTypes.func, }; render() { - const { metricsEvent, t } = this.context; + const { trackEvent, t } = this.context; const { nextRoute, history, @@ -109,15 +109,20 @@ export default class MetaMetricsOptIn extends Component { participateInMetaMetrics === null || participateInMetaMetrics === true ) { - await metricsEvent({ - eventOpts: { + await trackEvent( + { category: 'Onboarding', - action: 'Metrics Option', - name: 'Metrics Opt Out', + event: 'Metrics Opt Out', + properties: { + action: 'Metrics Option', + legacy_event: true, + }, }, - isOptIn: true, - flushImmediately: true, - }); + { + isOptIn: true, + flushImmediately: true, + }, + ); } } finally { history.push(nextRoute); @@ -136,28 +141,38 @@ export default class MetaMetricsOptIn extends Component { participateInMetaMetrics === false ) { metrics.push( - metricsEvent({ - eventOpts: { + trackEvent( + { category: 'Onboarding', - action: 'Metrics Option', - name: 'Metrics Opt In', + event: 'Metrics Opt In', + properties: { + action: 'Metrics Option', + legacy_event: true, + }, }, - isOptIn: true, - flushImmediately: true, - }), + { + isOptIn: true, + flushImmediately: true, + }, + ), ); } metrics.push( - metricsEvent({ - eventOpts: { + trackEvent( + { category: 'Onboarding', - action: 'Import or Create', - name: firstTimeSelectionMetaMetricsName, + event: firstTimeSelectionMetaMetricsName, + properties: { + action: 'Import or Create', + legacy_event: true, + }, }, - isOptIn: true, - metaMetricsId, - flushImmediately: true, - }), + { + isOptIn: true, + metaMetricsId, + flushImmediately: true, + }, + ), ); await Promise.all(metrics); } finally { diff --git a/ui/pages/first-time-flow/onboarding-initiator-util.js b/ui/pages/first-time-flow/onboarding-initiator-util.js index abfa424d7..98dc98f89 100644 --- a/ui/pages/first-time-flow/onboarding-initiator-util.js +++ b/ui/pages/first-time-flow/onboarding-initiator-util.js @@ -1,9 +1,9 @@ -import extension from 'extensionizer'; +import browser from 'webextension-polyfill'; import log from 'loglevel'; const returnToOnboardingInitiatorTab = async (onboardingInitiator) => { const tab = await new Promise((resolve) => { - extension.tabs.update( + browser.tabs.update( onboardingInitiator.tabId, { active: true }, // eslint-disable-next-line no-shadow @@ -12,8 +12,8 @@ const returnToOnboardingInitiatorTab = async (onboardingInitiator) => { resolve(tab); } else { // silence console message about unchecked error - if (extension.runtime.lastError) { - log.debug(extension.runtime.lastError); + if (browser.runtime.lastError) { + log.debug(browser.runtime.lastError); } resolve(); } @@ -24,7 +24,7 @@ const returnToOnboardingInitiatorTab = async (onboardingInitiator) => { if (tab) { window.close(); } else { - // this case can happen if the tab was closed since being checked with `extension.tabs.get` + // this case can happen if the tab was closed since being checked with `browser.tabs.get` log.warn( `Setting current tab to onboarding initiator has failed; falling back to redirect`, ); @@ -35,13 +35,13 @@ const returnToOnboardingInitiatorTab = async (onboardingInitiator) => { export const returnToOnboardingInitiator = async (onboardingInitiator) => { const tab = await new Promise((resolve) => { // eslint-disable-next-line no-shadow - extension.tabs.get(onboardingInitiator.tabId, (tab) => { + browser.tabs.get(onboardingInitiator.tabId, (tab) => { if (tab) { resolve(tab); } else { // silence console message about unchecked error - if (extension.runtime.lastError) { - log.debug(extension.runtime.lastError); + if (browser.runtime.lastError) { + log.debug(browser.runtime.lastError); } resolve(); } diff --git a/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase-component.test.js b/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase-component.test.js index 852fce269..b47bb9532 100644 --- a/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase-component.test.js +++ b/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase-component.test.js @@ -24,7 +24,7 @@ describe('ConfirmSeedPhrase Component', () => { }); it('should add/remove selected on click', () => { - const metricsEventSpy = sinon.spy(); + const trackEventSpy = sinon.spy(); const replaceSpy = sinon.spy(); const component = shallowRender( { @@ -32,7 +32,7 @@ describe('ConfirmSeedPhrase Component', () => { history: { replace: replaceSpy }, }, { - metricsEvent: metricsEventSpy, + trackEvent: trackEventSpy, }, ); @@ -57,7 +57,7 @@ describe('ConfirmSeedPhrase Component', () => { }); it('should render correctly on hover', () => { - const metricsEventSpy = sinon.spy(); + const trackEventSpy = sinon.spy(); const replaceSpy = sinon.spy(); const component = shallowRender( { @@ -65,7 +65,7 @@ describe('ConfirmSeedPhrase Component', () => { history: { replace: replaceSpy }, }, { - metricsEvent: metricsEventSpy, + trackEvent: trackEventSpy, }, ); @@ -92,7 +92,7 @@ describe('ConfirmSeedPhrase Component', () => { }); it('should insert seed in place on drop', () => { - const metricsEventSpy = sinon.spy(); + const trackEventSpy = sinon.spy(); const replaceSpy = sinon.spy(); const component = shallowRender( { @@ -100,7 +100,7 @@ describe('ConfirmSeedPhrase Component', () => { history: { replace: replaceSpy }, }, { - metricsEvent: metricsEventSpy, + trackEvent: trackEventSpy, }, ); @@ -137,7 +137,7 @@ describe('ConfirmSeedPhrase Component', () => { '狗', '豬', ]; - const metricsEventSpy = sinon.spy(); + const trackEventSpy = sinon.spy(); const replaceSpy = sinon.spy(); const initialize3BoxSpy = sinon.spy(); const component = shallowRender( @@ -148,7 +148,7 @@ describe('ConfirmSeedPhrase Component', () => { initializeThreeBox: initialize3BoxSpy, }, { - metricsEvent: metricsEventSpy, + trackEvent: trackEventSpy, }, ); @@ -166,11 +166,12 @@ describe('ConfirmSeedPhrase Component', () => { await new Promise((resolve) => setTimeout(resolve, 100)); - expect(metricsEventSpy.args[0][0]).toStrictEqual({ - eventOpts: { - category: 'Onboarding', + expect(trackEventSpy.args[0][0]).toStrictEqual({ + category: 'Onboarding', + event: 'Verify Complete', + properties: { action: 'Seed Phrase Setup', - name: 'Verify Complete', + legacy_event: true, }, }); expect(initialize3BoxSpy.calledOnce).toStrictEqual(true); diff --git a/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js b/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js index 72f1d0b45..2b569eb38 100644 --- a/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js +++ b/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.component.js @@ -13,7 +13,7 @@ const EMPTY_SEEDS = Array(12).fill(null); export default class ConfirmSeedPhrase extends PureComponent { static contextTypes = { - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, t: PropTypes.func, }; @@ -77,11 +77,12 @@ export default class ConfirmSeedPhrase extends PureComponent { } try { - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Verify Complete', + properties: { action: 'Seed Phrase Setup', - name: 'Verify Complete', + legacy_event: true, }, }); diff --git a/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss b/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss index 96e1b7ee7..32922fa11 100644 --- a/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss +++ b/ui/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss @@ -26,7 +26,7 @@ } &--selected { - color: var(--white); + color: var(--color-overlay-inverse); } &--dragging { @@ -71,9 +71,9 @@ flex-flow: row wrap; min-height: 161px; max-width: $break-small; - border: 1px solid #cdcdcd; + border: 1px solid var(--color-border-muted); border-radius: 6px; - background-color: var(--white); + background-color: var(--color-background-default); margin: 24px 0 36px; padding: 12px; diff --git a/ui/pages/first-time-flow/seed-phrase/index.scss b/ui/pages/first-time-flow/seed-phrase/index.scss index 15f801632..4bb2b8255 100644 --- a/ui/pages/first-time-flow/seed-phrase/index.scss +++ b/ui/pages/first-time-flow/seed-phrase/index.scss @@ -33,7 +33,7 @@ } .first-time-flow__text-block { - color: #5a5a5a; + color: var(--color-text-default); } } } diff --git a/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss index 35b6f9163..f000930cc 100644 --- a/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss +++ b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss @@ -11,9 +11,9 @@ position: relative; display: flex; justify-content: center; - border: 1px solid #cdcdcd; + border: 1px solid var(--color-border-default); border-radius: 6px; - background-color: var(--white); + background-color: var(--color-background-default); padding: 18px; margin-top: 36px; max-width: 350px; @@ -36,7 +36,7 @@ bottom: 0; height: 100%; width: 100%; - background-color: rgba(0, 0, 0, 0.6); + background-color: var(--color-overlay-default); display: flex; flex-flow: column nowrap; align-items: center; @@ -48,7 +48,7 @@ &__reveal-button { @include H7; - color: var(--white); + color: var(--color-overlay-inverse); font-weight: 500; text-transform: uppercase; margin-top: 8px; @@ -56,7 +56,7 @@ } &__export-text { - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; font-weight: 500; } diff --git a/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js index 1d9a47b8d..1745a2527 100644 --- a/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js +++ b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.component.js @@ -16,7 +16,7 @@ import { returnToOnboardingInitiator } from '../../onboarding-initiator-util'; export default class RevealSeedPhrase extends PureComponent { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -42,11 +42,12 @@ export default class RevealSeedPhrase extends PureComponent { const { isShowingSeedPhrase } = this.state; const { history } = this.props; - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Advance to Verify', + properties: { action: 'Seed Phrase Setup', - name: 'Advance to Verify', + legacy_event: true, }, }); @@ -65,11 +66,12 @@ export default class RevealSeedPhrase extends PureComponent { onboardingInitiator, } = this.props; - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Remind me later', + properties: { action: 'Seed Phrase Setup', - name: 'Remind me later', + legacy_event: true, }, }); @@ -102,17 +104,22 @@ export default class RevealSeedPhrase extends PureComponent {
{ - this.context.metricsEvent({ - eventOpts: { - category: 'Onboarding', + this.context.trackEvent({ + category: 'Onboarding', + event: 'Revealed Words', + properties: { action: 'Seed Phrase Setup', - name: 'Revealed Words', + legacy_event: true, }, }); this.setState({ isShowingSeedPhrase: true }); }} > - +
{t('clickToRevealSeed')}
diff --git a/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.stories.js b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.stories.js new file mode 100644 index 000000000..32c7f4136 --- /dev/null +++ b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.stories.js @@ -0,0 +1,11 @@ +import React from 'react'; +import RevealSeedPhrase from '.'; + +export default { + title: 'Components/Pages/FirstTimeFlow/RevealSeedPhrase', + id: __filename, +}; + +export const DefaultStory = () => ; + +DefaultStory.storyName = 'Default'; diff --git a/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.test.js b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.test.js index 262b64f46..7bc9f4355 100644 --- a/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.test.js +++ b/ui/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.test.js @@ -22,7 +22,7 @@ describe('Reveal Secret Recovery Phrase', () => { wrapper = mount(, { context: { t: (str) => str, - metricsEvent: () => undefined, + trackEvent: () => undefined, }, }); }); diff --git a/ui/pages/first-time-flow/select-action/index.scss b/ui/pages/first-time-flow/select-action/index.scss index 8c8fe939a..6f6a5efb4 100644 --- a/ui/pages/first-time-flow/select-action/index.scss +++ b/ui/pages/first-time-flow/select-action/index.scss @@ -15,7 +15,7 @@ text-align: center; margin-top: 65px; - color: black; + color: var(--color-text-default); } &__select-buttons { @@ -32,7 +32,7 @@ justify-content: space-evenly; width: 388px; height: 278px; - border: 1px solid #d8d8d8; + border: 1px solid var(--color-border-muted); box-sizing: border-box; border-radius: 10px; padding: 8px; @@ -44,7 +44,7 @@ } &__button-symbol { - color: #c4c4c4; + color: var(--color-icon-muted); margin-top: 41px; } @@ -59,7 +59,7 @@ &__button-text-big { @include H4; - color: #000; + color: var(--color-text-default); margin-top: 12px; text-align: center; } @@ -67,7 +67,7 @@ &__button-text-small { @include H6; - color: #7a7a7b; + color: var(--color-text-alternative); margin-top: 10px; text-align: center; } diff --git a/ui/pages/first-time-flow/select-action/select-action.component.js b/ui/pages/first-time-flow/select-action/select-action.component.js index 7420b1333..7feadb9a3 100644 --- a/ui/pages/first-time-flow/select-action/select-action.component.js +++ b/ui/pages/first-time-flow/select-action/select-action.component.js @@ -50,7 +50,7 @@ export default class SelectAction extends PureComponent {
- +
{t('noAlreadyHaveSeed')} @@ -70,7 +70,7 @@ export default class SelectAction extends PureComponent {
- +
{t('letsGoSetUp')} diff --git a/ui/pages/first-time-flow/welcome/index.scss b/ui/pages/first-time-flow/welcome/index.scss index 185572491..69f0f0a9c 100644 --- a/ui/pages/first-time-flow/welcome/index.scss +++ b/ui/pages/first-time-flow/welcome/index.scss @@ -5,7 +5,7 @@ align-items: center; max-width: 442px; padding: 0 18px; - color: black; + color: var(--color-text-default); &__wrapper { display: flex; @@ -35,7 +35,7 @@ } a { - color: var(--primary-1); + color: var(--color-primary-default); } @media screen and (max-width: $break-small) { diff --git a/ui/pages/home/home.component.js b/ui/pages/home/home.component.js index 730fd7de9..346f7520f 100644 --- a/ui/pages/home/home.component.js +++ b/ui/pages/home/home.component.js @@ -98,6 +98,8 @@ export default class Home extends PureComponent { setConnectedStatusPopoverHasBeenShown: PropTypes.func, connectedStatusPopoverHasBeenShown: PropTypes.bool, defaultHomeActiveTabName: PropTypes.string, + firstTimeFlowType: PropTypes.string, + completedOnboarding: PropTypes.bool, onTabClick: PropTypes.func.isRequired, haveSwapsQuotes: PropTypes.bool.isRequired, showAwaitingSwapScreen: PropTypes.bool.isRequired, @@ -309,10 +311,7 @@ export default class Home extends PureComponent { className="home__new-network-notification" message={
- + - + diff --git a/ui/pages/home/home.container.js b/ui/pages/home/home.container.js index 01a470b44..103b40ddb 100644 --- a/ui/pages/home/home.container.js +++ b/ui/pages/home/home.container.js @@ -66,6 +66,8 @@ const mapStateToProps = (state) => { defaultHomeActiveTabName, swapsState, dismissSeedBackUpReminder, + firstTimeFlowType, + completedOnboarding, } = metamask; const accountBalance = getCurrentEthBalance(state); const { forgottenPassword, threeBoxLastUpdated } = appState; @@ -112,6 +114,8 @@ const mapStateToProps = (state) => { totalUnapprovedCount, connectedStatusPopoverHasBeenShown, defaultHomeActiveTabName, + firstTimeFlowType, + completedOnboarding, haveSwapsQuotes: Boolean(Object.values(swapsState.quotes || {}).length), swapsFetchParams: swapsState.fetchParams, showAwaitingSwapScreen: swapsState.routeState === 'awaiting', diff --git a/ui/pages/home/index.scss b/ui/pages/home/index.scss index 2625059c6..96d88061d 100644 --- a/ui/pages/home/index.scss +++ b/ui/pages/home/index.scss @@ -6,7 +6,7 @@ &__main-view { flex: 1 1 66.5%; - background: var(--white); + background: var(--color-background-default); min-width: 0; display: flex; flex-direction: column; @@ -30,12 +30,12 @@ @include H6; flex-grow: 1; - color: var(--Grey-500); + color: var(--color-icon-default); font-weight: 500; } &__main-view &__tab--active { - color: var(--Blue-500); + color: var(--color-primary-default); } &__main-view &__tab button { @@ -50,7 +50,7 @@ padding-left: 24px; padding-right: 24px; - color: var(--Grey-800); + color: var(--color-text-default); div { margin-bottom: 20px; @@ -103,7 +103,7 @@ a, a:hover { - color: var(--dodger-blue); + color: var(--color-primary-alternative); cursor: pointer; } } @@ -130,7 +130,7 @@ text-align: center; a { - color: var(--primary-1); + color: var(--color-primary-default); } } @@ -141,14 +141,16 @@ &__new-network-notification-message { display: flex; flex-direction: row; + align-items: center; - &--image { - margin-right: 10px; + &--icon { + margin-right: 8px; + color: var(--color-success-default); } } &__close { - color: var(--ui-black); + color: var(--color-text-default); background: none; margin-left: 20px; } diff --git a/ui/pages/import-token/README.mdx b/ui/pages/import-token/README.mdx new file mode 100644 index 000000000..83daea8ff --- /dev/null +++ b/ui/pages/import-token/README.mdx @@ -0,0 +1,31 @@ +import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; + +import ImportToken from './import-token.component'; + +import testData from '../../../.storybook/test-data'; +import configureStore from '../../store/store'; +const store = configureStore(testData); +const { metamask } = store.getState(); + +export const PersonalAddress = () => {metamask.selectedAddress} + +# ImportToken + +The `ImportToken` component allows a user to import custom tokens in one of two ways: +1. By searching for one +2. By importing one by `Token Contract Address` + + + + + +## Example inputs + +An example input that works, to enable the `Add Custom Token` button is `0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`. + +### Personal address error +To show the personal address detected error, input the address in the `Token Contract Address` field. + +## Component API + + diff --git a/ui/pages/import-token/import-token.component.js b/ui/pages/import-token/import-token.component.js index 77cc89657..647829575 100644 --- a/ui/pages/import-token/import-token.component.js +++ b/ui/pages/import-token/import-token.component.js @@ -36,19 +36,77 @@ class ImportToken extends Component { }; static propTypes = { + /** + * History object of the router. + */ history: PropTypes.object, + + /** + * Set the state of `pendingTokens`, called when adding a token. + */ setPendingTokens: PropTypes.func, + + /** + * The current list of pending tokens to be added. + */ pendingTokens: PropTypes.object, + + /** + * Clear the list of pending tokens. Called when closing the modal. + */ clearPendingTokens: PropTypes.func, + + /** + * The list of already added tokens. + */ tokens: PropTypes.array, + + /** + * The identities/accounts that are currently added to the wallet. + */ identities: PropTypes.object, + + /** + * Boolean flag that shows/hides the search tab. + */ showSearchTab: PropTypes.bool.isRequired, + + /** + * The most recent overview page route, which is 'navigated' to when closing the modal. + */ mostRecentOverviewPage: PropTypes.string.isRequired, + + /** + * The active chainId in use. + */ chainId: PropTypes.string, + + /** + * The rpc preferences to use for the current provider. + */ rpcPrefs: PropTypes.object, + + /** + * The list of tokens available for search. + */ tokenList: PropTypes.object, + + /** + * Boolean flag indicating whether token detection is enabled or not. + * When disabled, shows an information alert in the search tab informing the + * user of the availability of this feature. + */ useTokenDetection: PropTypes.bool, + + /** + * Function called to fetch information about the token standard and + * details, see `actions.js`. + */ getTokenStandardAndDetails: PropTypes.func, + + /** + * The currently selected active address. + */ selectedAddress: PropTypes.string, }; @@ -368,7 +426,7 @@ class ImportToken extends Component { type="warning" withRightButton useIcon - iconFillColor="#f8c000" + iconFillColor="var(--color-warning-default)" /> )} diff --git a/ui/pages/import-token/import-token.stories.js b/ui/pages/import-token/import-token.stories.js index e6b1e089b..1b619804b 100644 --- a/ui/pages/import-token/import-token.stories.js +++ b/ui/pages/import-token/import-token.stories.js @@ -1,15 +1,120 @@ import React from 'react'; -import { boolean } from '@storybook/addon-knobs'; +import { Provider } from 'react-redux'; +import { action } from '@storybook/addon-actions'; +import { DEFAULT_ROUTE } from '../../helpers/constants/routes'; +import configureStore from '../../store/store'; +import testData from '../../../.storybook/test-data'; import ImportToken from './import-token.component'; +import README from './README.mdx'; + +const store = configureStore(testData); +const { metamask } = store.getState(); + +const { + frequentRpcListDetail, + identities, + pendingTokens, + selectedAddress, + tokenList, + tokens, +} = metamask; export default { title: 'Pages/ImportToken', id: __filename, + decorators: [(story) => {story()}], + component: ImportToken, + parameters: { + docs: { + page: README, + }, + }, + argTypes: { + history: { + control: { + type: 'object', + }, + }, + setPendingTokens: { + action: 'setPendingTokens', + }, + pendingTokens: { + control: { + type: 'object', + }, + }, + clearPendingTokens: { + action: 'clearPendingTokens', + }, + tokens: { + control: { + type: 'object', + }, + }, + identities: { + control: { + type: 'object', + }, + }, + showSearchTab: { + control: { + type: 'boolean', + }, + }, + mostRecentOverviewPage: { + control: { + type: 'text', + }, + }, + chainId: { + control: { + type: 'text', + }, + }, + rpcPrefs: { + control: { + type: 'object', + }, + }, + tokenList: { + control: { + type: 'object', + }, + }, + useTokenDetection: { + control: { + type: 'boolean', + }, + }, + getTokenStandardAndDetails: { + action: 'getTokenStandardAndDetails', + }, + selectedAddress: { + control: { + type: 'text', + }, + }, + }, + args: { + history: { + push: action('history.push()'), + }, + pendingTokens, + tokens, + identities, + showSearchTab: true, + mostRecentOverviewPage: DEFAULT_ROUTE, + chainId: frequentRpcListDetail[0].chainId, + rpcPrefs: frequentRpcListDetail[0].rpcPrefs, + tokenList, + useTokenDetection: false, + selectedAddress, + }, }; -export const DefaultStory = () => { - return ; +export const DefaultStory = (args) => { + return ; }; DefaultStory.storyName = 'Default'; diff --git a/ui/pages/import-token/index.scss b/ui/pages/import-token/index.scss index 178334fda..a6e320750 100644 --- a/ui/pages/import-token/index.scss +++ b/ui/pages/import-token/index.scss @@ -41,7 +41,7 @@ &__edit { flex: 1 1 auto; text-align: right; - color: var(--primary-blue); + color: var(--color-primary-default); padding-right: 4px; cursor: pointer; } @@ -51,7 +51,7 @@ @include H7; display: inline; - color: var(--primary-blue); + color: var(--color-primary-default); padding-left: 0; } @@ -61,11 +61,11 @@ } &__collectible-address-error-link { - color: var(--primary-blue); + color: var(--color-primary-default); cursor: pointer; &:hover { - color: var(--Blue-400); + color: var(--color-primary-default); } } } diff --git a/ui/pages/import-token/token-list/index.scss b/ui/pages/import-token/token-list/index.scss index ac1bc4d7b..f1bfd790d 100644 --- a/ui/pages/import-token/token-list/index.scss +++ b/ui/pages/import-token/token-list/index.scss @@ -25,11 +25,11 @@ &:hover, &:focus { - border-color: rgba($malibu-blue, 0.5); + border-color: var(--color-primary-muted); } &--selected { - border-color: var(--malibu-blue) !important; + border-color: var(--color-primary-default) !important; } &--disabled { @@ -45,7 +45,7 @@ background-size: contain; background-position: center; border-radius: 50%; - background-color: var(--white); + background-color: var(--color-background-default); box-shadow: 0 2px 4px 0 rgba($black, 0.24); margin-right: 12px; flex: 0 0 auto; diff --git a/ui/pages/import-token/token-list/token-list-placeholder/index.scss b/ui/pages/import-token/token-list/token-list-placeholder/index.scss index cdd423f36..3024e2023 100644 --- a/ui/pages/import-token/token-list/token-list-placeholder/index.scss +++ b/ui/pages/import-token/token-list/token-list-placeholder/index.scss @@ -10,7 +10,7 @@ } &__text { - color: var(--silver-chalice); + color: var(--color-text-alternative); width: 50%; text-align: center; margin-top: 8px; diff --git a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js b/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js index 75cef89d1..3ec1f8b8e 100644 --- a/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js +++ b/ui/pages/import-token/token-list/token-list-placeholder/token-list-placeholder.component.js @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Button from '../../../../components/ui/button'; +import IconSearch from '../../../../components/ui/icon/icon-token-search'; export default class TokenListPlaceholder extends Component { static contextTypes = { @@ -10,7 +11,7 @@ export default class TokenListPlaceholder extends Component { render() { return (
- +
{this.context.t('addAcquiredTokens')}
diff --git a/ui/pages/import-token/token-search/token-search.component.js b/ui/pages/import-token/token-search/token-search.component.js index 84085b46b..d5f88a826 100644 --- a/ui/pages/import-token/token-search/token-search.component.js +++ b/ui/pages/import-token/token-search/token-search.component.js @@ -59,7 +59,10 @@ export default class TokenSearch extends Component { renderAdornment() { return ( - + ); } diff --git a/ui/pages/keychains/index.scss b/ui/pages/keychains/index.scss index 219604084..1c081b686 100644 --- a/ui/pages/keychains/index.scss +++ b/ui/pages/keychains/index.scss @@ -61,7 +61,7 @@ @include Paragraph; margin-bottom: 18px; - color: var(--black); + color: var(--color-text-default); position: absolute; top: -25px; } @@ -87,7 +87,7 @@ &__error { @include H6; - color: var(--crimson); + color: var(--color-error-default); padding-top: 5px; } } diff --git a/ui/pages/keychains/restore-vault.js b/ui/pages/keychains/restore-vault.js index 52f216818..87d6635e9 100644 --- a/ui/pages/keychains/restore-vault.js +++ b/ui/pages/keychains/restore-vault.js @@ -17,7 +17,7 @@ import { TYPOGRAPHY, COLORS } from '../../helpers/constants/design-system'; class RestoreVaultPage extends Component { static contextTypes = { t: PropTypes.func, - metricsEvent: PropTypes.func, + trackEvent: PropTypes.func, }; static propTypes = { @@ -40,11 +40,12 @@ class RestoreVaultPage extends Component { leaveImportSeedScreenState(); await createNewVaultAndRestore(password, seedPhrase); - this.context.metricsEvent({ - eventOpts: { - category: 'Retention', + this.context.trackEvent({ + category: 'Retention', + event: 'onboardingRestoredVault', + properties: { action: 'userEntersSeedPhrase', - name: 'onboardingRestoredVault', + legacy_event: true, }, }); initializeThreeBox(); diff --git a/ui/pages/onboarding-flow/create-password/create-password.js b/ui/pages/onboarding-flow/create-password/create-password.js index e91e31004..1de0171c7 100644 --- a/ui/pages/onboarding-flow/create-password/create-password.js +++ b/ui/pages/onboarding-flow/create-password/create-password.js @@ -1,9 +1,8 @@ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useContext } from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; import zxcvbn from 'zxcvbn'; import { useSelector } from 'react-redux'; -import { useNewMetricEvent } from '../../../hooks/useMetricEvent'; import { useI18nContext } from '../../../hooks/useI18nContext'; import Button from '../../../components/ui/button'; import Typography from '../../../components/ui/typography'; @@ -30,6 +29,7 @@ import { import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; import { getFirstTimeFlowType } from '../../../selectors'; import { FIRST_TIME_FLOW_TYPES } from '../../../helpers/constants/onboarding'; +import { MetaMetricsContext } from '../../../contexts/metametrics.new'; export default function CreatePassword({ createNewAccount, @@ -47,11 +47,7 @@ export default function CreatePassword({ const [showPassword, setShowPassword] = useState(false); const history = useHistory(); const firstTimeFlowType = useSelector(getFirstTimeFlowType); - - const submitPasswordEvent = useNewMetricEvent({ - event: 'Submit Password', - category: 'Onboarding', - }); + const trackEvent = useContext(MetaMetricsContext); const isValid = useMemo(() => { if (!password || !confirmPassword || password !== confirmPassword) { @@ -142,7 +138,10 @@ export default function CreatePassword({ if (createNewAccount) { await createNewAccount(password); } - submitPasswordEvent(); + trackEvent({ + event: 'Submit Password', + category: 'Onboarding', + }); history.push(ONBOARDING_SECURE_YOUR_WALLET_ROUTE); } catch (error) { setPasswordError(error.message); @@ -216,26 +215,28 @@ export default function CreatePassword({ justifyContent={JUSTIFY_CONTENT.SPACE_BETWEEN} marginBottom={4} > - setTermsChecked(!termsChecked)} - checked={termsChecked} - /> - - {t('passwordTermsWarning', [ - e.stopPropagation()} - key="create-password__link-text" - href={ZENDESK_URLS.PASSWORD_ARTICLE} - target="_blank" - rel="noopener noreferrer" - > - - {t('learnMoreUpperCase')} - - , - ])} - +
e.stopPropagation()} + key="create-password__link-text" + href={ZENDESK_URLS.PASSWORD_ARTICLE} + target="_blank" + rel="noopener noreferrer" + > + + {t('learnMoreUpperCase')} + + , + ])} + +