diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3313db7f8..973ee001a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -304,7 +304,7 @@ jobs:
steps:
- run:
name: Validate changelog
- command: yarn auto-changelog validate
+ command: yarn lint:changelog
- when:
condition:
matches:
@@ -313,7 +313,7 @@ jobs:
steps:
- run:
name: Validate release candidate changelog
- command: yarn auto-changelog validate --rc
+ command: yarn lint:changelog:rc
test-deps-audit:
diff --git a/.eslintrc.js b/.eslintrc.js
index d3763fdef..b6094c794 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -166,6 +166,16 @@ module.exports = {
sourceType: 'script',
},
},
+ {
+ files: [
+ 'app/scripts/lockdown-run.js',
+ 'test/unit-global/protect-intrinsics.test.js',
+ ],
+ globals: {
+ harden: 'readonly',
+ Compartment: 'readonly',
+ },
+ },
],
settings: {
diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml
index 9bef72a01..b97bb8247 100644
--- a/.github/workflows/cla.yml
+++ b/.github/workflows/cla.yml
@@ -22,6 +22,6 @@ jobs:
url-to-cladocument: 'https://metamask.io/cla.html'
# This branch can't have protections, commits are made directly to the specified branch.
branch: 'cla-signatures'
- allowlist: 'dependabot[bot],metamaskbot,muji'
+ allowlist: 'dependabot[bot],metamaskbot'
allow-organization-members: true
blockchain-storage-flag: false
diff --git a/.storybook/images/UNI.png b/.storybook/images/UNI.png
new file mode 100644
index 000000000..01ffcf726
Binary files /dev/null and b/.storybook/images/UNI.png differ
diff --git a/.storybook/initial-states/approval-screens/add-suggested-token.js b/.storybook/initial-states/approval-screens/add-suggested-token.js
new file mode 100644
index 000000000..0b623e3f7
--- /dev/null
+++ b/.storybook/initial-states/approval-screens/add-suggested-token.js
@@ -0,0 +1,65 @@
+export const suggestedTokens = {
+ "0x6b175474e89094c44da98b954eedeac495271d0f": {
+ "address": "0x6b175474e89094c44da98b954eedeac495271d0f",
+ "symbol": "META",
+ "decimals": 18,
+ "image": "metamark.svg",
+ "unlisted": false
+ },
+ "0xB8c77482e45F1F44dE1745F52C74426C631bDD52": {
+ "address": "0xB8c77482e45F1F44dE1745F52C74426C631bDD52",
+ "symbol": "0X",
+ "decimals": 18,
+ "image": "0x.svg",
+ "unlisted": false
+ },
+ "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984": {
+ "address": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
+ "symbol": "AST",
+ "decimals": 18,
+ "image": "ast.png",
+ "unlisted": false
+ },
+ "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2": {
+ "address": "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2",
+ "symbol": "BAT",
+ "decimals": 18,
+ "image": "BAT_icon.svg",
+ "unlisted": false
+ },
+ "0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1": {
+ "address": "0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1",
+ "symbol": "CVL",
+ "decimals": 18,
+ "image": "CVL_token.svg",
+ "unlisted": false
+ },
+ "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e": {
+ "address": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
+ "symbol": "GLA",
+ "decimals": 18,
+ "image": "gladius.svg",
+ "unlisted": false
+ },
+ "0x467Bccd9d29f223BcE8043b84E8C8B282827790F": {
+ "address": "0x467Bccd9d29f223BcE8043b84E8C8B282827790F",
+ "symbol": "GNO",
+ "decimals": 18,
+ "image": "gnosis.svg",
+ "unlisted": false
+ },
+ "0xff20817765cb7f73d4bde2e66e067e58d11095c2": {
+ "address": "0xff20817765cb7f73d4bde2e66e067e58d11095c2",
+ "symbol": "OMG",
+ "decimals": 18,
+ "image": "omg.jpg",
+ "unlisted": false
+ },
+ "0x8e870d67f660d95d5be530380d0ec0bd388289e1": {
+ "address": "0x8e870d67f660d95d5be530380d0ec0bd388289e1",
+ "symbol": "WED",
+ "decimals": 18,
+ "image": "wed.png",
+ "unlisted": false
+ },
+ }
\ No newline at end of file
diff --git a/.storybook/initial-states/approval-screens/add-token.js b/.storybook/initial-states/approval-screens/add-token.js
new file mode 100644
index 000000000..c5a7699f1
--- /dev/null
+++ b/.storybook/initial-states/approval-screens/add-token.js
@@ -0,0 +1,56 @@
+export const tokens = {
+ "0x33f90dee07c6e8b9682dd20f73e6c358b2ed0f03": {
+ "address": "0x33f90dee07c6e8b9682dd20f73e6c358b2ed0f03",
+ "symbol": "TRDT",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0x39013f961c378f02c2b82a6e1d31e9812786fd9d": {
+ "address": "0x39013f961c378f02c2b82a6e1d31e9812786fd9d",
+ "symbol": "SMS",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0x78b7fada55a64dd895d8c8c35779dd8b67fa8a05": {
+ "address": "0x78b7fada55a64dd895d8c8c35779dd8b67fa8a05",
+ "symbol": "ATL",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16": {
+ "address": "0xfd8971d5e8e1740ce2d0a84095fca4de729d0c16",
+ "symbol": "ZLA",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1": {
+ "address": "0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1",
+ "symbol": "BTT",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0x7a07e1a0c2514d51132183ecfea2a880ec3b7648": {
+ "address": "0x7a07e1a0c2514d51132183ecfea2a880ec3b7648",
+ "symbol": "IXE",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0x467Bccd9d29f223BcE8043b84E8C8B282827790F": {
+ "address": "0x467Bccd9d29f223BcE8043b84E8C8B282827790F",
+ "symbol": "TEL",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0xff20817765cb7f73d4bde2e66e067e58d11095c2": {
+ "address": "0xff20817765cb7f73d4bde2e66e067e58d11095c2",
+ "symbol": "AMP",
+ "decimals": 18,
+ "unlisted": false
+ },
+ "0x15bda08c3afbf5955d6e9b235fd55a1fd0dbc829": {
+ "address": "0x15bda08c3afbf5955d6e9b235fd55a1fd0dbc829",
+ "symbol": "APC",
+ "decimals": 18,
+ "unlisted": false
+ },
+ }
\ No newline at end of file
diff --git a/.storybook/test-data.js b/.storybook/test-data.js
index eede73242..242a70acb 100644
--- a/.storybook/test-data.js
+++ b/.storybook/test-data.js
@@ -8,7 +8,13 @@ const state = {
"unconnectedAccount": {
"state": "CLOSED"
},
- "activeTab": {},
+ "activeTab": {
+ "id": 113,
+ "title": "E2E Test Dapp",
+ "origin": "https://metamask.github.io",
+ "protocol": "https:",
+ "url": "https://metamask.github.io/test-dapp/"
+ },
"metamask": {
"networkDetails": {
"EIPS": {
@@ -34,8 +40,8 @@ const state = {
}
},
"unapprovedTxs": {
- "7786962153682822": {
- "id": 7786962153682822,
+ "3111025347726181": {
+ "id": 3111025347726181,
"time": 1620710815484,
"status": "unapproved",
"metamaskNetworkId": "3",
@@ -43,7 +49,7 @@ const state = {
"loadingDefaults": false,
"txParams": {
"from": "0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
- "to": "0xad6d458402f60fd3bd25163575031acdce07538d",
+ "to": "0xaD6D458402F60fD3Bd25163575031ACDce07538D",
"value": "0x0",
"data": "0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000",
"gas": "0xcb28",
@@ -62,7 +68,7 @@ const state = {
"loadingDefaults": true,
"txParams": {
"from": "0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
- "to": "0xad6d458402f60fd3bd25163575031acdce07538d",
+ "to": "0xaD6D458402F60fD3Bd25163575031ACDce07538D",
"value": "0x0",
"data": "0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000",
"gas": "0xcb28",
@@ -95,11 +101,11 @@ const state = {
}
},
"contractExchangeRates": {
- "0xad6d458402f60fd3bd25163575031acdce07538d": 0
+ "0xaD6D458402F60fD3Bd25163575031ACDce07538D": 0
},
"tokens": [
{
- "address": "0xad6d458402f60fd3bd25163575031acdce07538d",
+ "address": "0xaD6D458402F60fD3Bd25163575031ACDce07538D",
"symbol": "DAI",
"decimals": 18
}
@@ -122,7 +128,7 @@ const state = {
"ensResolution": null,
"ensResolutionError": "",
"token": {
- "address": "0xad6d458402f60fd3bd25163575031acdce07538d",
+ "address": "0xaD6D458402F60fD3Bd25163575031ACDce07538D",
"symbol": "DAI",
"decimals": 18
}
@@ -259,7 +265,7 @@ const state = {
],
"0x3": [
{
- "address": "0xad6d458402f60fd3bd25163575031acdce07538d",
+ "address": "0xaD6D458402F60fD3Bd25163575031ACDce07538D",
"symbol": "DAI",
"decimals": 18
}
@@ -274,7 +280,7 @@ const state = {
}
},
"assetImages": {
- "0xad6d458402f60fd3bd25163575031acdce07538d": "./images/logo.png"
+ "0xaD6D458402F60fD3Bd25163575031ACDce07538D": "./sai.svg"
},
"hiddenTokens": [],
"suggestedTokens": {},
@@ -547,7 +553,7 @@ const state = {
}
],
"permissionsHistory": {
- "https://app.uniswap.org": {
+ "https://metamask.github.io": {
"eth_accounts": {
"lastApproved": 1620710693213,
"accounts": {
@@ -562,6 +568,12 @@ const state = {
"icon": "https://metamask.github.io/test-dapp/metamask-fox.svg",
"lastUpdated": 1620723443380,
"host": "metamask.github.io"
+ },
+ "https://app.uniswap.org": {
+ "name": "Uniswap",
+ "icon": "./UNI.png",
+ "lastUpdated": 1620723443380,
+ "host": "app.uniswap.org"
}
},
"threeBoxSyncingAllowed": false,
@@ -652,14 +664,14 @@ const state = {
"chainId": "0x3",
"loadingDefaults": false,
"txParams": {
- "from": "0x983211ce699ea5ab57cc528086154b6db1ad8e55",
- "to": "0xad6d458402f60fd3bd25163575031acdce07538d",
+ "from": "0x64a845a5b02460acf8a3d84503b0d68d028b4bb4",
+ "to": "0xaD6D458402F60fD3Bd25163575031ACDce07538D",
"value": "0x0",
"data": "0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170",
"gas": "0xea60",
"gasPrice": "0x4a817c800"
},
- "type": "standard",
+ "type": "transfer",
"origin": "https://metamask.github.io",
"transactionCategory": "approve",
"history": [
@@ -672,7 +684,7 @@ const state = {
"loadingDefaults": true,
"txParams": {
"from": "0x983211ce699ea5ab57cc528086154b6db1ad8e55",
- "to": "0xad6d458402f60fd3bd25163575031acdce07538d",
+ "to": "0xaD6D458402F60fD3Bd25163575031ACDce07538D",
"value": "0x0",
"data": "0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170",
"gas": "0xea60",
diff --git a/app/_locales/am/messages.json b/app/_locales/am/messages.json
index 2a19d1ced..6622ee03c 100644
--- a/app/_locales/am/messages.json
+++ b/app/_locales/am/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "አውታረ መረብ አክል"
},
- "addRecipient": {
- "message": "ተቀባይ አክል"
- },
"addSuggestedTokens": {
"message": "የተጠቆሙ ተለዋጭ ስሞችን አክል"
},
diff --git a/app/_locales/ar/messages.json b/app/_locales/ar/messages.json
index 4b7cd7bb7..200cf48a5 100644
--- a/app/_locales/ar/messages.json
+++ b/app/_locales/ar/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "أضف شبكة"
},
- "addRecipient": {
- "message": "إضافة مستلم"
- },
"addSuggestedTokens": {
"message": "أضف العملات الرمزية المقترحة"
},
diff --git a/app/_locales/bg/messages.json b/app/_locales/bg/messages.json
index 803779b79..6fdc24928 100644
--- a/app/_locales/bg/messages.json
+++ b/app/_locales/bg/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Добавяне на мрежа"
},
- "addRecipient": {
- "message": "Добавете получател"
- },
"addSuggestedTokens": {
"message": "Добавете препоръчани жетони"
},
diff --git a/app/_locales/bn/messages.json b/app/_locales/bn/messages.json
index e0d45ad97..58f0a92b5 100644
--- a/app/_locales/bn/messages.json
+++ b/app/_locales/bn/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "নেটওয়ার্ক যোগ করুন"
},
- "addRecipient": {
- "message": "প্রাপক যোগ করুন"
- },
"addSuggestedTokens": {
"message": "প্রস্তাবিত টোকেনগুলি যোগ করুন"
},
diff --git a/app/_locales/ca/messages.json b/app/_locales/ca/messages.json
index ab0cbd0a8..b7c5ed7b6 100644
--- a/app/_locales/ca/messages.json
+++ b/app/_locales/ca/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Afegir Xarxa"
},
- "addRecipient": {
- "message": "Afegeix un recipient"
- },
"addSuggestedTokens": {
"message": "Afegir Fitxes Suggerides"
},
diff --git a/app/_locales/da/messages.json b/app/_locales/da/messages.json
index 8a5143637..d41c80913 100644
--- a/app/_locales/da/messages.json
+++ b/app/_locales/da/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Tilføj netværk"
},
- "addRecipient": {
- "message": "Tilføj modtager"
- },
"addSuggestedTokens": {
"message": "Tilføj foreslåede tokens"
},
diff --git a/app/_locales/de/messages.json b/app/_locales/de/messages.json
index 116d81b4b..109851fe5 100644
--- a/app/_locales/de/messages.json
+++ b/app/_locales/de/messages.json
@@ -35,9 +35,6 @@
"addNetwork": {
"message": "Netzwerk hinzufügen"
},
- "addRecipient": {
- "message": "Empfänger hinzufügen"
- },
"addSuggestedTokens": {
"message": "Vorgeschlagene Token hinzufügen"
},
diff --git a/app/_locales/el/messages.json b/app/_locales/el/messages.json
index 50edf7795..033aeaa75 100644
--- a/app/_locales/el/messages.json
+++ b/app/_locales/el/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Προσθήκη Δικτύου"
},
- "addRecipient": {
- "message": "Προσθήκη Παραλήπτη"
- },
"addSuggestedTokens": {
"message": "Προσθέστε τα Προτεινόμενα Tokens"
},
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 8928ca36e..8e528f42e 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Add Network"
},
- "addRecipient": {
- "message": "Add Recipient"
- },
"addSuggestedTokens": {
"message": "Add Suggested Tokens"
},
@@ -323,10 +320,10 @@
"message": "Confirm password"
},
"confirmSecretBackupPhrase": {
- "message": "Confirm your Secret Backup Phrase"
+ "message": "Confirm your Secret Recovery Phrase"
},
"confirmSeedPhrase": {
- "message": "Confirm Seed Phrase"
+ "message": "Confirm Secret Recovery Phrase"
},
"confirmed": {
"message": "Confirmed"
@@ -594,10 +591,10 @@
"message": "Dismiss"
},
"dismissReminderDescriptionField": {
- "message": "Turn this on to dismiss the recovery phrase backup reminder message. We highly recommend that you back up your Secret Recovery Phrase to avoid loss of funds"
+ "message": "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"
},
"dismissReminderField": {
- "message": "Dismiss recovery phrase backup reminder"
+ "message": "Dismiss Secret Recovery Phrase backup reminder"
},
"domain": {
"message": "Domain"
@@ -612,7 +609,7 @@
"message": "Download Google Chrome"
},
"downloadSecretBackup": {
- "message": "Download this Secret Backup Phrase and keep it stored safely on an external encrypted hard drive or storage medium."
+ "message": "Download this Secret Recovery Phrase and keep it stored safely on an external encrypted hard drive or storage medium."
},
"downloadStateLogs": {
"message": "Download State Logs"
@@ -630,16 +627,16 @@
"message": "How should I choose?"
},
"editGasEducationHighExplanation": {
- "message": "This is best for swaps or other time sensitive transactions. If a swap takes too long to process it will often fail and you may lose funds."
+ "message": "This is best for time sensitive transactions (like Swaps) as it increases the likelihood of a successful transaction. If a Swap takes too long to process it may fail and result in losing some of your gas fee."
},
"editGasEducationLowExplanation": {
- "message": "A lower gas fee should only be selected for transactions where processing time is less important. With a lower fee, it can be hard to predict when (or if) your transaction will be successful."
+ "message": "A lower gas fee should only be used when processing time is less important. Lower fees make it hard predict when (or if) your transaction will be successful."
},
"editGasEducationMediumExplanation": {
- "message": "A medium gas fee is good for sending, withdrawing or other non-time sensitive but important transactions."
+ "message": "A medium gas fee is good for sending, withdrawing or other non-time sensitive transactions. This setting will most often result in a successful transaction."
},
"editGasEducationModalIntro": {
- "message": "The right gas amount to select depends on the type of transaction and how important it is."
+ "message": "Selecting the right gas fee depends on the type of transaction and how important it is to you."
},
"editGasEducationModalTitle": {
"message": "How to choose?"
@@ -675,7 +672,7 @@
"message": "Max priority fee is higher than necessary. You may pay more than needed."
},
"editGasMaxPriorityFeeLow": {
- "message": "Max priority fee extremely low for network conditions"
+ "message": "Max priority fee is low for current network conditions"
},
"editGasMaxPriorityFeeTooltip": {
"message": "Max priority fee (aka “miner tip”) goes directly to miners and incentivizes them to prioritize your transaction. You’ll most often pay your max setting"
@@ -714,9 +711,8 @@
"editGasTooLowTooltip": {
"message": "Your max fee or max priority fee may be low for current market conditions. We don't know when (or if) your transaction will be processed. "
},
- "editGasTotalBannerSubtitle": {
- "message": "Up to $1 ($2)",
- "display": "$1 represents a fiat value"
+ "editGasTooLowWarningTooltip": {
+ "message": "This lowers your maximum fee but if network traffic increases your transaction may be delayed or fail."
},
"editNonceField": {
"message": "Edit Nonce"
@@ -1048,6 +1044,9 @@
"importAccount": {
"message": "Import Account"
},
+ "importAccountError": {
+ "message": "Error importing account."
+ },
"importAccountLinkText": {
"message": "import using Secret Recovery Phrase"
},
@@ -1221,7 +1220,7 @@
},
"makeSureNoOneWatching": {
"message": "Make sure no one is watching your screen",
- "description": "Warning to users to be care while creating and saving their new seed phrase"
+ "description": "Warning to users to be care while creating and saving their new Secret Recovery Phrase"
},
"max": {
"message": "Max"
@@ -1774,13 +1773,13 @@
"message": "Search Tokens"
},
"secretBackupPhrase": {
- "message": "Secret Backup Phrase"
+ "message": "Secret Recovery Phrase"
},
"secretBackupPhraseDescription": {
- "message": "Your secret backup phrase makes it easy to back up and restore your account."
+ "message": "Your Secret Recovery Phrase makes it easy to back up and restore your account."
},
"secretBackupPhraseWarning": {
- "message": "WARNING: Never disclose your backup phrase. Anyone with this phrase can take your Ether forever."
+ "message": "WARNING: Never disclose your Secret Recovery Phrase. Anyone with this phrase can take your Ether forever."
},
"secretPhrase": {
"message": "Enter your secret phrase here to restore your vault."
@@ -1813,28 +1812,28 @@
"message": "Store in a bank vault."
},
"seedPhraseIntroSidebarCopyOne": {
- "message": "Your recovery phrase is the “master key” to your wallet and funds."
+ "message": "Your Secret Recovery Phrase is the “master key” to your wallet and funds."
},
"seedPhraseIntroSidebarCopyThree": {
- "message": "If someone asks for your recovery phrase, they are most likely trying to scam you."
+ "message": "If someone asks for your Secret Recovery Phrase, they are most likely trying to scam you."
},
"seedPhraseIntroSidebarCopyTwo": {
- "message": "Never, ever share your recovery phrase, even with MetaMask!"
+ "message": "Never, ever share your Secret Recovery Phrase, even with MetaMask!"
},
"seedPhraseIntroSidebarTitleOne": {
- "message": "What is a recovery phrase?"
+ "message": "What is a Secret Recovery Phrase?"
},
"seedPhraseIntroSidebarTitleThree": {
- "message": "Should I share my recovery phrase?"
+ "message": "Should I share my Secret Recovery Phrase?"
},
"seedPhraseIntroSidebarTitleTwo": {
- "message": "How do I save my recovery phrase?"
+ "message": "How do I save my Secret Recovery Phrase?"
},
"seedPhraseIntroTitle": {
"message": "Secure your wallet"
},
"seedPhraseIntroTitleCopy": {
- "message": "Before getting started, watch this short video to learn about your recovery phrase and how to keep your wallet safe."
+ "message": "Before getting started, watch this short video to learn about your Secret Recovery Phrase and how to keep your wallet safe."
},
"seedPhrasePlaceholder": {
"message": "Separate each word with a single space"
@@ -1898,6 +1897,9 @@
"message": "Send $1",
"description": "Symbol of the specified token"
},
+ "sendTo": {
+ "message": "Send to"
+ },
"sendTokens": {
"message": "Send Tokens"
},
@@ -1956,7 +1958,7 @@
"message": "Sign"
},
"signNotice": {
- "message": "Signing this message can have \ndangerous side effects. Only sign messages from \nsites you fully trust with your entire account.\n This dangerous method will be removed in a future version. "
+ "message": "We are unable to predict the result of this signature. This signature could potentially perform any operation on your account's behalf, including granting complete control of your account and all of its assets to the requesting site. Only sign this message if you know what you're doing or completely trust the requesting site."
},
"signatureRequest": {
"message": "Signature Request"
@@ -2416,6 +2418,9 @@
"symbolBetweenZeroTwelve": {
"message": "Symbol must be 11 characters or fewer."
},
+ "syncFailed": {
+ "message": "Sync failed"
+ },
"syncInProgress": {
"message": "Sync in progress"
},
@@ -2510,9 +2515,8 @@
"transactionCreated": {
"message": "Transaction created with a value of $1 at $2."
},
- "transactionDetailDappGasHeading": {
- "message": "$1 suggested gas fee",
- "description": "$1 represents a dapp origin"
+ "transactionDetailDappGasMoreInfo": {
+ "message": "Site suggested"
},
"transactionDetailDappGasTooltip": {
"message": "Edit to use MetaMask's recommended gas fee based on the latest block."
@@ -2527,7 +2531,7 @@
"message": "Gas fees are set by the network and fluctuate based on network traffic and transaction complexity."
},
"transactionDetailGasTooltipIntro": {
- "message": "Gas fees are paid to crypto miners who process transactions on the Ethereum network. MetaMask does not profit from gas fees."
+ "message": "Gas fees are paid to crypto miners who process transactions on the $1 network. MetaMask does not profit from gas fees."
},
"transactionDetailGasTotalSubtitle": {
"message": "Amount + gas fee"
diff --git a/app/_locales/es/messages.json b/app/_locales/es/messages.json
index 33fb6d0e5..50cab00ed 100644
--- a/app/_locales/es/messages.json
+++ b/app/_locales/es/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Agregar red"
},
- "addRecipient": {
- "message": "Agregar destinatario"
- },
"addSuggestedTokens": {
"message": "Agregar tokens sugeridos"
},
diff --git a/app/_locales/es_419/messages.json b/app/_locales/es_419/messages.json
index 33fb6d0e5..50cab00ed 100644
--- a/app/_locales/es_419/messages.json
+++ b/app/_locales/es_419/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Agregar red"
},
- "addRecipient": {
- "message": "Agregar destinatario"
- },
"addSuggestedTokens": {
"message": "Agregar tokens sugeridos"
},
diff --git a/app/_locales/et/messages.json b/app/_locales/et/messages.json
index 58d69b71e..c85dc3a29 100644
--- a/app/_locales/et/messages.json
+++ b/app/_locales/et/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Lisage võrk"
},
- "addRecipient": {
- "message": "Lisa saaja"
- },
"addSuggestedTokens": {
"message": "Lisa soovitatud lube"
},
diff --git a/app/_locales/fa/messages.json b/app/_locales/fa/messages.json
index 3d95df38b..4c09d9770 100644
--- a/app/_locales/fa/messages.json
+++ b/app/_locales/fa/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "اضافه شبکه"
},
- "addRecipient": {
- "message": "اضافه کردن دریافت کننده"
- },
"addSuggestedTokens": {
"message": "اضافه رمزیاب های پیشنهاد شده"
},
diff --git a/app/_locales/fi/messages.json b/app/_locales/fi/messages.json
index 9df9c20d5..41aeff611 100644
--- a/app/_locales/fi/messages.json
+++ b/app/_locales/fi/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Lisää verkko"
},
- "addRecipient": {
- "message": "Lisää vastaanottaja"
- },
"addSuggestedTokens": {
"message": "Lisää ehdotetut käyttötunnukset"
},
diff --git a/app/_locales/fil/messages.json b/app/_locales/fil/messages.json
index 91c808bfc..809d4ac43 100644
--- a/app/_locales/fil/messages.json
+++ b/app/_locales/fil/messages.json
@@ -35,9 +35,6 @@
"addNetwork": {
"message": "Magdagdag ng Network"
},
- "addRecipient": {
- "message": "Magdagdag ng Recipient"
- },
"addSuggestedTokens": {
"message": "Magdagdag ng Mga Iminungkahing Token"
},
diff --git a/app/_locales/fr/messages.json b/app/_locales/fr/messages.json
index 2f8e52fed..fefa18ca1 100644
--- a/app/_locales/fr/messages.json
+++ b/app/_locales/fr/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Ajouter un réseau"
},
- "addRecipient": {
- "message": "Ajouter destinataire"
- },
"addSuggestedTokens": {
"message": "Ajouter les jetons suggérés"
},
@@ -657,7 +654,7 @@
"message": "de"
},
"off": {
- "message": "Déconnecté"
+ "message": "Désactivé"
},
"on": {
"message": "Activé"
diff --git a/app/_locales/he/messages.json b/app/_locales/he/messages.json
index a0d2e9686..14761f635 100644
--- a/app/_locales/he/messages.json
+++ b/app/_locales/he/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "הוסף/י רשת"
},
- "addRecipient": {
- "message": "הוסף נמען"
- },
"addSuggestedTokens": {
"message": "הוסף/י אסימונים מוצעים"
},
diff --git a/app/_locales/hi/messages.json b/app/_locales/hi/messages.json
index 31736f438..b94ffe20d 100644
--- a/app/_locales/hi/messages.json
+++ b/app/_locales/hi/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "नेटवर्क जोड़ें"
},
- "addRecipient": {
- "message": "प्राप्तकर्ता को जोड़ें"
- },
"addSuggestedTokens": {
"message": "सुझाए गए टोकन जोड़ें"
},
diff --git a/app/_locales/hr/messages.json b/app/_locales/hr/messages.json
index 44ca7e1db..ca55da6c5 100644
--- a/app/_locales/hr/messages.json
+++ b/app/_locales/hr/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Dodaj mrežu"
},
- "addRecipient": {
- "message": "Dodaj primatelja"
- },
"addSuggestedTokens": {
"message": "Dodaj predložene tokene"
},
diff --git a/app/_locales/hu/messages.json b/app/_locales/hu/messages.json
index f7ea54d5b..ecee8c255 100644
--- a/app/_locales/hu/messages.json
+++ b/app/_locales/hu/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Hálózat hozzáadása"
},
- "addRecipient": {
- "message": "Címzett hozzáadása"
- },
"addSuggestedTokens": {
"message": "Javasolt tokenek hozzáadása"
},
diff --git a/app/_locales/id/messages.json b/app/_locales/id/messages.json
index 0c1b5a0c8..1179db5e1 100644
--- a/app/_locales/id/messages.json
+++ b/app/_locales/id/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Tambahkan Jaringan"
},
- "addRecipient": {
- "message": "Tambahkan Penerima"
- },
"addSuggestedTokens": {
"message": "Tambahkan Token yang Disarankan"
},
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
index f3923d480..7ff83f0ed 100644
--- a/app/_locales/it/messages.json
+++ b/app/_locales/it/messages.json
@@ -52,9 +52,6 @@
"addNetwork": {
"message": "Aggiungi Rete"
},
- "addRecipient": {
- "message": "Aggiungi destinatario"
- },
"addSuggestedTokens": {
"message": "Aggiungi Token Suggeriti"
},
diff --git a/app/_locales/ja/messages.json b/app/_locales/ja/messages.json
index bc3b961d2..45b023b4c 100644
--- a/app/_locales/ja/messages.json
+++ b/app/_locales/ja/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "ネットワークの追加"
},
- "addRecipient": {
- "message": "受信者の追加"
- },
"addSuggestedTokens": {
"message": "推奨されたトークンの追加"
},
diff --git a/app/_locales/kn/messages.json b/app/_locales/kn/messages.json
index d5c178c3e..f847d8957 100644
--- a/app/_locales/kn/messages.json
+++ b/app/_locales/kn/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "ನೆಟ್ವರ್ಕ್ ಸೇರಿಸಿ"
},
- "addRecipient": {
- "message": "ಸ್ವೀಕೃತಿದಾರರನ್ನು ಸೇರಿಸಿ"
- },
"addSuggestedTokens": {
"message": "ಸೂಚಿಸಲಾದ ಟೋಕನ್ಗಳನ್ನು ಸೇರಿಸಿ"
},
diff --git a/app/_locales/ko/messages.json b/app/_locales/ko/messages.json
index d8e78ce2b..b79004c42 100644
--- a/app/_locales/ko/messages.json
+++ b/app/_locales/ko/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "네트워크 추가"
},
- "addRecipient": {
- "message": "수신인 추가"
- },
"addSuggestedTokens": {
"message": "추천 토큰 추가"
},
diff --git a/app/_locales/lt/messages.json b/app/_locales/lt/messages.json
index 4daca63f5..54c9f6531 100644
--- a/app/_locales/lt/messages.json
+++ b/app/_locales/lt/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Pridėti tinklą"
},
- "addRecipient": {
- "message": "Pridėti gavėją"
- },
"addSuggestedTokens": {
"message": "Pridėti siūlomų žetonų"
},
diff --git a/app/_locales/lv/messages.json b/app/_locales/lv/messages.json
index e2dc7dccc..f05e6f544 100644
--- a/app/_locales/lv/messages.json
+++ b/app/_locales/lv/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Pievienot tīklu"
},
- "addRecipient": {
- "message": "Pievienot saņēmēju"
- },
"addSuggestedTokens": {
"message": "Pievienot ieteiktos marķierus"
},
diff --git a/app/_locales/ms/messages.json b/app/_locales/ms/messages.json
index 340cecf2f..79523de48 100644
--- a/app/_locales/ms/messages.json
+++ b/app/_locales/ms/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Tambah Rangkaian"
},
- "addRecipient": {
- "message": "Tambah Penerima"
- },
"addSuggestedTokens": {
"message": "Tambah Token yang Disyorkan"
},
diff --git a/app/_locales/no/messages.json b/app/_locales/no/messages.json
index a62885187..e9c168390 100644
--- a/app/_locales/no/messages.json
+++ b/app/_locales/no/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Legg til nettverk"
},
- "addRecipient": {
- "message": "Legg til mottaker "
- },
"addSuggestedTokens": {
"message": "Legg til foreslåtte tokener "
},
diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json
index 96637d9bf..0e831ca3a 100644
--- a/app/_locales/ph/messages.json
+++ b/app/_locales/ph/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Magdagdag ng Network"
},
- "addRecipient": {
- "message": "Magdagdag ng Recipient"
- },
"addSuggestedTokens": {
"message": "Magdagdag ng Mga Iminumungkahing Token"
},
diff --git a/app/_locales/pl/messages.json b/app/_locales/pl/messages.json
index 903a977e0..154b70941 100644
--- a/app/_locales/pl/messages.json
+++ b/app/_locales/pl/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Dodaj sieć"
},
- "addRecipient": {
- "message": "Dodaj odbiorcę"
- },
"addSuggestedTokens": {
"message": "Dodaj sugerowane tokeny."
},
diff --git a/app/_locales/pt_BR/messages.json b/app/_locales/pt_BR/messages.json
index 344179b45..7f08ceb34 100644
--- a/app/_locales/pt_BR/messages.json
+++ b/app/_locales/pt_BR/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Adicionar rede"
},
- "addRecipient": {
- "message": "Adicionar destinatário"
- },
"addSuggestedTokens": {
"message": "Adicionar tokens sugeridos"
},
diff --git a/app/_locales/ro/messages.json b/app/_locales/ro/messages.json
index ec7270acf..bb0c4b73c 100644
--- a/app/_locales/ro/messages.json
+++ b/app/_locales/ro/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Adăugați rețea"
},
- "addRecipient": {
- "message": "Adăugați destinatarul"
- },
"addSuggestedTokens": {
"message": "Adăugați indicativele sugerate"
},
diff --git a/app/_locales/ru/messages.json b/app/_locales/ru/messages.json
index 353a543a3..b6091fa3e 100644
--- a/app/_locales/ru/messages.json
+++ b/app/_locales/ru/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Добавить сеть"
},
- "addRecipient": {
- "message": "Добавить получателя"
- },
"addSuggestedTokens": {
"message": "Добавить предложенные токены"
},
diff --git a/app/_locales/sk/messages.json b/app/_locales/sk/messages.json
index 5f273558e..caa5cff7f 100644
--- a/app/_locales/sk/messages.json
+++ b/app/_locales/sk/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Pridať sieť"
},
- "addRecipient": {
- "message": "Pridať príjemcu"
- },
"addSuggestedTokens": {
"message": "Pridať navrhované tokeny"
},
diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json
index 726dbb591..3b50867eb 100644
--- a/app/_locales/sl/messages.json
+++ b/app/_locales/sl/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Dodaj omrežje"
},
- "addRecipient": {
- "message": "Dodaj prejemnika"
- },
"addSuggestedTokens": {
"message": "Dodaj priporočene žetone"
},
diff --git a/app/_locales/sr/messages.json b/app/_locales/sr/messages.json
index 37a43945d..4511f86b0 100644
--- a/app/_locales/sr/messages.json
+++ b/app/_locales/sr/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Dodajte mrežu"
},
- "addRecipient": {
- "message": "Dodaj primaoca"
- },
"addSuggestedTokens": {
"message": "Dodajte sugerisane tokene"
},
diff --git a/app/_locales/sv/messages.json b/app/_locales/sv/messages.json
index c6e35a7a5..16f772796 100644
--- a/app/_locales/sv/messages.json
+++ b/app/_locales/sv/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Lägg till nätverk"
},
- "addRecipient": {
- "message": "Lägg till mottagare"
- },
"addSuggestedTokens": {
"message": "Lägg till föreslagna tokens"
},
diff --git a/app/_locales/sw/messages.json b/app/_locales/sw/messages.json
index f2c3a4fa4..353e0fced 100644
--- a/app/_locales/sw/messages.json
+++ b/app/_locales/sw/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Ongeza Mtandao"
},
- "addRecipient": {
- "message": "Ongeza Mpokeaji"
- },
"addSuggestedTokens": {
"message": "Ongeza Vianzio Vilivyopendekezwa"
},
diff --git a/app/_locales/tl/messages.json b/app/_locales/tl/messages.json
index 523b97d57..24dc2646a 100644
--- a/app/_locales/tl/messages.json
+++ b/app/_locales/tl/messages.json
@@ -52,9 +52,6 @@
"addNetwork": {
"message": "Magdagdag ng Network"
},
- "addRecipient": {
- "message": "Magdagdag ng Recipient"
- },
"addSuggestedTokens": {
"message": "Magdagdag ng Mga Iminumungkahing Token"
},
diff --git a/app/_locales/tr/messages.json b/app/_locales/tr/messages.json
index 3c4bdebf7..2875cc770 100644
--- a/app/_locales/tr/messages.json
+++ b/app/_locales/tr/messages.json
@@ -322,7 +322,7 @@
"message": "Gelecekte Bu jetonu hesap seçenekleri menüsünde “Jeton ekle”'ye giderek geri ekleyebilirsiniz."
},
"reject": {
- "message": "Reddetmek"
+ "message": "Reddet"
},
"rejected": {
"message": "Rededildi"
diff --git a/app/_locales/uk/messages.json b/app/_locales/uk/messages.json
index 6beb7c861..c7c8cd9ac 100644
--- a/app/_locales/uk/messages.json
+++ b/app/_locales/uk/messages.json
@@ -38,9 +38,6 @@
"addNetwork": {
"message": "Додати мережу"
},
- "addRecipient": {
- "message": "Додати отримувача"
- },
"addSuggestedTokens": {
"message": "Додати рекомендовані токени"
},
diff --git a/app/_locales/vi/messages.json b/app/_locales/vi/messages.json
index c6f1af5a3..51bf77096 100644
--- a/app/_locales/vi/messages.json
+++ b/app/_locales/vi/messages.json
@@ -79,9 +79,6 @@
"addNetwork": {
"message": "Thêm mạng"
},
- "addRecipient": {
- "message": "Thêm người nhận"
- },
"addSuggestedTokens": {
"message": "Thêm token được đề xuất"
},
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index e9de843e4..d7b1b0e1b 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -52,9 +52,6 @@
"addNetwork": {
"message": "添加网络"
},
- "addRecipient": {
- "message": "添加接收方"
- },
"addSuggestedTokens": {
"message": "添加推荐代币"
},
diff --git a/app/_locales/zh_TW/messages.json b/app/_locales/zh_TW/messages.json
index 208b184a1..4a5c3deaa 100644
--- a/app/_locales/zh_TW/messages.json
+++ b/app/_locales/zh_TW/messages.json
@@ -41,9 +41,6 @@
"addNetwork": {
"message": "新增網路"
},
- "addRecipient": {
- "message": "新增接收人"
- },
"addSuggestedTokens": {
"message": "加入建議的代幣"
},
diff --git a/app/images/videos/recovery-onboarding/subtitles-en.vtt b/app/images/videos/recovery-onboarding/subtitles/en.vtt
similarity index 100%
rename from app/images/videos/recovery-onboarding/subtitles-en.vtt
rename to app/images/videos/recovery-onboarding/subtitles/en.vtt
diff --git a/app/images/videos/recovery-onboarding/subtitles/es.vtt b/app/images/videos/recovery-onboarding/subtitles/es.vtt
new file mode 100644
index 000000000..2f56d35be
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/es.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+MetaMask es una nueva forma de conectarse
+a sitios y aplicaciones.
+
+2
+00:00:04.580 --> 00:00:08.860
+En los sitios web tradicionales, una base de datos
+o un banco central es responsable de controlar y
+
+3
+00:00:08.860 --> 00:00:10.179
+recuperar sus cuentas.
+
+4
+00:00:10.179 --> 00:00:15.050
+Pero con MetaMask, todo el control lo tiene
+el titular de la clave maestra.
+
+5
+00:00:15.050 --> 00:00:18.460
+La persona que tenga esta clave controlará las cuentas.
+
+6
+00:00:18.460 --> 00:00:21.110
+La “clave maestra”
+es su frase secreta de recuperación.
+
+7
+00:00:21.110 --> 00:00:26.070
+Esta frase está compuesta por 12 palabras y se crea
+la primera vez que se configura MetaMask; le permite
+
+8
+00:00:26.070 --> 00:00:30.120
+recuperar su cartera y los fondos en caso de que
+alguna vez pierda su clave de acceso.
+
+9
+00:00:30.120 --> 00:00:33.451
+Es fundamental que proteja
+su cartera
+
+10
+00:00:33.451 --> 00:00:37.510
+guardando la frase secreta de recuperación
+en un lugar sumamente seguro y secreto.
+
+11
+00:00:37.510 --> 00:00:41.429
+Si alguna persona llegara a encontrarla, accederá
+a la “clave maestra” de su cartera y podrá
+
+12
+00:00:41.429 --> 00:00:45.190
+ingresar a todos sus fondos y tomarlos libremente.
+
+13
+00:00:45.190 --> 00:00:50.109
+Para proteger su cartera en MetaMask,
+guarde en un lugar seguro su frase secreta de recuperación.
+
+14
+00:00:50.109 --> 00:00:54.930
+Puede anotarla, esconderla en algún lugar,
+guardarla en una caja de seguridad
+
+15
+00:00:54.930 --> 00:00:57.729
+o utilizar un administrador seguro de contraseñas.
+
+16
+00:00:57.729 --> 00:01:01.050
+Inclusive, algunos usuarios graban
+la frase en una placa metálica.
+
+17
+00:01:01.050 --> 00:01:04.440
+Si llegara a perder su frase secreta de recuperación,
+ninguna persona, ni siquiera el equipo de MetaMask, podrá ayudarlo
+
+18
+00:01:04.440 --> 00:01:07.820
+a recuperar
+su cartera.
+
+19
+00:01:07.820 --> 00:01:12.072
+Si aún no ha anotado ni guardado en un lugar seguro su
+frase secreta de recuperación,
+
+20
+00:01:12.072 --> 00:01:15.492
+hágalo ahora mismo. Lo esperamos.
+
+21
+00:01:15.500 --> 00:01:20.780
+Y recuerde no compartir nunca su
+frase secreta de recuperación con nadie; ni siquiera con nosotros.
+
+22
+00:01:20.780 --> 00:01:24.910
+Si alguien se la pide alguna vez,
+será con intenciones de estafarlo.
+
+23
+00:01:24.910 --> 00:01:26.250
+¡Eso es todo!
+
+24
+00:01:26.250 --> 00:01:31.020
+Ahora ya sabe qué es una frase secreta de recuperación
+y qué debe hacer para mantener protegida su cartera.
diff --git a/app/images/videos/recovery-onboarding/subtitles/hi.vtt b/app/images/videos/recovery-onboarding/subtitles/hi.vtt
new file mode 100644
index 000000000..f82c69566
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/hi.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+MetaMask साइटों और एप्लिकेशन से
+जुड़ने का एक नया तरीका है।
+
+2
+00:00:04.580 --> 00:00:08.860
+पारंपरिक वेबसाइटों पर, आपके खातों को नियंत्रित करने और
+पुनर्प्राप्त करने के लिए एक केंद्रीय डेटाबेस या
+
+3
+00:00:08.860 --> 00:00:10.179
+बैंक ज़िम्मेदार होता है।
+
+4
+00:00:10.179 --> 00:00:15.050
+लेकिन MetaMask पर, सारी शक्ति
+मास्टर कुंजी के धारक की होती है।
+
+5
+00:00:15.050 --> 00:00:18.460
+जो भी कुंजी रखता है, वह खातों को नियंत्रित करता है।
+
+6
+00:00:18.460 --> 00:00:21.110
+आपका गुप्त रिकवरी फ्रेज़
+आपकी "मास्टर कुंजी" है।
+
+7
+00:00:21.110 --> 00:00:26.070
+यह 12 शब्दों की एक सीरीज़ होती है, जो
+आपके द्वारा पहली बार MetaMask सेट करने पर जेनरेट होती है, जिससे
+
+8
+00:00:26.070 --> 00:00:30.120
+आप कभी भी एक्सेस खोने पर अपने वॉलेट और धन को
+पुनर्प्राप्त कर सकते हैं।
+
+9
+00:00:30.120 --> 00:00:33.451
+यह महत्वपूर्ण है कि आप
+अपने गुप्त रिकवरी फ्रेज़ को
+
+10
+00:00:33.451 --> 00:00:37.510
+बहुत सुरक्षित और बहुत गुप्त रखकर
+अपने वॉलेट को सुरक्षित रखें।
+
+11
+00:00:37.510 --> 00:00:41.429
+अगर किसी को भी इसकी सुविधा का एक्सेस मिल जाता है, तो
+उनके पास आपके वॉलेट की "मास्टर कुंजी" होगी और
+
+12
+00:00:41.429 --> 00:00:45.190
+वे आपके सारे धन को आसानी से एक्सेस कर सकते हैं।
+
+13
+00:00:45.190 --> 00:00:50.109
+अपने MetaMask वॉलेट को सुरक्षित करने के लिए आप अपने
+गुप्त रिकवरी फ्रेज़ को सुरक्षित रूप से सहेजना चाहेंगे।
+
+14
+00:00:50.109 --> 00:00:54.930
+आप इसे लिख सकते हैं, इसे कहीं छुपा सकते हैं,
+इसे सेफ़ डिपोज़िट बॉक्स में रख सकते हैं
+
+15
+00:00:54.930 --> 00:00:57.729
+या सुरक्षित पासवर्ड मैनेजर का उपयोग कर सकते हैं।
+
+16
+00:00:57.729 --> 00:01:01.050
+कुछ उपयोगकर्ता अपने
+फ्रेज़ को धातु की प्लेट पर भी उकेर कर रखते हैं!
+
+17
+00:01:01.050 --> 00:01:04.440
+यदि आप अपना गुप्त रिकवरी फ्रेज़ खो देते हैं, तो
+कोई भी, यहां तक कि MetaMask की टीम भी,
+
+18
+00:01:04.440 --> 00:01:07.820
+आपके वॉलेट को पुनर्प्राप्त करने में आपकी
+सहायता नहीं कर सकती है।
+
+19
+00:01:07.820 --> 00:01:12.072
+यदि आपने अपना गुप्त रिकवरी फ्रेज़
+लिखा नहीं है और इसे कहीं सुरक्षित संग्रहीत नहीं किया है,
+
+20
+00:01:12.072 --> 00:01:15.492
+तो अभी करें। हम इंतजार करेंगे।
+
+21
+00:01:15.500 --> 00:01:20.780
+और याद रखें, कभी भी अपना गुप्त रिकवरी फ्रेज़
+किसी के साथ साझा न करें: हमसे भी नहीं।
+
+22
+00:01:20.780 --> 00:01:24.910
+यदि कोई आपसे कभी भी इसे मांगता है, तो
+वे आपके साथ धोखाधड़ी करने की कोशिश कर सकते हैं।
+
+23
+00:01:24.910 --> 00:01:26.250
+बस इतना ही!
+
+24
+00:01:26.250 --> 00:01:31.020
+अब आपको पता चल गया है कि गुप्त रिकवरी फ्रेज़ क्या है
+और अपने वॉलेट को कैसे सकुशल और सुरक्षित रखा जाए।
diff --git a/app/images/videos/recovery-onboarding/subtitles/id.vtt b/app/images/videos/recovery-onboarding/subtitles/id.vtt
new file mode 100644
index 000000000..94761496b
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/id.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+MetaMask adalah cara baru untuk terhubung
+ke situs dan aplikasi.
+
+2
+00:00:04.580 --> 00:00:08.860
+Di situs web tradisional, database sentral
+atau bank bertanggung jawab untuk mengontrol dan
+
+3
+00:00:08.860 --> 00:00:10.179
+memulihkan akun Anda.
+
+4
+00:00:10.179 --> 00:00:15.050
+Tetapi di MetaMask, semua kuasa milik
+pemegang kunci induk.
+
+5
+00:00:15.050 --> 00:00:18.460
+Siapa pun yang memegang kunci tersebut, akan mengontrol akun.
+
+6
+00:00:18.460 --> 00:00:21.110
+Frasa pemulihan rahasia
+adalah "kunci induk" Anda.
+
+7
+00:00:21.110 --> 00:00:26.070
+Ini adalah rangkaian 12 kata yang dibuat
+saat Anda menyiapkan MetaMask pertama kali, yang memungkinkan
+
+8
+00:00:26.070 --> 00:00:30.120
+Anda memulihkan dompet dan dana jika Anda
+kehilangan akses.
+
+9
+00:00:30.120 --> 00:00:33.451
+Penting agar Anda mengamankan
+dompet Anda dengan menjaga
+
+10
+00:00:33.451 --> 00:00:37.510
+frasa pemulihan rahasia
+Anda dengan sangat aman dan sangat rahasia.
+
+11
+00:00:37.510 --> 00:00:41.429
+Jika seseorang mendapatkan aksesnya, mereka akan memiliki
+"kunci induk" ke dompet Anda dan dapat
+
+12
+00:00:41.429 --> 00:00:45.190
+mengakses secara bebas dan mengambil semua dana Anda.
+
+13
+00:00:45.190 --> 00:00:50.109
+Untuk mengamankan dompet MetaMask, Anda pasti ingin
+menyimpan frasa pemulihan rahasia Anda secara aman.
+
+14
+00:00:50.109 --> 00:00:54.930
+Anda dapat menuliskannya, menyembunyikannya di suatu tempat,
+menempatkannya di kotak deposit yang aman
+
+15
+00:00:54.930 --> 00:00:57.729
+atau menggunakan pengelola kata sandi yang aman.
+
+16
+00:00:57.729 --> 00:01:01.050
+Beberapa pengguna bahkan mengukir frasa
+mereka pada pelat logam!
+
+17
+00:01:01.050 --> 00:01:04.440
+Tidak ada seorang pun, bahkan tidak juga tim
+di MetaMask, dapat membantu Anda
+
+18
+00:01:04.440 --> 00:01:07.820
+memulihkan dompet Anda jika Anda menghilangkan
+frasa pemulihan rahasia Anda.
+
+19
+00:01:07.820 --> 00:01:12.072
+Jika belum menuliskan frasa pemulihan rahasia Anda
+dan menyimpannya di suatu tempat yang aman,
+
+20
+00:01:12.072 --> 00:01:15.492
+lakukan sekarang. Kami akan menunggu.
+
+21
+00:01:15.500 --> 00:01:20.780
+Dan ingat, jangan membagikan frasa pemulihan rahasia
+Anda kepada siapa pun: bahkan tidak kepada kami.
+
+22
+00:01:20.780 --> 00:01:24.910
+Jika ada yang menanyakannya,
+mereka akan mencoba menipu Anda.
+
+23
+00:01:24.910 --> 00:01:26.250
+Begitulah!
+
+24
+00:01:26.250 --> 00:01:31.020
+Sekarang, Anda tahu apa itu frasa pemulihan rahasia
+dan cara menjaga dompet Anda tetap aman.
diff --git a/app/images/videos/recovery-onboarding/subtitles/ja.vtt b/app/images/videos/recovery-onboarding/subtitles/ja.vtt
new file mode 100644
index 000000000..e99b3b7a4
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/ja.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+MetaMask は
+をサイトとアプリケーションにつなぐ新たな方法です。
+
+2
+00:00:04.580 --> 00:00:08.860
+従来のウェブサイト上では、中央データベース
+または銀行がアカウントの制御と
+
+3
+00:00:08.860 --> 00:00:10.179
+回復の責任を負います。
+
+4
+00:00:10.179 --> 00:00:15.050
+しかし、MetaMask 上では、全ての権限は
+マスターキーの保持者に属します。
+
+5
+00:00:15.050 --> 00:00:18.460
+当該のキーの保持者が、アカウントを制御します。
+
+6
+00:00:18.460 --> 00:00:21.110
+あなたのシークレット リカバリー フレーズ
+があなたの「マスターキー」です。
+
+7
+00:00:21.110 --> 00:00:26.070
+これは一連の 12 の単語で
+あなたが最初に MetaMask を設定した際に自動生成され、これにより
+
+8
+00:00:26.070 --> 00:00:30.120
+あなたは万が一アクセス出来なくなった場合に
+ウォレットと資金を復元できます。
+
+9
+00:00:30.120 --> 00:00:33.451
+ウォレットの安全性を確保することは非常に重要
+であり、あなたの
+
+10
+00:00:33.451 --> 00:00:37.510
+シークレット リカバリー フレーズ
+を非常に安全かつ秘密に保つことで実現します。
+
+11
+00:00:37.510 --> 00:00:41.429
+誰かがそれにアクセスすれば、彼らは
+あなたのウォレットの「マスターキー」を得て、
+
+12
+00:00:41.429 --> 00:00:45.190
+あなたの資金に自由にアクセスして全てを奪えます。
+
+13
+00:00:45.190 --> 00:00:50.109
+MetaMask ウォレットの安全性を確保するため
+あなたは シークレット リカバリー フレーズを安全に保存したくなるでしょう。
+
+14
+00:00:50.109 --> 00:00:54.930
+それを書き留めたり、どこかへ隠したり、
+セーフティボックスに入れたり
+
+15
+00:00:54.930 --> 00:00:57.729
+または安全確保のためのパスワードマネジャーを使用できます。
+
+16
+00:00:57.729 --> 00:01:01.050
+自分たちの
+フレーズをメタルプレートに彫るユーザーさえいます!
+
+17
+00:01:01.050 --> 00:01:04.440
+何者も、
+MetaMask のチームのメンバーですら、あなたが
+
+18
+00:01:04.440 --> 00:01:07.820
+シークレット リカバリー フレーズを無くしたら
+あなたのウォレットを復元する手助けはできません。
+
+19
+00:01:07.820 --> 00:01:12.072
+あなたがシークレット リカバリー
+フレーズを書き留め安全な場所に保管していないのならば、
+
+20
+00:01:12.072 --> 00:01:15.492
+ぜひ今それを実行してください。お待ちしております。
+
+21
+00:01:15.500 --> 00:01:20.780
+さらに、あなたのシークレット リカバリー
+フレーズを誰とも決して共有しないことを忘れないでください。私たちでさえも。
+
+22
+00:01:20.780 --> 00:01:24.910
+それを尋ねる者がいたら、
+彼らはあなたを騙そうとしているのです。
+
+23
+00:01:24.910 --> 00:01:26.250
+以上です!
+
+24
+00:01:26.250 --> 00:01:31.020
+これでシークレット リカバリ フレーズ
+が何であるか、あなたのウォレットと資金の安全を確保する方法が判りました。
diff --git a/app/images/videos/recovery-onboarding/subtitles/ko.vtt b/app/images/videos/recovery-onboarding/subtitles/ko.vtt
new file mode 100644
index 000000000..68c04d1ae
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/ko.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+MetaMask는 사이트와 애플리케이션에
+연결할 수 있는 새로운 방법입니다.
+
+2
+00:00:04.580 --> 00:00:08.860
+전통적인 웹사이트에서는 중앙 데이터베이스
+또는 은행에게 계정을 제어 및
+
+3
+00:00:08.860 --> 00:00:10.179
+복구할 책임이 있습니다.
+
+4
+00:00:10.179 --> 00:00:15.050
+하지만 MetaMask에서는 모든 권한이
+마스터 키의 소유자에게 있습니다.
+
+5
+00:00:15.050 --> 00:00:18.460
+키를 보유하고 있는 사람은 계정을 제어합니다.
+
+6
+00:00:18.460 --> 00:00:21.110
+계정 시드 구문은
+"마스터 키"입니다.
+
+7
+00:00:21.110 --> 00:00:26.070
+먼저 MetaMask를 설정하면, 일련의
+12단어가 생성되어,
+
+8
+00:00:26.070 --> 00:00:30.120
+접근 권한을 상실했을 때 지갑과
+자금을 복구할 수 있습니다.
+
+9
+00:00:30.120 --> 00:00:33.451
+계정 시드 구문을
+안전하게 비밀을
+
+10
+00:00:33.451 --> 00:00:37.510
+유지하여 지갑을 안전하게
+지키는 것이 중요합니다.
+
+11
+00:00:37.510 --> 00:00:41.429
+계정 시드 구문에 액세스하는 사람에게는
+지갑에 대한 "마스터 키"가 있어 자유롭게
+
+12
+00:00:41.429 --> 00:00:45.190
+액세스하여 모든 자금을 가져갈 수 있습니다.
+
+13
+00:00:45.190 --> 00:00:50.109
+MetaMask 지갑을 안전하게 보호하려면, 계정 시드
+구문을 저장할 수 있습니다.
+
+14
+00:00:50.109 --> 00:00:54.930
+계정 시드 구문을 적어서 어딘가에 숨겨두거나
+대여 금고에 두거나
+
+15
+00:00:54.930 --> 00:00:57.729
+보안 암호 관리자를 사용할 수 있습니다.
+
+16
+00:00:57.729 --> 00:01:01.050
+일부 사용자는 자신의 구문을
+금속판에 새겨두기도 합니다!
+
+17
+00:01:01.050 --> 00:01:04.440
+계정 시드 구문을 잊으면,
+MetaMask의 팀이라고
+
+18
+00:01:04.440 --> 00:01:07.820
+해도 지갑을
+복구할 수 없습니다.
+
+19
+00:01:07.820 --> 00:01:12.072
+게정 시드 구문을 적어두지
+않으면, 안전한 장소에
+
+20
+00:01:12.072 --> 00:01:15.492
+보관하십시오. 기다리겠습니다.
+
+21
+00:01:15.500 --> 00:01:20.780
+다른 사람과 계정 시드 구문을
+고유하면 안 됩니다. 당사하고도 공유하지 마십시오.
+
+22
+00:01:20.780 --> 00:01:24.910
+계정 시드 구문을 요청하는 사람은
+사기를 치려고 하는 것입니다.
+
+23
+00:01:24.910 --> 00:01:26.250
+이제 다 됐습니다.
+
+24
+00:01:26.250 --> 00:01:31.020
+이제 여러분은 계정 시드 구문이 무엇이고
+지갑을 안전하게 보관하는 방법을 알고 있습니다.
diff --git a/app/images/videos/recovery-onboarding/subtitles/pt.vtt b/app/images/videos/recovery-onboarding/subtitles/pt.vtt
new file mode 100644
index 000000000..bebef88e1
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/pt.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+O MetaMask é um novo jeito de se conectar
+a sites e aplicativos.
+
+2
+00:00:04.580 --> 00:00:08.860
+Em websites tradicionais, um banco ou base de dados central
+é responsável por controlar e
+
+3
+00:00:08.860 --> 00:00:10.179
+recuperar as suas contas.
+
+4
+00:00:10.179 --> 00:00:15.050
+Mas, no MetaMask, todo o poder pertence
+ao titular de uma chave-mestra.
+
+5
+00:00:15.050 --> 00:00:18.460
+Quem quer que detenha a chave controla as contas.
+
+6
+00:00:18.460 --> 00:00:21.110
+A sua frase de recuperação secreta
+é a sua "chave-mestra".
+
+7
+00:00:21.110 --> 00:00:26.070
+É uma série de 12 palavras que são geradas
+quando você configura o MetaMask na primeira vez, o que permite que
+
+8
+00:00:26.070 --> 00:00:30.120
+você recupere a sua carteira e recursos, caso você
+venha a perder o acesso.
+
+9
+00:00:30.120 --> 00:00:33.451
+É importante que você mantenha protegida
+a sua carteira ao manter a sua
+
+10
+00:00:33.451 --> 00:00:37.510
+frase de recuperação secreta
+muito segura e muito secreta.
+
+11
+00:00:37.510 --> 00:00:41.429
+Caso alguém obtenha acesso a ela, essa pessoa terá
+a "chave-mestra" para a sua carteira e poderá
+
+12
+00:00:41.429 --> 00:00:45.190
+acessá-la livremente e tome todos os seus recursos.
+
+13
+00:00:45.190 --> 00:00:50.109
+A fim de proteger a sua carteira MetaMask, você desejará
+manter em segurança a sua frase de recuperação secreta.
+
+14
+00:00:50.109 --> 00:00:54.930
+Você pode escrevê-la, escondê-la em algum lugar,
+colocá-la em um cofre
+
+15
+00:00:54.930 --> 00:00:57.729
+ou usar um gerenciador de senhas seguras.
+
+16
+00:00:57.729 --> 00:01:01.050
+Alguns usuários até mesmo gravam sua
+frase em uma placa de metal!
+
+17
+00:01:01.050 --> 00:01:04.440
+Ninguém, nem mesmo a equipe
+na MetaMask, pode lhe ajudar
+
+18
+00:01:04.440 --> 00:01:07.820
+a recuperar a sua carteira, caso você perca
+a sua frase de recuperação secreta.
+
+19
+00:01:07.820 --> 00:01:12.072
+Caso você não tenha escrito a sua frase de recuperação
+secreta e a tenha armazenado em algum lugar seguro,
+
+20
+00:01:12.072 --> 00:01:15.492
+faça isso agora. Iremos aguardar.
+
+21
+00:01:15.500 --> 00:01:20.780
+E lembre-se de jamais compartilhar a sua frase de recuperação
+secreta com ninguém: nem mesmo conosco.
+
+22
+00:01:20.780 --> 00:01:24.910
+Caso alguém venha a lhe pedir a sua frase de recuperação secreta,
+essa pessoa está tentando dar um golpe em você.
+
+23
+00:01:24.910 --> 00:01:26.250
+É isso!
+
+24
+00:01:26.250 --> 00:01:31.020
+Agora, você sabe o que é uma frase de recuperação secreta
+e como manter a sua carteira protegida e segura.
diff --git a/app/images/videos/recovery-onboarding/subtitles/ru.vtt b/app/images/videos/recovery-onboarding/subtitles/ru.vtt
new file mode 100644
index 000000000..46c165a6a
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/ru.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+MetaMask — это новый способ подключения
+к сайтам и приложениям.
+
+2
+00:00:04.580 --> 00:00:08.860
+На традиционных сайтах центральная база данных
+или банк несет ответственность за контроль и
+
+3
+00:00:08.860 --> 00:00:10.179
+восстановление ваших счетов.
+
+4
+00:00:10.179 --> 00:00:15.050
+На MetaMask все полномочия находятся
+в руках владельца мастер-ключа.
+
+5
+00:00:15.050 --> 00:00:18.460
+Тот, в чьих руках находится ключ, контролирует счета.
+
+6
+00:00:18.460 --> 00:00:21.110
+Ваша секретная фраза восстановления
+— это ваш «мастер-ключ».
+
+7
+00:00:21.110 --> 00:00:26.070
+Это набор из 12 слов, которые генерируются
+при первой настройке MetaMask, он позволяет
+
+8
+00:00:26.070 --> 00:00:30.120
+вам восстанавливать ваш кошелек и средства, если вы
+теряете к ним доступ.
+
+9
+00:00:30.120 --> 00:00:33.451
+Важно, чтобы вы обезопасили
+свой кошелек, храня вашу
+
+10
+00:00:33.451 --> 00:00:37.510
+секретную фразу восстановления
+в очень надежном и тайном месте.
+
+11
+00:00:37.510 --> 00:00:41.429
+Если кто-то получит доступ к ней, у этого человека окажется в руках
+«мастер-ключ» от вашего кошелька, и он сможет
+
+12
+00:00:41.429 --> 00:00:45.190
+распоряжаться им и завладеть всеми вашими средствами.
+
+13
+00:00:45.190 --> 00:00:50.109
+Чтобы обезопасить ваш кошелек MetaMask,
+сохраните секретную фразу восстановления в безопасном месте.
+
+14
+00:00:50.109 --> 00:00:54.930
+Вы можете записать ее, спрятать ее где-то,
+положить ее в банковский сейф
+
+15
+00:00:54.930 --> 00:00:57.729
+или воспользоваться безопасным диспетчером паролей.
+
+16
+00:00:57.729 --> 00:01:01.050
+Некоторые пользователи даже гравируют свою
+фразу на металлической пластине!
+
+17
+00:01:01.050 --> 00:01:04.440
+Никто, даже команда
+MetaMask, не сможет помочь вам
+
+18
+00:01:04.440 --> 00:01:07.820
+восстановить ваш кошелек, если вы потеряете
+вашу секретную фразу восстановления.
+
+19
+00:01:07.820 --> 00:01:12.072
+Если вы еще не записали секретную фразу
+восстановления и не поместили ее в надежное место,
+
+20
+00:01:12.072 --> 00:01:15.492
+сделайте это сейчас. Мы подождем.
+
+21
+00:01:15.500 --> 00:01:20.780
+И помните, никогда не сообщайте свою секретную фразу
+восстановления никому: даже нам.
+
+22
+00:01:20.780 --> 00:01:24.910
+Если кто-нибудь когда-либо спросит у вас ее,
+этот человек пытается вас обмануть.
+
+23
+00:01:24.910 --> 00:01:26.250
+Вот и все!
+
+24
+00:01:26.250 --> 00:01:31.020
+Теперь вы знаете, что такое секретная фраза восстановления
+и как обезопасить ваш кошелек.
diff --git a/app/images/videos/recovery-onboarding/subtitles/tl.vtt b/app/images/videos/recovery-onboarding/subtitles/tl.vtt
new file mode 100644
index 000000000..cfb3ebca7
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/tl.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+Ang MetaMask ay isang bagong paraan para kumonekta
+sa mga site at application.
+
+2
+00:00:04.580 --> 00:00:08.860
+Sa mga tradisyonal na website, ang isang central database
+o bangko ang magiging responsable sa pagkontrol at
+
+3
+00:00:08.860 --> 00:00:10.179
+pag-recover ng iyong mga account.
+
+4
+00:00:10.179 --> 00:00:15.050
+Pero sa MetaMask, ang lahat ng kakayahan ay nasa
+may hawak ng master key.
+
+5
+00:00:15.050 --> 00:00:18.460
+Kung sino man ang may hawak ng key, siya ang magkokontrol sa mga account.
+
+6
+00:00:18.460 --> 00:00:21.110
+Ang iyong lihim na recovery phrase
+ay ang iyong "master key".
+
+7
+00:00:21.110 --> 00:00:26.070
+Isa itong 12 salita na nagagawa
+sa unang pagkakataong i-set up mo ang MetaMask, na magbibigay-daan sa iyo
+
+8
+00:00:26.070 --> 00:00:30.120
+na maibalik ang iyong wallet at pera kung sakaling
+mawalan ka ng access.
+
+9
+00:00:30.120 --> 00:00:33.451
+Mahalagang i-secure
+ang iyong wallet sa pamamagitan ng pagpapanatiling sobrang ligtas at walang nakakaalam ng iyong
+
+10
+00:00:33.451 --> 00:00:37.510
+lihim na recovery phrase
+.
+
+11
+00:00:37.510 --> 00:00:41.429
+Kung may ibang taong makaka-access nito, makukuha nila
+ang "master key" sa iyong wallet at
+
+12
+00:00:41.429 --> 00:00:45.190
+madali nilang maa-access at makukuha ang lahat ng pera mo.
+
+13
+00:00:45.190 --> 00:00:50.109
+Para ma-secure ang iyong MetaMask wallet,
+ligtas na i-save ang iyong lihim na recovery phrase.
+
+14
+00:00:50.109 --> 00:00:54.930
+Puwede mo itong isulat, itago,
+ilagay sa isang safe deposit box
+
+15
+00:00:54.930 --> 00:00:57.729
+o kaya ay gumamit ng ligtas na password manager.
+
+16
+00:00:57.729 --> 00:01:01.050
+Ang ilang user nga ay inuukit pa ang kanilang
+phrase sa isang metal plate!
+
+17
+00:01:01.050 --> 00:01:04.440
+Walang sinuman, maging ang team
+sa MetaMask, ang makakatulong sa iyong
+
+18
+00:01:04.440 --> 00:01:07.820
+maibalik ang wallet mo kung maiwawala mo
+iyong lihim na recovery phrase.
+
+19
+00:01:07.820 --> 00:01:12.072
+Kung hindi mo pa naisusulat ang iyong lihim na recovery
+phrase at hindi pa naitatago sa ligtas na lugar,
+
+20
+00:01:12.072 --> 00:01:15.492
+gawin mo na ngayon. Hihintayin ka namin.
+
+21
+00:01:15.500 --> 00:01:20.780
+At tandaan, huwag kailanman ipaalam sa iba ang iyong lihim na recovery
+phrase: maging sa amin.
+
+22
+00:01:20.780 --> 00:01:24.910
+Kung may magtatanong man sa iyo,
+sinusubukan ka nilang i-scam.
+
+23
+00:01:24.910 --> 00:01:26.250
+´Yun lang!
+
+24
+00:01:26.250 --> 00:01:31.020
+Ngayon ay alam mo na kung ano ang lihim na recovery phrase
+at kung paano mapapanatiling ligtas ang iyong wallet.
diff --git a/app/images/videos/recovery-onboarding/subtitles/vi.vtt b/app/images/videos/recovery-onboarding/subtitles/vi.vtt
new file mode 100644
index 000000000..a05e97d85
--- /dev/null
+++ b/app/images/videos/recovery-onboarding/subtitles/vi.vtt
@@ -0,0 +1,115 @@
+WEBVTT
+
+1
+00:00:00.780 --> 00:00:04.580
+MetaMask là cách thức mới để kết nối
+với các trang web và ứng dụng.
+
+2
+00:00:04.580 --> 00:00:08.860
+Trên các trang web truyền thống, một cơ sở dữ liệu trung tâm
+hay ngân hàng sẽ chịu trách nhiệm kiểm soát và
+
+3
+00:00:08.860 --> 00:00:10.179
+khôi phục các tài khoản của bạn.
+
+4
+00:00:10.179 --> 00:00:15.050
+Tuy nhiên, trên MetaMask, toàn bộ quyền sẽ thuộc về
+người nắm giữ khóa chính.
+
+5
+00:00:15.050 --> 00:00:18.460
+Người có khóa chính sẽ kiểm soát được tài khoản.
+
+6
+00:00:18.460 --> 00:00:21.110
+Cụm mật khẩu khôi phục bí mật
+là “khóa chính” của bạn.
+
+7
+00:00:21.110 --> 00:00:26.070
+Đây là chuỗi gồm 12 từ được tạo
+vào lần đầu tiên bạn thiết lập MetaMask, chuỗi này cho phép
+
+8
+00:00:26.070 --> 00:00:30.120
+bạn khôi phục ví và tiền của mình nếu
+bạn bị mất quyền truy cập.
+
+9
+00:00:30.120 --> 00:00:33.451
+Bạn cần phải bảo vệ an toàn cho
+ví của mình bằng cách lưu giữ
+
+10
+00:00:33.451 --> 00:00:37.510
+cụm mật khẩu khôi phục bí mật
+thật an toàn và bí mật.
+
+11
+00:00:37.510 --> 00:00:41.429
+Nếu ai đó có được cụm mật khẩu khôi phục bí mật của bạn thì người đó sẽ có
+“khóa chính” cho ví của bạn và có thể
+
+12
+00:00:41.429 --> 00:00:45.190
+tự do truy cập và lấy toàn bộ tiền của bạn.
+
+13
+00:00:45.190 --> 00:00:50.109
+Để bảo vệ an toàn cho ví MetaMask, bạn cần
+lưu giữ cụm mật khẩu khôi phục bí mật một cách an toàn.
+
+14
+00:00:50.109 --> 00:00:54.930
+Bạn có thể chép lại và giấu ở một nơi nào đó,
+cất trong hộp ký gửi an toàn
+
+15
+00:00:54.930 --> 00:00:57.729
+hoặc dùng một trình quản lý mật khẩu an toàn.
+
+16
+00:00:57.729 --> 00:01:01.050
+Một số người dùng thậm chí còn khắc
+cụm mật khẩu của họ lên một tấm kim loại!
+
+17
+00:01:01.050 --> 00:01:04.440
+Không một ai, kể cả đội ngũ
+tại MetaMask, có thể giúp bạn
+
+18
+00:01:04.440 --> 00:01:07.820
+khôi phục lại ví nếu bạn đánh mất
+cụm mật khẩu khôi phục bí mật của mình.
+
+19
+00:01:07.820 --> 00:01:12.072
+Nếu chưa ghi lại cụm mật khẩu khôi phục bí mật
+của mình và lưu giữ ở nơi an toàn,
+
+20
+00:01:12.072 --> 00:01:15.492
+thì bạn hãy thực hiện ngay bây giờ. Chúng tôi sẽ chờ bạn.
+
+21
+00:01:15.500 --> 00:01:20.780
+Và đừng bao giờ chia sẻ cụm mật khẩu khôi phục
+bí mật với bất kỳ ai: kể cả chúng tôi.
+
+22
+00:01:20.780 --> 00:01:24.910
+Nếu ai đó hỏi bạn cụm mật khẩu khôi phục bí mật,
+thì họ đang cố gắng lừa đảo bạn.
+
+23
+00:01:24.910 --> 00:01:26.250
+Xin hãy ghi nhớ!
+
+24
+00:01:26.250 --> 00:01:31.020
+Bây giờ bạn đã biết cụm mật khẩu khôi phục bí mật
+là gì và cách bảo vệ ví của bạn an toàn và bảo mật.
diff --git a/app/manifest/chrome.json b/app/manifest/chrome.json
index 281c847a4..e4bb01cdd 100644
--- a/app/manifest/chrome.json
+++ b/app/manifest/chrome.json
@@ -3,5 +3,5 @@
"matches": ["https://metamask.io/*"],
"ids": ["*"]
},
- "minimum_chrome_version": "63"
+ "minimum_chrome_version": "66"
}
diff --git a/app/phishing.html b/app/phishing.html
index 5460447b9..dd180fb77 100644
--- a/app/phishing.html
+++ b/app/phishing.html
@@ -51,7 +51,7 @@
Ethereum Phishing Detector.
Domains on these warning lists may include outright malicious websites and legitimate websites that have been compromised by a malicious actor.
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.
@@ -60,7 +60,7 @@
If you think this domain is incorrectly flagged or if a blocked legitimate website has resolved its security issues,
- please file an issue.
+ please file an issue.
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 56044213c..d8d99150f 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -2,9 +2,6 @@
* @file The entry point for the web extension singleton process.
*/
-// polyfills
-import 'abortcontroller-polyfill/dist/polyfill-patch-fetch';
-
import endOfStream from 'end-of-stream';
import pump from 'pump';
import debounce from 'debounce-stream';
@@ -435,12 +432,16 @@ function setupController(initState, initLangCode) {
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
- controller.approvalController.subscribe(updateBadge);
controller.appStateController.on(
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
updateBadge,
);
+ controller.controllerMessenger.subscribe(
+ METAMASK_CONTROLLER_EVENTS.APPROVAL_STATE_CHANGE,
+ updateBadge,
+ );
+
/**
* Updates the Web Extension's "badge" number, on the little fox in the toolbar.
* The number reflects the current number of pending transactions or message signatures needing user approval.
diff --git a/app/scripts/controllers/permissions/enums.js b/app/scripts/controllers/permissions/enums.js
index 447040bed..4901140a1 100644
--- a/app/scripts/controllers/permissions/enums.js
+++ b/app/scripts/controllers/permissions/enums.js
@@ -40,6 +40,7 @@ export const SAFE_METHODS = [
'eth_coinbase',
'eth_decrypt',
'eth_estimateGas',
+ 'eth_feeHistory',
'eth_gasPrice',
'eth_getBalance',
'eth_getBlockByHash',
diff --git a/app/scripts/controllers/permissions/index.js b/app/scripts/controllers/permissions/index.js
index 281c4e8a3..061782c23 100644
--- a/app/scripts/controllers/permissions/index.js
+++ b/app/scripts/controllers/permissions/index.js
@@ -249,7 +249,7 @@ export class PermissionsController {
approved.permissions,
accounts,
);
- this.approvals.resolve(id, approved.permissions);
+ this.approvals.accept(id, approved.permissions);
}
} catch (err) {
// if finalization fails, reject the request
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index bd2c91a09..11b38a478 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -816,7 +816,6 @@ export default class PreferencesController {
return await tokenContract
.supportsInterface(ERC721_INTERFACE_ID)
.catch((error) => {
- console.log('error', error);
log.debug(error);
return false;
});
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index 5a1d72611..5fe5cc4e6 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -1383,6 +1383,10 @@ export default class TransactionController extends EventEmitter {
* @param {Object} extraParams - optional props and values to include in sensitiveProperties
*/
_trackTransactionMetricsEvent(txMeta, event, extraParams = {}) {
+ if (!txMeta) {
+ return;
+ }
+
const {
type,
time,
diff --git a/app/scripts/controllers/transactions/pending-tx-tracker.js b/app/scripts/controllers/transactions/pending-tx-tracker.js
index 52e686cf6..96e7d9793 100644
--- a/app/scripts/controllers/transactions/pending-tx-tracker.js
+++ b/app/scripts/controllers/transactions/pending-tx-tracker.js
@@ -136,8 +136,8 @@ export default class PendingTransactionTracker extends EventEmitter {
const retryCount = txMeta.retryCount || 0;
- // Exponential backoff to limit retries at publishing
- if (txBlockDistance <= Math.pow(2, retryCount) - 1) {
+ // Exponential backoff to limit retries at publishing (capped at ~15 minutes between retries)
+ if (txBlockDistance < Math.min(50, Math.pow(2, retryCount))) {
return undefined;
}
diff --git a/app/scripts/lib/ComposableObservableStore.js b/app/scripts/lib/ComposableObservableStore.js
index 789a12d80..1563ae548 100644
--- a/app/scripts/lib/ComposableObservableStore.js
+++ b/app/scripts/lib/ComposableObservableStore.js
@@ -1,4 +1,5 @@
import { ObservableStore } from '@metamask/obs-store';
+import { getPersistentState } from '@metamask/controllers';
/**
* @typedef {import('@metamask/controllers').ControllerMessenger} ControllerMessenger
@@ -27,9 +28,11 @@ export default class ComposableObservableStore extends ObservableStore {
* messenger, used for subscribing to events from BaseControllerV2-based
* controllers.
* @param {Object} [options.state] - The initial store state
+ * @param {boolean} [options.persist] - Wether or not to apply the persistence for v2 controllers
*/
- constructor({ config, controllerMessenger, state }) {
+ constructor({ config, controllerMessenger, state, persist }) {
super(state);
+ this.persist = persist;
this.controllerMessenger = controllerMessenger;
if (config) {
this.updateStructure(config);
@@ -60,7 +63,11 @@ export default class ComposableObservableStore extends ObservableStore {
this.controllerMessenger.subscribe(
`${store.name}:stateChange`,
(state) => {
- this.updateState({ [key]: state });
+ let updatedState = state;
+ if (this.persist) {
+ updatedState = getPersistentState(state, config[key].metadata);
+ }
+ this.updateState({ [key]: updatedState });
},
);
}
diff --git a/app/scripts/lockdown-run.js b/app/scripts/lockdown-run.js
index f0682654e..3c7276fdf 100644
--- a/app/scripts/lockdown-run.js
+++ b/app/scripts/lockdown-run.js
@@ -12,9 +12,103 @@ try {
// If the `lockdown` call throws an exception, it interferes with the
// contentscript injection on some versions of Firefox. The error is
// caught and logged here so that the contentscript still gets injected.
- // This affects Firefox v56 and Waterfox Classic
+ // This affects Firefox v56 and Waterfox Classic.
console.error('Lockdown failed:', error);
if (globalThis.sentry && globalThis.sentry.captureException) {
- globalThis.sentry.captureException(error);
+ globalThis.sentry.captureException(
+ new Error(`Lockdown failed: ${error.message}`),
+ );
+ }
+}
+
+// Make all "object" and "function" own properties of globalThis
+// non-configurable and non-writable, when possible.
+// We call the a property that is non-configurable and non-writable,
+// "non-modifiable".
+try {
+ /**
+ * `lockdown` only hardens the properties enumerated by the
+ * universalPropertyNames constant specified in 'ses/src/whitelist'. This
+ * function makes all function and object properties on the start compartment
+ * global non-configurable and non-writable, unless they are already
+ * non-configurable.
+ *
+ * It is critical that this function runs at the right time during
+ * initialization, which should always be immediately after `lockdown` has been
+ * called. At the time of writing, the modifications this function makes to the
+ * runtime environment appear to be non-breaking, but that could change with
+ * the addition of dependencies, or the order of our scripts in our HTML files.
+ * Exercise caution.
+ *
+ * See inline comments for implementation details.
+ *
+ * We write this function in IIFE format to avoid polluting global scope.
+ */
+ (function protectIntrinsics() {
+ const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis);
+
+ // These named intrinsics are not automatically hardened by `lockdown`
+ const shouldHardenManually = new Set(['eval', 'Function']);
+
+ const globalProperties = new Set([
+ // universalPropertyNames is a constant added by lockdown to global scope
+ // at the time of writing, it is initialized in 'ses/src/whitelist'.
+ // These properties tend to be non-enumerable.
+ ...namedIntrinsics,
+
+ // TODO: Also include the named platform globals
+ // This grabs every enumerable property on globalThis.
+ // ...Object.keys(globalThis),
+ ]);
+
+ globalProperties.forEach((propertyName) => {
+ const descriptor = Reflect.getOwnPropertyDescriptor(
+ globalThis,
+ propertyName,
+ );
+
+ if (descriptor) {
+ if (descriptor.configurable) {
+ // If the property on globalThis is configurable, make it
+ // non-configurable. If it has no accessor properties, also make it
+ // non-writable.
+ if (hasAccessor(descriptor)) {
+ Object.defineProperty(globalThis, propertyName, {
+ configurable: false,
+ });
+ } else {
+ Object.defineProperty(globalThis, propertyName, {
+ configurable: false,
+ writable: false,
+ });
+ }
+ }
+
+ if (shouldHardenManually.has(propertyName)) {
+ harden(globalThis[propertyName]);
+ }
+ }
+ });
+
+ /**
+ * Checks whether the given propertyName descriptor has any accessors, i.e. the
+ * properties `get` or `set`.
+ *
+ * We want to make globals non-writable, and we can't set the `writable`
+ * property and accessor properties at the same time.
+ *
+ * @param {Object} descriptor - The propertyName descriptor to check.
+ * @returns {boolean} Whether the propertyName descriptor has any accessors.
+ */
+ function hasAccessor(descriptor) {
+ return 'set' in descriptor || 'get' in descriptor;
+ }
+ })();
+} catch (error) {
+ console.error('Protecting intrinsics failed:', error);
+ if (globalThis.sentry && globalThis.sentry.captureException) {
+ globalThis.sentry.captureException(
+ new Error(`Protecting intrinsics failed: ${error.message}`),
+ );
}
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 115110620..17405884c 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -76,6 +76,8 @@ export const METAMASK_CONTROLLER_EVENTS = {
// Fired after state changes that impact the extension badge (unapproved msg count)
// The process of updating the badge happens in app/scripts/background.js.
UPDATE_BADGE: 'updateBadge',
+ // TODO: Add this and similar enums to @metamask/controllers and export them
+ APPROVAL_STATE_CHANGE: 'ApprovalController:stateChange',
};
export default class MetamaskController extends EventEmitter {
@@ -106,12 +108,13 @@ export default class MetamaskController extends EventEmitter {
this.getRequestAccountTabIds = opts.getRequestAccountTabIds;
this.getOpenMetamaskTabsIds = opts.getOpenMetamaskTabsIds;
- const controllerMessenger = new ControllerMessenger();
+ this.controllerMessenger = new ControllerMessenger();
// observable state store
this.store = new ComposableObservableStore({
state: initState,
- controllerMessenger,
+ controllerMessenger: this.controllerMessenger,
+ persist: true,
});
// external connections by origin
@@ -131,6 +134,9 @@ export default class MetamaskController extends EventEmitter {
// controller initialization order matters
this.approvalController = new ApprovalController({
+ messenger: this.controllerMessenger.getRestricted({
+ name: 'ApprovalController',
+ }),
showApprovalRequest: opts.showUserConfirmation,
});
@@ -169,7 +175,7 @@ export default class MetamaskController extends EventEmitter {
initState: initState.MetaMetricsController,
});
- const gasFeeMessenger = controllerMessenger.getRestricted({
+ const gasFeeMessenger = this.controllerMessenger.getRestricted({
name: 'GasFeeController',
});
@@ -210,7 +216,7 @@ export default class MetamaskController extends EventEmitter {
preferencesStore: this.preferencesController.store,
});
- const currencyRateMessenger = controllerMessenger.getRestricted({
+ const currencyRateMessenger = this.controllerMessenger.getRestricted({
name: 'CurrencyRateController',
});
this.currencyRateController = new CurrencyRateController({
@@ -219,7 +225,7 @@ export default class MetamaskController extends EventEmitter {
state: initState.CurrencyController,
});
- const tokenListMessenger = controllerMessenger.getRestricted({
+ const tokenListMessenger = this.controllerMessenger.getRestricted({
name: 'TokenListController',
});
this.tokenListController = new TokenListController({
@@ -569,7 +575,7 @@ export default class MetamaskController extends EventEmitter {
GasFeeController: this.gasFeeController,
TokenListController: this.tokenListController,
},
- controllerMessenger,
+ controllerMessenger: this.controllerMessenger,
});
this.memStore.subscribe(this.sendUpdate.bind(this));
@@ -1097,7 +1103,7 @@ export default class MetamaskController extends EventEmitter {
// approval controller
resolvePendingApproval: nodeify(
- approvalController.resolve,
+ approvalController.accept,
approvalController,
),
rejectPendingApproval: nodeify(
diff --git a/app/scripts/migrations/053.js b/app/scripts/migrations/053.js
index f9d4dc55c..318ce1614 100644
--- a/app/scripts/migrations/053.js
+++ b/app/scripts/migrations/053.js
@@ -23,23 +23,27 @@ function transformState(state) {
state?.IncomingTransactionsController?.incomingTransactions;
if (Array.isArray(transactions)) {
transactions.forEach((transaction) => {
- if (
- transaction.type !== TRANSACTION_TYPES.RETRY &&
- transaction.type !== TRANSACTION_TYPES.CANCEL
- ) {
- transaction.type = transaction.transactionCategory;
+ if (transaction) {
+ if (
+ transaction.type !== TRANSACTION_TYPES.RETRY &&
+ transaction.type !== TRANSACTION_TYPES.CANCEL
+ ) {
+ transaction.type = transaction.transactionCategory;
+ }
+ delete transaction.transactionCategory;
}
- delete transaction.transactionCategory;
});
}
if (incomingTransactions) {
const incomingTransactionsEntries = Object.entries(incomingTransactions);
incomingTransactionsEntries.forEach(([key, transaction]) => {
- delete transaction.transactionCategory;
- state.IncomingTransactionsController.incomingTransactions[key] = {
- ...transaction,
- type: TRANSACTION_TYPES.INCOMING,
- };
+ if (transaction) {
+ delete transaction.transactionCategory;
+ state.IncomingTransactionsController.incomingTransactions[key] = {
+ ...transaction,
+ type: TRANSACTION_TYPES.INCOMING,
+ };
+ }
});
}
return state;
diff --git a/app/scripts/migrations/056.js b/app/scripts/migrations/056.js
index f11d4b3f1..70300f363 100644
--- a/app/scripts/migrations/056.js
+++ b/app/scripts/migrations/056.js
@@ -15,14 +15,14 @@ export default {
const { PreferencesController } = versionedData.data;
- if (Array.isArray(PreferencesController.tokens)) {
+ if (Array.isArray(PreferencesController?.tokens)) {
PreferencesController.tokens = PreferencesController.tokens.filter(
({ address }) => address,
);
}
if (
- PreferencesController.accountTokens &&
+ PreferencesController?.accountTokens &&
typeof PreferencesController.accountTokens === 'object'
) {
Object.keys(PreferencesController.accountTokens).forEach((account) => {
@@ -40,7 +40,7 @@ export default {
}
if (
- PreferencesController.assetImages &&
+ PreferencesController?.assetImages &&
'undefined' in PreferencesController.assetImages
) {
delete PreferencesController.assetImages.undefined;
diff --git a/app/scripts/phishing-detect.js b/app/scripts/phishing-detect.js
index f036461c3..bb6bf5b2e 100644
--- a/app/scripts/phishing-detect.js
+++ b/app/scripts/phishing-detect.js
@@ -12,7 +12,12 @@ function start() {
const hash = window.location.hash.substring(1);
const suspect = querystring.parse(hash);
- document.getElementById('csdbLink').href = `https://cryptoscamdb.org/search`;
+ const newIssueLink = document.getElementById('new-issue-link');
+ const newIssueUrl = `https://github.com/MetaMask/eth-phishing-detect/issues/new`;
+ const newIssueParams = `?title=[Legitimate%20Site%20Blocked]%20${encodeURIComponent(
+ suspect.hostname,
+ )}&body=${encodeURIComponent(suspect.href)}`;
+ newIssueLink.href = `${newIssueUrl}${newIssueParams}`;
global.platform = new ExtensionPlatform();
diff --git a/app/scripts/ui.js b/app/scripts/ui.js
index ee4539370..362567aa8 100644
--- a/app/scripts/ui.js
+++ b/app/scripts/ui.js
@@ -1,5 +1,4 @@
// polyfills
-import 'abortcontroller-polyfill/dist/polyfill-patch-fetch';
import '@formatjs/intl-relativetimeformat/polyfill';
// dev only, "react-devtools" import is skipped in prod builds
diff --git a/babel.config.js b/babel.config.js
index 6c98e6df4..efd474696 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -6,7 +6,7 @@ module.exports = function (api) {
'@babel/preset-env',
{
targets: {
- browsers: ['chrome >= 63', 'firefox >= 68'],
+ browsers: ['chrome >= 66', 'firefox >= 68'],
},
},
],
diff --git a/development/build/index.js b/development/build/index.js
index 9a60ead28..452b6bce0 100755
--- a/development/build/index.js
+++ b/development/build/index.js
@@ -28,12 +28,17 @@ require('@babel/preset-react');
require('@babel/core');
const browserPlatforms = ['firefox', 'chrome', 'brave', 'opera'];
+const shouldIncludeLockdown = !process.argv.includes('--omit-lockdown');
defineAllTasks();
detectAndRunEntryTask();
function defineAllTasks() {
- const staticTasks = createStaticAssetTasks({ livereload, browserPlatforms });
+ const staticTasks = createStaticAssetTasks({
+ livereload,
+ browserPlatforms,
+ shouldIncludeLockdown,
+ });
const manifestTasks = createManifestTasks({ browserPlatforms });
const styleTasks = createStyleTasks({ livereload });
const scriptTasks = createScriptTasks({ livereload, browserPlatforms });
diff --git a/development/build/static.js b/development/build/static.js
index 9d6ba3060..ed7e9290a 100644
--- a/development/build/static.js
+++ b/development/build/static.js
@@ -7,107 +7,17 @@ const locales = require('../../app/_locales/index.json');
const { createTask, composeSeries } = require('./task');
-module.exports = createStaticAssetTasks;
+const EMPTY_JS_FILE = './development/empty.js';
-const copyTargets = [
- {
- src: `./app/_locales/`,
- dest: `_locales`,
- },
- {
- src: `./app/images/`,
- dest: `images`,
- },
- {
- src: `./node_modules/@metamask/contract-metadata/images/`,
- dest: `images/contract`,
- },
- {
- src: `./app/fonts/`,
- dest: `fonts`,
- },
- {
- src: `./app/vendor/`,
- dest: `vendor`,
- },
- {
- src: `./node_modules/@fortawesome/fontawesome-free/webfonts/`,
- dest: `fonts/fontawesome`,
- },
- {
- src: `./ui/css/output/`,
- pattern: `*.css`,
- dest: ``,
- },
- {
- src: `./app/loading.html`,
- dest: `loading.html`,
- },
- {
- src: `./node_modules/globalthis/dist/browser.js`,
- dest: `globalthis.js`,
- },
- {
- src: `./node_modules/ses/dist/lockdown.cjs`,
- dest: `lockdown-install.js`,
- },
- {
- src: `./app/scripts/lockdown-run.js`,
- dest: `lockdown-run.js`,
- },
- {
- // eslint-disable-next-line node/no-extraneous-require
- src: require.resolve('@lavamoat/lavapack/src/runtime-cjs.js'),
- dest: `runtime-cjs.js`,
- },
- {
- src: `./app/phishing.html`,
- dest: `phishing.html`,
- },
-];
+module.exports = function createStaticAssetTasks({
+ livereload,
+ browserPlatforms,
+ shouldIncludeLockdown = true,
+}) {
+ const [copyTargetsProd, copyTargetsDev] = getCopyTargets(
+ shouldIncludeLockdown,
+ );
-const languageTags = new Set();
-for (const locale of locales) {
- const { code } = locale;
- const tag = code.split('_')[0];
- languageTags.add(tag);
-}
-
-for (const tag of languageTags) {
- copyTargets.push({
- src: `./node_modules/@formatjs/intl-relativetimeformat/dist/locale-data/${tag}.json`,
- dest: `intl/${tag}/relative-time-format-data.json`,
- });
-}
-
-const copyTargetsDev = [
- ...copyTargets,
- {
- src: './development',
- pattern: '/chromereload.js',
- dest: ``,
- },
- // empty files to suppress missing file errors
- {
- src: './development/empty.js',
- dest: `bg-libs.js`,
- },
- {
- src: './development/empty.js',
- dest: `ui-libs.js`,
- },
-];
-
-const copyTargetsProd = [
- ...copyTargets,
- // empty files to suppress missing file errors
- {
- src: './development/empty.js',
- dest: `chromereload.js`,
- },
-];
-
-function createStaticAssetTasks({ livereload, browserPlatforms }) {
const prod = createTask(
'static:prod',
composeSeries(
@@ -169,4 +79,110 @@ function createStaticAssetTasks({ livereload, browserPlatforms }) {
}),
);
}
+};
+
+function getCopyTargets(shouldIncludeLockdown) {
+ const allCopyTargets = [
+ {
+ src: `./app/_locales/`,
+ dest: `_locales`,
+ },
+ {
+ src: `./app/images/`,
+ dest: `images`,
+ },
+ {
+ src: `./node_modules/@metamask/contract-metadata/images/`,
+ dest: `images/contract`,
+ },
+ {
+ src: `./app/fonts/`,
+ dest: `fonts`,
+ },
+ {
+ src: `./app/vendor/`,
+ dest: `vendor`,
+ },
+ {
+ src: `./node_modules/@fortawesome/fontawesome-free/webfonts/`,
+ dest: `fonts/fontawesome`,
+ },
+ {
+ src: `./ui/css/output/`,
+ pattern: `*.css`,
+ dest: ``,
+ },
+ {
+ src: `./app/loading.html`,
+ dest: `loading.html`,
+ },
+ {
+ src: `./node_modules/globalthis/dist/browser.js`,
+ dest: `globalthis.js`,
+ },
+ {
+ src: shouldIncludeLockdown
+ ? `./node_modules/ses/dist/lockdown.umd.min.js`
+ : EMPTY_JS_FILE,
+ dest: `lockdown-install.js`,
+ },
+ {
+ src: shouldIncludeLockdown
+ ? `./app/scripts/lockdown-run.js`
+ : EMPTY_JS_FILE,
+ dest: `lockdown-run.js`,
+ },
+ {
+ // eslint-disable-next-line node/no-extraneous-require
+ src: require.resolve('@lavamoat/lavapack/src/runtime-cjs.js'),
+ dest: `runtime-cjs.js`,
+ },
+ {
+ src: `./app/phishing.html`,
+ dest: `phishing.html`,
+ },
+ ];
+
+ const languageTags = new Set();
+ for (const locale of locales) {
+ const { code } = locale;
+ const tag = code.split('_')[0];
+ languageTags.add(tag);
+ }
+
+ for (const tag of languageTags) {
+ allCopyTargets.push({
+ src: `./node_modules/@formatjs/intl-relativetimeformat/dist/locale-data/${tag}.json`,
+ dest: `intl/${tag}/relative-time-format-data.json`,
+ });
+ }
+
+ const copyTargetsDev = [
+ ...allCopyTargets,
+ {
+ src: './development',
+ pattern: '/chromereload.js',
+ dest: ``,
+ },
+ // empty files to suppress missing file errors
+ {
+ src: EMPTY_JS_FILE,
+ dest: `bg-libs.js`,
+ },
+ {
+ src: EMPTY_JS_FILE,
+ dest: `ui-libs.js`,
+ },
+ ];
+
+ const copyTargetsProd = [
+ ...allCopyTargets,
+ // empty files to suppress missing file errors
+ {
+ src: EMPTY_JS_FILE,
+ dest: `chromereload.js`,
+ },
+ ];
+
+ return [copyTargetsProd, copyTargetsDev];
}
diff --git a/docs/extension_description/en.txt b/docs/extension_description/en.txt
index 53bc4780d..b52d476cc 100644
--- a/docs/extension_description/en.txt
+++ b/docs/extension_description/en.txt
@@ -5,4 +5,4 @@ The extension injects the Ethereum web3 API into every website's javascript cont
MetaMask also lets the user create and manage their own identities, so when a Dapp wants to perform a transaction and write to the blockchain, the user gets a secure interface to review the transaction, before approving or rejecting it.
Because it adds functionality to the normal browser context, MetaMask requires the permission to read and write to any webpage. You can always "view the source" of MetaMask the way you do any extension, or view the source code on Github:
-https://github.com/MetaMask/metamask-plugin
\ No newline at end of file
+https://github.com/MetaMask/metamask-extension
\ No newline at end of file
diff --git a/package.json b/package.json
index 71d4aa3f1..626ee32f7 100644
--- a/package.json
+++ b/package.json
@@ -48,6 +48,8 @@
"lint:fix": "prettier --write '**/*.json' && eslint . --ext js --cache --fix",
"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: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:\"",
@@ -60,8 +62,8 @@
"devtools:redux": "remotedev --hostname=localhost --port=8000",
"start:dev": "concurrently -k -n build,react,redux yarn:start yarn:devtools:react yarn:devtools:redux",
"announce": "node development/announcer.js",
- "storybook": "start-storybook -p 6006 -c .storybook --static-dir ./app ./storybook/images",
- "storybook:build": "build-storybook -c .storybook -o storybook-build --static-dir ./app ./storybook/images",
+ "storybook": "start-storybook -p 6006 -c .storybook -s ./app,./.storybook/images",
+ "storybook:build": "build-storybook -c .storybook -o storybook-build -s ./app,./.storybook/images",
"storybook:deploy": "storybook-to-ghpages --existing-output-dir storybook-build --remote storybook --branch master",
"update-changelog": "auto-changelog update",
"generate:migration": "./development/generate-migration.sh",
@@ -102,7 +104,7 @@
"@fortawesome/fontawesome-free": "^5.13.0",
"@material-ui/core": "^4.11.0",
"@metamask/contract-metadata": "^1.28.0",
- "@metamask/controllers": "^14.0.2",
+ "@metamask/controllers": "^15.0.2",
"@metamask/eth-ledger-bridge-keyring": "^0.7.0",
"@metamask/eth-token-tracker": "^3.0.1",
"@metamask/etherscan-link": "^2.1.0",
@@ -116,7 +118,6 @@
"@sentry/browser": "^5.26.0",
"@sentry/integrations": "^5.26.0",
"@zxing/library": "^0.8.0",
- "abortcontroller-polyfill": "^1.4.0",
"analytics-node": "^3.4.0-beta.3",
"await-semaphore": "^0.1.1",
"base32-encode": "^1.2.0",
diff --git a/shared/modules/fetch-with-timeout.test.js b/shared/modules/fetch-with-timeout.test.js
index a7400e617..d7b1f2624 100644
--- a/shared/modules/fetch-with-timeout.test.js
+++ b/shared/modules/fetch-with-timeout.test.js
@@ -30,7 +30,9 @@ describe('getFetchWithTimeout', function () {
throw new Error('Request should throw');
};
- await expect(fetchWithTimeoutThrowsError()).rejects.toThrow('Aborted');
+ await expect(fetchWithTimeoutThrowsError()).rejects.toThrow(
+ 'The user aborted a request.',
+ );
});
it('should abort the request when the custom timeout is hit', async function () {
@@ -48,7 +50,9 @@ describe('getFetchWithTimeout', function () {
throw new Error('Request should be aborted');
};
- await expect(fetchWithTimeoutThrowsError()).rejects.toThrow('Aborted');
+ await expect(fetchWithTimeoutThrowsError()).rejects.toThrow(
+ 'The user aborted a request.',
+ );
});
it('throws on invalid timeout', async function () {
diff --git a/test/e2e/benchmark.js b/test/e2e/benchmark.js
index c5ad8ebb7..fe2dc7926 100644
--- a/test/e2e/benchmark.js
+++ b/test/e2e/benchmark.js
@@ -7,7 +7,7 @@ const { hideBin } = require('yargs/helpers');
const ttest = require('ttest');
const { retry } = require('../../development/lib/retry');
const { exitWithError } = require('../../development/lib/exit-with-error');
-const { withFixtures } = require('./helpers');
+const { withFixtures, tinyDelayMs } = require('./helpers');
const { PAGES } = require('./webdriver/driver');
const DEFAULT_NUM_SAMPLES = 20;
@@ -16,6 +16,7 @@ const ALL_PAGES = Object.values(PAGES);
async function measurePage(pageName) {
let metrics;
await withFixtures({ fixtures: 'imported-account' }, async ({ driver }) => {
+ await driver.delay(tinyDelayMs);
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js
index b799f1d53..e5ed3a831 100644
--- a/test/e2e/helpers.js
+++ b/test/e2e/helpers.js
@@ -12,6 +12,8 @@ const { ensureXServerIsRunning } = require('./x-server');
const tinyDelayMs = 200;
const regularDelayMs = tinyDelayMs * 2;
const largeDelayMs = regularDelayMs * 2;
+const xLargeDelayMs = largeDelayMs * 2;
+const xxLargeDelayMs = xLargeDelayMs * 2;
const dappPort = 8080;
@@ -148,5 +150,7 @@ module.exports = {
tinyDelayMs,
regularDelayMs,
largeDelayMs,
+ xLargeDelayMs,
+ xxLargeDelayMs,
withFixtures,
};
diff --git a/test/e2e/metamask-ui.spec.js b/test/e2e/metamask-ui.spec.js
index 73af211d0..2c7044a2b 100644
--- a/test/e2e/metamask-ui.spec.js
+++ b/test/e2e/metamask-ui.spec.js
@@ -191,25 +191,6 @@ describe('MetaMask', function () {
});
});
- describe('Show account information', function () {
- it('shows the QR code for the account', async function () {
- await driver.clickElement('[data-testid="account-options-menu-button"]');
- await driver.clickElement(
- '[data-testid="account-options-menu__account-details"]',
- );
- await driver.findVisibleElement('.qr-code__wrapper');
- await driver.delay(regularDelayMs);
-
- // wait for permission modal to be visible.
- const permissionModal = await driver.findVisibleElement('span .modal');
- await driver.clickElement('.account-modal__close');
-
- // wait for permission modal to be removed from DOM.
- await permissionModal.waitForElementState('hidden');
- await driver.delay(regularDelayMs);
- });
- });
-
describe('Import Secret Recovery Phrase', function () {
it('logs out of the vault', async function () {
await driver.clickElement('.account-menu__icon');
diff --git a/test/e2e/metrics.spec.js b/test/e2e/metrics.spec.js
index fb54eea5d..498a6ccd0 100644
--- a/test/e2e/metrics.spec.js
+++ b/test/e2e/metrics.spec.js
@@ -1,6 +1,6 @@
const { strict: assert } = require('assert');
const waitUntilCalled = require('../lib/wait-until-called');
-const { withFixtures } = require('./helpers');
+const { withFixtures, tinyDelayMs } = require('./helpers');
/**
* WARNING: These tests must be run using a build created with `yarn build:test:metrics`, so that it has
@@ -30,6 +30,7 @@ describe('Segment metrics', function () {
const threeSegmentEventsReceived = waitUntilCalled(segmentStub, null, {
callCount: 3,
});
+ await driver.delay(tinyDelayMs);
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
diff --git a/test/e2e/tests/account-details.spec.js b/test/e2e/tests/account-details.spec.js
new file mode 100644
index 000000000..82f28ad15
--- /dev/null
+++ b/test/e2e/tests/account-details.spec.js
@@ -0,0 +1,38 @@
+const { strict: assert } = require('assert');
+const { withFixtures } = require('../helpers');
+
+describe('Show account details', function () {
+ const ganacheOptions = {
+ accounts: [
+ {
+ secretKey:
+ '0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
+ balance: 25000000000000000000,
+ },
+ ],
+ };
+ it('should show the QR code for the account', async function () {
+ await withFixtures(
+ {
+ 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);
+
+ await driver.clickElement(
+ '[data-testid="account-options-menu-button"]',
+ );
+ await driver.clickElement(
+ '[data-testid="account-options-menu__account-details"]',
+ );
+
+ const qrCode = await driver.findElement('.qr-code__wrapper');
+ assert.equal(await qrCode.isDisplayed(), true);
+ },
+ );
+ });
+});
diff --git a/test/e2e/tests/custom-rpc-history.spec.js b/test/e2e/tests/custom-rpc-history.spec.js
index 792bfa980..08a2bdd31 100644
--- a/test/e2e/tests/custom-rpc-history.spec.js
+++ b/test/e2e/tests/custom-rpc-history.spec.js
@@ -49,7 +49,7 @@ describe('Stores custom RPC history', function () {
await chainIdInput.sendKeys(chainId.toString());
await driver.clickElement('.network-form__footer .btn-secondary');
- await driver.findElement({ text: networkName, tag: 'div' });
+ await driver.findElement({ text: networkName, tag: 'span' });
},
);
});
diff --git a/test/e2e/tests/permissions.spec.js b/test/e2e/tests/permissions.spec.js
index afc860731..503066626 100644
--- a/test/e2e/tests/permissions.spec.js
+++ b/test/e2e/tests/permissions.spec.js
@@ -1,5 +1,5 @@
const { strict: assert } = require('assert');
-const { withFixtures } = require('../helpers');
+const { withFixtures, xxLargeDelayMs } = require('../helpers');
describe('Permissions', function () {
it('sets permissions and connect to Dapp', async function () {
@@ -34,6 +34,7 @@ describe('Permissions', function () {
await driver.waitUntilXWindowHandles(3);
const windowHandles = await driver.getAllWindowHandles();
const extension = windowHandles[0];
+ await driver.delay(xxLargeDelayMs);
await driver.switchToWindowWithTitle(
'MetaMask Notification',
windowHandles,
diff --git a/test/e2e/tests/provider-events.spec.js b/test/e2e/tests/provider-events.spec.js
index 0949dbf46..196b0bc08 100644
--- a/test/e2e/tests/provider-events.spec.js
+++ b/test/e2e/tests/provider-events.spec.js
@@ -1,5 +1,5 @@
const { strict: assert } = require('assert');
-const { withFixtures, regularDelayMs } = require('../helpers');
+const { withFixtures, regularDelayMs, xxLargeDelayMs } = require('../helpers');
describe('MetaMask', function () {
it('provider should inform dapp when switching networks', async function () {
@@ -27,7 +27,7 @@ describe('MetaMask', function () {
await driver.openNewPage('http://127.0.0.1:8080/');
const networkDiv = await driver.findElement('#network');
const chainIdDiv = await driver.findElement('#chainId');
- await driver.delay(regularDelayMs);
+ await driver.delay(xxLargeDelayMs);
assert.equal(await networkDiv.getText(), '1337');
assert.equal(await chainIdDiv.getText(), '0x539');
diff --git a/test/helpers/setup-helper.js b/test/helpers/setup-helper.js
index 749577c40..6e371a790 100644
--- a/test/helpers/setup-helper.js
+++ b/test/helpers/setup-helper.js
@@ -56,17 +56,12 @@ const popoverContent = window.document.createElement('div');
popoverContent.setAttribute('id', 'popover-content');
window.document.body.appendChild(popoverContent);
-// delete AbortController added by jsdom so it can be polyfilled correctly below
-delete window.AbortController;
-
// fetch
const fetch = require('node-fetch');
const { Headers, Request, Response } = fetch;
Object.assign(window, { fetch, Headers, Request, Response });
-require('abortcontroller-polyfill/dist/polyfill-patch-fetch');
-
// localStorage
window.localStorage = {
removeItem: () => null,
diff --git a/test/mocks/permission-controller.js b/test/mocks/permission-controller.js
index 1a2ee0d20..048fcde01 100644
--- a/test/mocks/permission-controller.js
+++ b/test/mocks/permission-controller.js
@@ -1,7 +1,7 @@
import { ethErrors, errorCodes } from 'eth-rpc-errors';
import deepFreeze from 'deep-freeze-strict';
-import { ApprovalController } from '@metamask/controllers';
+import { ApprovalController, ControllerMessenger } from '@metamask/controllers';
import _getRestrictedMethods from '../../app/scripts/controllers/permissions/restrictedMethods';
@@ -70,6 +70,7 @@ const getRestrictedMethods = (permController) => {
export function getPermControllerOpts() {
return {
approvals: new ApprovalController({
+ messenger: new ControllerMessenger(),
showApprovalRequest: noop,
}),
getKeyringAccounts: async () => [...keyringAccounts],
diff --git a/test/unit-global/frozenPromise.test.js b/test/unit-global/frozenPromise.test.js
deleted file mode 100644
index b41362266..000000000
--- a/test/unit-global/frozenPromise.test.js
+++ /dev/null
@@ -1,53 +0,0 @@
-// Should occur before anything else
-import './globalPatch';
-import 'ses/lockdown';
-import '../../app/scripts/lockdown-run';
-import { strict as assert } from 'assert'; /* eslint-disable-line import/first,import/order */
-
-describe('Promise global is immutable', function () {
- it('throws when reassinging promise (syntax 1)', function () {
- try {
- // eslint-disable-next-line no-global-assign,no-native-reassign
- Promise = {};
- assert.fail('did not throw error');
- } catch (err) {
- assert.ok(err, 'did throw error');
- }
- });
-
- it('throws when reassinging promise (syntax 2)', function () {
- try {
- global.Promise = {};
- assert.fail('did not throw error');
- } catch (err) {
- assert.ok(err, 'did throw error');
- }
- });
-
- it('throws when mutating existing Promise property', function () {
- try {
- Promise.all = () => undefined;
- assert.fail('did not throw error');
- } catch (err) {
- assert.ok(err, 'did throw error');
- }
- });
-
- it('throws when adding new Promise property', function () {
- try {
- Promise.foo = 'bar';
- assert.fail('did not throw error');
- } catch (err) {
- assert.ok(err, 'did throw error');
- }
- });
-
- it('throws when deleting Promise from global', function () {
- try {
- delete global.Promise;
- assert.fail('did not throw error');
- } catch (err) {
- assert.ok(err, 'did throw error');
- }
- });
-});
diff --git a/test/unit-global/globalPatch.js b/test/unit-global/globalPatch.js
deleted file mode 100644
index da239953c..000000000
--- a/test/unit-global/globalPatch.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// eslint-disable-next-line import/unambiguous,node/no-unsupported-features/es-builtins
-global.globalThis = global;
diff --git a/test/unit-global/protect-intrinsics.test.js b/test/unit-global/protect-intrinsics.test.js
new file mode 100644
index 000000000..5075080f9
--- /dev/null
+++ b/test/unit-global/protect-intrinsics.test.js
@@ -0,0 +1,71 @@
+import 'ses/lockdown';
+import '../../app/scripts/lockdown-run';
+import { strict as assert } from 'assert';
+
+// These are Agoric inventions, and we don't care about them.
+const ignoreList = new Set([
+ 'Compartment',
+ 'HandledPromise',
+ 'StaticModuleRecord',
+]);
+
+describe('non-modifiable intrinsics', function () {
+ const namedIntrinsics = Reflect.ownKeys(new Compartment().globalThis);
+
+ const globalProperties = new Set(
+ [
+ // Added to global scope by ses/dist/lockdown.cjs.
+ ...namedIntrinsics,
+
+ // TODO: Also include the named platform globals
+ // This grabs every enumerable property on globalThis.
+ // ...Object.keys(globalThis),
+ ].filter((propertyName) => !ignoreList.has(propertyName)),
+ );
+
+ globalProperties.forEach((propertyName) => {
+ it(`intrinsic globalThis["${propertyName}"]`, function () {
+ const descriptor = Reflect.getOwnPropertyDescriptor(
+ globalThis,
+ propertyName,
+ );
+
+ assert.ok(
+ descriptor,
+ `globalThis["${propertyName}"] should have a descriptor`,
+ );
+
+ // As long as Object.isFrozen is the true Object.isFrozen, the object
+ // it is called with cannot lie about being frozen.
+ const value = globalThis[propertyName];
+ if (value !== globalThis) {
+ assert.equal(
+ Object.isFrozen(value),
+ true,
+ `value of universal property globalThis["${propertyName}"] should be frozen`,
+ );
+ }
+
+ // The writability of properties with accessors cannot be modified.
+ if ('set' in descriptor || 'get' in descriptor) {
+ assert.equal(
+ descriptor.configurable,
+ false,
+ `globalThis["${propertyName}"] should be non-configurable`,
+ );
+ } else {
+ assert.equal(
+ descriptor.configurable,
+ false,
+ `globalThis["${propertyName}"] should be non-configurable`,
+ );
+
+ assert.equal(
+ descriptor.writable,
+ false,
+ `globalThis["${propertyName}"] should be non-writable`,
+ );
+ }
+ });
+ });
+});
diff --git a/ui/components/app/account-list-item/account-list-item.stories.js b/ui/components/app/account-list-item/account-list-item.stories.js
new file mode 100644
index 000000000..8cdc99220
--- /dev/null
+++ b/ui/components/app/account-list-item/account-list-item.stories.js
@@ -0,0 +1,10 @@
+import React from 'react';
+import AccountListItem from './account-list-item';
+
+export default {
+ title: 'AccountListItem',
+};
+
+export const AccountListItemComponent = () => {
+ return ;
+};
diff --git a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
index 16f8e82c7..93f3e5ee3 100644
--- a/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
+++ b/ui/components/app/advanced-gas-controls/advanced-gas-controls.component.js
@@ -131,5 +131,5 @@ AdvancedGasControls.propTypes = {
maxPriorityFeeFiat: PropTypes.string,
maxFeeFiat: PropTypes.string,
gasErrors: PropTypes.object,
- minimumGasLimit: PropTypes.number,
+ minimumGasLimit: PropTypes.string,
};
diff --git a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js
index c73de3f84..1b887bba4 100644
--- a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js
+++ b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.js
@@ -15,7 +15,7 @@ import {
getSelectedAddress,
getSelectedIdentity,
} from '../../../../selectors';
-import { isExtensionUrl } from '../../../../helpers/utils/util';
+import { isExtensionUrl, getURLHost } from '../../../../helpers/utils/util';
import Popover from '../../../ui/popover';
import Button from '../../../ui/button';
import Checkbox from '../../../ui/check-box';
@@ -88,7 +88,7 @@ const UnconnectedAccountAlert = () => {
return (
{
+ const firstLetter = contact.name[0].toUpperCase();
+ return {
+ ...obj,
+ [firstLetter]: [...(obj[firstLetter] || []), contact],
+ };
+ }, {});
- const contactGroups = contacts.reduce((acc, contact) => {
- const firstLetter = contact.name.slice(0, 1).toUpperCase();
- acc[firstLetter] = acc[firstLetter] || [];
- const bucket = acc[firstLetter];
- bucket.push(contact);
- return acc;
- }, {});
+ const letters = Object.keys(unsortedContactsByLetter).sort();
- return Object.entries(contactGroups)
- .sort(([letter1], [letter2]) => {
- if (letter1 > letter2) {
- return 1;
- } else if (letter1 === letter2) {
- return 0;
- }
- return -1;
- })
- .map(([letter, groupItems]) => (
-
- ));
+ const sortedContactGroups = letters.map((letter) => {
+ return [
+ letter,
+ sortBy(unsortedContactsByLetter[letter], (contact) => {
+ return contact.name.toLowerCase();
+ }),
+ ];
+ });
+
+ return sortedContactGroups.map(([letter, groupItems]) => (
+
+ ));
}
renderMyAccounts() {
diff --git a/ui/components/app/contact-list/contact-list.test.js b/ui/components/app/contact-list/contact-list.test.js
new file mode 100644
index 000000000..71b638dee
--- /dev/null
+++ b/ui/components/app/contact-list/contact-list.test.js
@@ -0,0 +1,58 @@
+import React from 'react';
+import { within } from '@testing-library/react';
+import configureMockStore from 'redux-mock-store';
+import { renderWithProvider } from '../../../../test/jest/rendering';
+import ContactList from '.';
+
+describe('Contact List', () => {
+ const store = configureMockStore([])({ metamask: {} });
+
+ describe('given searchForContacts', () => {
+ const selectRecipient = () => null;
+ const selectedAddress = null;
+
+ it('sorts contacts by name within each letter group', () => {
+ const { getAllByTestId } = renderWithProvider(
+ {
+ return [
+ {
+ name: 'Al',
+ address: '0x0000000000000000000000000000000000000000',
+ },
+ {
+ name: 'aa',
+ address: '0x0000000000000000000000000000000000000001',
+ },
+ {
+ name: 'Az',
+ address: '0x0000000000000000000000000000000000000002',
+ },
+ {
+ name: 'bbb',
+ address: '0x0000000000000000000000000000000000000003',
+ },
+ ];
+ }}
+ selectRecipient={selectRecipient}
+ selectedAddress={selectedAddress}
+ />,
+ store,
+ );
+
+ const recipientGroups = getAllByTestId('recipient-group');
+ expect(within(recipientGroups[0]).getByText('A')).toBeInTheDocument();
+ const recipientsInA = within(recipientGroups[0]).getAllByTestId(
+ 'recipient',
+ );
+ expect(recipientsInA[0]).toHaveTextContent('aa0x0000...0001');
+ expect(recipientsInA[1]).toHaveTextContent('Al0x0000...0000');
+ expect(recipientsInA[2]).toHaveTextContent('Az0x0000...0002');
+ expect(within(recipientGroups[1]).getByText('B')).toBeInTheDocument();
+ const recipientsInB = within(recipientGroups[1]).getAllByTestId(
+ 'recipient',
+ );
+ expect(recipientsInB[0]).toHaveTextContent('bbb0x0000...0003');
+ });
+ });
+});
diff --git a/ui/components/app/contact-list/recipient-group/recipient-group.component.js b/ui/components/app/contact-list/recipient-group/recipient-group.component.js
index d57854917..a8fd589db 100644
--- a/ui/components/app/contact-list/recipient-group/recipient-group.component.js
+++ b/ui/components/app/contact-list/recipient-group/recipient-group.component.js
@@ -19,7 +19,10 @@ export default function RecipientGroup({
}
return (
-