mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #10257 from MetaMask/Version-v9.0.4
Version v9.0.4 RC
This commit is contained in:
commit
61374caa4e
@ -7,3 +7,4 @@ app/vendor/**
|
||||
.nyc_output/**
|
||||
.vscode/**
|
||||
test/e2e/send-eth-with-private-key-test/**
|
||||
*.scss
|
||||
|
@ -7,7 +7,7 @@ module.exports = {
|
||||
addons: [
|
||||
'@storybook/addon-knobs',
|
||||
'@storybook/addon-actions',
|
||||
'@storybook/addon-backgrounds'
|
||||
'@storybook/addon-backgrounds',
|
||||
],
|
||||
webpackFinal: async (config) => {
|
||||
config.module.strictExportPresence = true
|
||||
@ -27,18 +27,29 @@ module.exports = {
|
||||
loader: 'sass-loader',
|
||||
options: {
|
||||
sourceMap: true,
|
||||
implementation: require('sass'),
|
||||
sassOptions: {
|
||||
includePaths: ['ui/app/css/'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
config.plugins.push(new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.join('node_modules', '@fortawesome', 'fontawesome-free', 'webfonts'),
|
||||
to: path.join('fonts', 'fontawesome'),
|
||||
},
|
||||
],
|
||||
}))
|
||||
config.plugins.push(
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: path.join(
|
||||
'node_modules',
|
||||
'@fortawesome',
|
||||
'fontawesome-free',
|
||||
'webfonts',
|
||||
),
|
||||
to: path.join('fonts', 'fontawesome'),
|
||||
},
|
||||
],
|
||||
}),
|
||||
)
|
||||
return config
|
||||
},
|
||||
}
|
||||
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -2,6 +2,19 @@
|
||||
|
||||
## Current Develop Branch
|
||||
|
||||
## 9.0.4 Fri Jan 22 2021
|
||||
- [#10285](https://github.com/MetaMask/metamask-extension/pull/10285): Update @metamask/contract-metadata from v1.21.0 to 1.22.0
|
||||
- [#10174](https://github.com/MetaMask/metamask-extension/pull/10174): Move fox to bottom of 'About' page
|
||||
- [#10198](https://github.com/MetaMask/metamask-extension/pull/10198): Fix hardware account selection
|
||||
- [#10101](https://github.com/MetaMask/metamask-extension/pull/10101): Add a timeout to all network requests
|
||||
- [#10212](https://github.com/MetaMask/metamask-extension/pull/10212): Fix displayed balance of tokens with 0 decimals in swaps flow
|
||||
- [#10162](https://github.com/MetaMask/metamask-extension/pull/10162): Prevent accidentally submitting a swap twice
|
||||
- [#10224](https://github.com/MetaMask/metamask-extension/pull/10224): Improve chain ID validation
|
||||
- [#10195](https://github.com/MetaMask/metamask-extension/pull/10195): Increase minimum Firefox version to v68
|
||||
- [#10192](https://github.com/MetaMask/metamask-extension/pull/10192): Update TrezorConnect to v8
|
||||
- [#10166](https://github.com/MetaMask/metamask-extension/pull/10166): Fix back button on swaps loading page
|
||||
- [#9947](https://github.com/MetaMask/metamask-extension/pull/9947): Do not publish swaps transaction if the estimateGas call made when adding the transaction fails.
|
||||
|
||||
## 9.0.3 Fri Jan 22 2021
|
||||
- [#10243](https://github.com/MetaMask/metamask-extension/pull/10243): Fix site metadata handling
|
||||
- [#10252](https://github.com/MetaMask/metamask-extension/pull/10252): Fix decrypt message confirmation UI crash
|
||||
|
@ -1,5 +1,8 @@
|
||||
# MetaMask Browser Extension
|
||||
|
||||
Hey! We are hiring JavaScript Engineers! [Apply here](https://boards.greenhouse.io/consensys/jobs/2572388)!
|
||||
---
|
||||
|
||||
You can find the latest version of MetaMask on [our official website](https://metamask.io/). For help using MetaMask, visit our [User Support Site](https://metamask.zendesk.com/hc/en-us).
|
||||
|
||||
For [general questions](https://metamask.zendesk.com/hc/en-us/community/topics/360000682532-General), [feature requests](https://metamask.zendesk.com/hc/en-us/community/topics/360000682552-Feature-Requests-Ideas), or [developer questions](https://metamask.zendesk.com/hc/en-us/community/topics/360001751291-Developer-Questions), visit our [Community Forum](https://metamask.zendesk.com/hc/en-us/community/topics).
|
||||
|
@ -845,6 +845,9 @@
|
||||
"invalidBlockExplorerURL": {
|
||||
"message": "Invalid Block Explorer URL"
|
||||
},
|
||||
"invalidChainIdTooBig": {
|
||||
"message": "Invalid chain ID. The chain ID is too big."
|
||||
},
|
||||
"invalidCustomNetworkAlertContent1": {
|
||||
"message": "The chain ID for custom network '$1' has to be re-entered.",
|
||||
"description": "$1 is the name/identifier of the network."
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,11 +19,11 @@
|
||||
},
|
||||
"appDescription": {
|
||||
"message": "इथीरियम ब्राउज़र एक्सटेंशन",
|
||||
"description": "आवेदन का विवरण"
|
||||
"description": "The description of the application"
|
||||
},
|
||||
"appName": {
|
||||
"message": "मेटामास्क/MetaMask",
|
||||
"description": "एप्लिकेशन का नाम"
|
||||
"description": "The name of the application"
|
||||
},
|
||||
"approve": {
|
||||
"message": "मंजूर"
|
||||
@ -123,11 +123,11 @@
|
||||
},
|
||||
"fiat": {
|
||||
"message": "FIAT एक्सचेंज टाइप",
|
||||
"description": "एक्सचेंज FIAT टाइप"
|
||||
"description": "Exchange type"
|
||||
},
|
||||
"fileImportFail": {
|
||||
"message": "फ़ाइल आयात काम नहीं कर रहा है? यहां क्लिक करें!",
|
||||
"description": "यूजर को अपने खाते को जे.एस.ौ.एन फ़ाइल से आयात करने में मदद करता है"
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"from": {
|
||||
"message": "की तरफ से - संदेश"
|
||||
@ -146,11 +146,11 @@
|
||||
},
|
||||
"getEtherFromFaucet": {
|
||||
"message": "$1 के लिए एक नल से ईथर प्राप्त करें",
|
||||
"description": "ईथर नल के लिए नेटवर्क नाम प्रदर्शित करता है"
|
||||
"description": "Displays network name for Ether faucet"
|
||||
},
|
||||
"here": {
|
||||
"message": "यहां",
|
||||
"description": "अधिक जानकारी के लिए यहां क्लिक करें- (परेशानी के साथ जाता है टोकनबैलेंस) (troubleTokenBalances)"
|
||||
"description": "as in -click here- for more information (goes with troubleTokenBalances)"
|
||||
},
|
||||
"hide": {
|
||||
"message": "छुपाएं"
|
||||
@ -160,7 +160,7 @@
|
||||
},
|
||||
"import": {
|
||||
"message": "आयात",
|
||||
"description": "एक चयनित फ़ाइल से एक खाता आयात करने के लिए बटन "
|
||||
"description": "Button to import an account from a selected file"
|
||||
},
|
||||
"importAccount": {
|
||||
"message": "खाता आयात"
|
||||
@ -170,7 +170,7 @@
|
||||
},
|
||||
"imported": {
|
||||
"message": "आयातित",
|
||||
"description": "यह स्थिति दिखाती है कि कोई खाता पूरी तरह से कीरिंग में लोड हो चुका है"
|
||||
"description": "status showing that an account has been fully loaded into the keyring"
|
||||
},
|
||||
"infoHelp": {
|
||||
"message": "जानकारी और सहायता"
|
||||
@ -195,7 +195,7 @@
|
||||
},
|
||||
"jsonFile": {
|
||||
"message": "JSON फ़ाइल",
|
||||
"description": "एक खाता आयात करने के लिए प्रारूप"
|
||||
"description": "format for importing an account"
|
||||
},
|
||||
"kovan": {
|
||||
"message": "कोवान टेस्ट नेटवर्क"
|
||||
@ -235,7 +235,7 @@
|
||||
},
|
||||
"needImportFile": {
|
||||
"message": "आयात करने के लिए आपको एक फ़ाइल का चयन करना होगा।",
|
||||
"description": "प्रयोक्ता महत्वपूर्ण खाता है और उसे जारी रखने के लिए एक फ़ाइल जोड़ने की आवश्यकता है"
|
||||
"description": "User is important an account and needs to add a file to continue"
|
||||
},
|
||||
"negativeETH": {
|
||||
"message": "ईटीएच की नकारात्मक मात्रा नहीं भेज सकते हैं।."
|
||||
@ -248,7 +248,7 @@
|
||||
},
|
||||
"newAccountNumberName": {
|
||||
"message": "नया खाता $1",
|
||||
"description": "खाते का खाता बनाने पर अगले खाते का डिफ़ॉल्ट नाम"
|
||||
"description": "Default name of next account to be created on create account screen"
|
||||
},
|
||||
"newContract": {
|
||||
"message": "नया अनुबंध"
|
||||
@ -267,7 +267,7 @@
|
||||
},
|
||||
"pastePrivateKey": {
|
||||
"message": "यहां अपनी निजी कुंजी स्ट्रिंग चिपकाएं:",
|
||||
"description": "किसी निजी कुंजी से किसी खाते को आयात करने के लिए"
|
||||
"description": "For importing an account from a private key"
|
||||
},
|
||||
"personalAddressDetected": {
|
||||
"message": "व्यक्तिगत पता मिला। टोकन अनुबंध का पता इनपुट।"
|
||||
@ -277,7 +277,7 @@
|
||||
},
|
||||
"privateKey": {
|
||||
"message": "निजी कुंजी",
|
||||
"description": "खाता आयात करने के लिए उपयोग करने के लिए इस प्रकार की फ़ाइल का चयन करें"
|
||||
"description": "select this type of file to use to import an account"
|
||||
},
|
||||
"privateKeyWarning": {
|
||||
"message": "चेतावनी: कभी भी इस कुंजी का खुलासा न करें। आपकी निजी कुंजी वाले कोई भी आपके खाते में रखी किसी भी संपत्ति को चुरा सकता है।"
|
||||
@ -392,7 +392,7 @@
|
||||
},
|
||||
"troubleTokenBalances": {
|
||||
"message": "मुसीबत... आपके टोकन शेष राशि को लोड करने में हमें परेशानी हुई थी। आप उन्हें देख सकते हैं",
|
||||
"description": "टोकन शेष देखने के लिए एक लिंक ... (यहां)"
|
||||
"description": "Followed by a link (here) to view token balances"
|
||||
},
|
||||
"typePassword": {
|
||||
"message": "अपना पासवर्ड टाइप करें"
|
||||
|
@ -267,7 +267,7 @@
|
||||
},
|
||||
"connectTo": {
|
||||
"message": "Connettiti a $1",
|
||||
"description": "$1 is the name/origin of a site/dapp that the user can connect to metamask"
|
||||
"description": "$1 is the name/origin of a web3 site/application that the user can connect to metamask"
|
||||
},
|
||||
"connectToAll": {
|
||||
"message": "Connettiti a tutti i tuoi $1",
|
||||
@ -426,7 +426,7 @@
|
||||
},
|
||||
"decryptMessageNotice": {
|
||||
"message": "$1 vorrebbe leggere questo messaggio per completare l'azione",
|
||||
"description": "$1 is website or dapp name"
|
||||
"description": "$1 is the web3 site name"
|
||||
},
|
||||
"decryptMetamask": {
|
||||
"message": "Decifra messaggio"
|
||||
@ -511,7 +511,7 @@
|
||||
},
|
||||
"encryptionPublicKeyNotice": {
|
||||
"message": "$1 vorrebbe la tua chiave di cifratura pubblica. Consentendo, questo sito sarà in grado di comporre messaggi criptati per te.",
|
||||
"description": "$1 is website or dapp name"
|
||||
"description": "$1 is the web3 site name"
|
||||
},
|
||||
"encryptionPublicKeyRequest": {
|
||||
"message": "Richiedi chiave pubblica di cifratura"
|
||||
|
@ -13,7 +13,7 @@
|
||||
},
|
||||
"appDescription": {
|
||||
"message": "Ethereum Browser Extension",
|
||||
"description": "Ang deskripsyon ng application"
|
||||
"description": "The description of the application"
|
||||
},
|
||||
"appName": {
|
||||
"message": "MetaMask",
|
||||
@ -96,11 +96,11 @@
|
||||
},
|
||||
"fiat": {
|
||||
"message": "FIAT",
|
||||
"description": "Type ng exchange"
|
||||
"description": "Exchange type"
|
||||
},
|
||||
"fileImportFail": {
|
||||
"message": "Hindi gumagana ang file import? I-click ito!",
|
||||
"description": "Tinutulungan ang user na i-import ang kanilang account mula sa JSON file"
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"from": {
|
||||
"message": "Mula sa"
|
||||
@ -113,11 +113,11 @@
|
||||
},
|
||||
"getEtherFromFaucet": {
|
||||
"message": "Kumuha ng Ether mula sa faucet para sa $1",
|
||||
"description": "Ipinapakita ang pangalan ng network para sa Ether faucet"
|
||||
"description": "Displays network name for Ether faucet"
|
||||
},
|
||||
"here": {
|
||||
"message": "i-click ito",
|
||||
"description": "tulad ng -i-click dito- para sa mas maraming impormasyon (kasama ng troubleTokenBalances)"
|
||||
"description": "as in -click here- for more information (goes with troubleTokenBalances)"
|
||||
},
|
||||
"hide": {
|
||||
"message": "Itago"
|
||||
@ -127,14 +127,14 @@
|
||||
},
|
||||
"import": {
|
||||
"message": "I-import",
|
||||
"description": "Button para i-import ang account mula sa napiling file"
|
||||
"description": "Button to import an account from a selected file"
|
||||
},
|
||||
"importAccount": {
|
||||
"message": "I-import ang Account"
|
||||
},
|
||||
"imported": {
|
||||
"message": "Na-import na",
|
||||
"description": "status na nagpapakita na ang account ay lubos na na-load sa keyring"
|
||||
"description": "status showing that an account has been fully loaded into the keyring"
|
||||
},
|
||||
"infoHelp": {
|
||||
"message": "Impormasyon at Tulong"
|
||||
@ -159,7 +159,7 @@
|
||||
},
|
||||
"needImportFile": {
|
||||
"message": "Dapat kang pumili ng file para i-import.",
|
||||
"description": "Ang user ay nag-iimport ng account at kailangan magdagdag ng file upang tumuloy"
|
||||
"description": "User is important an account and needs to add a file to continue"
|
||||
},
|
||||
"newAccount": {
|
||||
"message": "Bagong Account"
|
||||
@ -181,7 +181,7 @@
|
||||
},
|
||||
"pastePrivateKey": {
|
||||
"message": "I-paste dito ang iyong private key string:",
|
||||
"description": "Para sa pag-import ng account mula sa private key"
|
||||
"description": "For importing an account from a private key"
|
||||
},
|
||||
"privateKeyWarning": {
|
||||
"message": "Babala: Huwag sabihin sa kahit na sino ang key na ito. Maaring makuha at manakaw ng sinumang nakakaalam ng iyong private key ang mga assets sa iyong account."
|
||||
@ -233,7 +233,7 @@
|
||||
},
|
||||
"troubleTokenBalances": {
|
||||
"message": "Nagkaroon kami ng problema sa paglo-load ng iyong mga balanseng token. Tingnan ito dito ",
|
||||
"description": "Susundan ng link (dito) para tingnan ang token balances"
|
||||
"description": "Followed by a link (here) to view token balances"
|
||||
},
|
||||
"typePassword": {
|
||||
"message": "I-type ang iyong Password"
|
||||
|
@ -134,7 +134,8 @@
|
||||
"message": "Блок-эксплорер"
|
||||
},
|
||||
"blockExplorerView": {
|
||||
"message": "Посмотреть аккаунт на $1"
|
||||
"message": "Посмотреть аккаунт на $1",
|
||||
"description": "$1 replaced by URL for custom block explorer"
|
||||
},
|
||||
"blockiesIdenticon": {
|
||||
"message": "Использовать Blockies Identicon"
|
||||
@ -292,7 +293,7 @@
|
||||
},
|
||||
"decryptMessageNotice": {
|
||||
"message": "Для $1 необходимо прочитать это сообщение, чтобы завершить Ваше действие",
|
||||
"description": "$1 is website or dapp name"
|
||||
"description": "$1 is the web3 site name"
|
||||
},
|
||||
"decryptMetamask": {
|
||||
"message": "Расшифровать сообщение"
|
||||
@ -353,7 +354,7 @@
|
||||
},
|
||||
"encryptionPublicKeyNotice": {
|
||||
"message": "$1 запрашивает ваш открытый ключ шифрования. По согласованию, этот сайт сможет создавать для Вас зашифрованные сообщения.",
|
||||
"description": "$1 is website or dapp name"
|
||||
"description": "$1 is the web3 site name"
|
||||
},
|
||||
"encryptionPublicKeyRequest": {
|
||||
"message": "Запрос публичного ключа шифрования"
|
||||
|
@ -25,11 +25,11 @@
|
||||
},
|
||||
"appDescription": {
|
||||
"message": "எதெரியும் பிரௌசர் நீட்டிப்பு",
|
||||
"description": "பயன்பாட்டின் விளக்கம்"
|
||||
"description": "The description of the application"
|
||||
},
|
||||
"appName": {
|
||||
"message": "மேடமஸ்க் ",
|
||||
"description": "பயன்பாட்டின் பெயர்"
|
||||
"description": "The name of the application"
|
||||
},
|
||||
"approve": {
|
||||
"message": "ஒப்புதல்"
|
||||
@ -168,11 +168,11 @@
|
||||
},
|
||||
"fiat": {
|
||||
"message": "FIAT",
|
||||
"description": "பரிமாற்ற வகை"
|
||||
"description": "Exchange type"
|
||||
},
|
||||
"fileImportFail": {
|
||||
"message": "கோப்பு இறக்குமதி வேலை செய்யவில்லையா? இங்கே கிளிக் செய்யவும்!",
|
||||
"description": "JSON கோப்பில் பயனர் கணக்கை தங்கள் கணக்கை இறக்குமதி செய்ய உதவுகிறது"
|
||||
"description": "Helps user import their account from a JSON file"
|
||||
},
|
||||
"forgetDevice": {
|
||||
"message": "இந்தச் சாதனத்தை அகற்று"
|
||||
@ -194,14 +194,14 @@
|
||||
},
|
||||
"getEtherFromFaucet": {
|
||||
"message": "$ 1 க்கு ஒரு குழாய் இருந்து ஈதர் கிடைக்கும்$1",
|
||||
"description": "ஈத்தர் குழாய் ஐந்து பிணைய பெயர் காட்டுகிறது"
|
||||
"description": "Displays network name for Ether faucet"
|
||||
},
|
||||
"getStarted": {
|
||||
"message": "தொடங்குக"
|
||||
},
|
||||
"here": {
|
||||
"message": "இங்கே",
|
||||
"description": "இங்கே-கிளிக் செய்யவும்- மேலும் தகவலுக்கு (troubleTokenBalances செல்கிறது)"
|
||||
"description": "as in -click here- for more information (goes with troubleTokenBalances)"
|
||||
},
|
||||
"hide": {
|
||||
"message": "மறை"
|
||||
@ -249,7 +249,7 @@
|
||||
},
|
||||
"jsonFile": {
|
||||
"message": "JSON கோப்பு",
|
||||
"description": "ஒரு கணக்கை இறக்குமதி செய்ய வடிவமைக்கப்பட்டுள்ளது"
|
||||
"description": "format for importing an account"
|
||||
},
|
||||
"kovan": {
|
||||
"message": "கோவன் டெஸ்ட் நெட்வொர்க்"
|
||||
@ -298,7 +298,7 @@
|
||||
},
|
||||
"needImportFile": {
|
||||
"message": "இறக்குமதி செய்ய ஒரு கோப்பை நீங்கள் தேர்ந்தெடுக்க வேண்டும்.",
|
||||
"description": "பயனர் ஒரு கணக்கு முக்கியம் மற்றும் தொடர ஒரு கோப்பு சேர்க்க வேண்டும்"
|
||||
"description": "User is important an account and needs to add a file to continue"
|
||||
},
|
||||
"negativeETH": {
|
||||
"message": "ETH எதிர்மறை அளவுகளை அனுப்ப முடியாது."
|
||||
@ -311,7 +311,7 @@
|
||||
},
|
||||
"newAccountNumberName": {
|
||||
"message": "கணக்கு $ 1",
|
||||
"description": "கணக்கு கணக்கை உருவாக்குவதற்கு அடுத்த கணக்கின் இயல்புநிலை பெயர் உருவாக்கப்படும்"
|
||||
"description": "Default name of next account to be created on create account screen"
|
||||
},
|
||||
"newContract": {
|
||||
"message": "புதிய ஒப்பந்தம்"
|
||||
@ -351,7 +351,7 @@
|
||||
},
|
||||
"pastePrivateKey": {
|
||||
"message": "இங்கே உங்கள் தனிப்பட்ட விசை சரத்தை ஒட்டுக:",
|
||||
"description": "ஒரு தனிப்பட்ட விசை ஒரு கணக்கை இறக்குமதி செய்ய"
|
||||
"description": "For importing an account from a private key"
|
||||
},
|
||||
"personalAddressDetected": {
|
||||
"message": "தனிப்பட்ட முகவரி கண்டறியப்பட்டது. டோக்கன் ஒப்பந்த முகவரியை உள்ளிடவும்."
|
||||
@ -361,7 +361,7 @@
|
||||
},
|
||||
"privateKey": {
|
||||
"message": "தனிப்பட்ட விசை",
|
||||
"description": "ஒரு கணக்கை இறக்குமதி செய்ய பயன்படுத்த இந்த வகை கோப்பை தேர்ந்தெடுக்கவும்"
|
||||
"description": "select this type of file to use to import an account"
|
||||
},
|
||||
"privateKeyWarning": {
|
||||
"message": "எச்சரிக்கை: இந்த விசையை எப்போதும் வெளியிட வேண்டாம். உங்கள் தனிப்பட்ட விசைகளைக் கொண்ட எவரும் உங்கள் கணக்கில் உள்ள எந்த சொத்துக்களையும் திருடலாம்."
|
||||
@ -509,7 +509,7 @@
|
||||
},
|
||||
"troubleTokenBalances": {
|
||||
"message": "உங்கள் டோக்கன் நிலுவைகளை ஏற்றுவதில் சிக்கல் ஏற்பட்டது. நீங்கள் அவர்களை பார்க்க முடியும்.",
|
||||
"description": "டோக்கன் நிலுவைகளை காண ஒரு இணைப்பு (இங்கே) தொடர்ந்து"
|
||||
"description": "Followed by a link (here) to view token balances"
|
||||
},
|
||||
"tryAgain": {
|
||||
"message": "மீண்டும் முயல்க"
|
||||
|
@ -78,6 +78,6 @@
|
||||
"notifications"
|
||||
],
|
||||
"short_name": "__MSG_appName__",
|
||||
"version": "9.0.3",
|
||||
"version": "9.0.4",
|
||||
"web_accessible_resources": ["inpage.js", "phishing.html"]
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "webextension@metamask.io",
|
||||
"strict_min_version": "56.0"
|
||||
"strict_min_version": "68.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,12 @@ import extension from 'extensionizer'
|
||||
import { storeAsStream, storeTransformStream } from '@metamask/obs-store'
|
||||
import PortStream from 'extension-port-stream'
|
||||
import { captureException } from '@sentry/browser'
|
||||
|
||||
import {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
} from '../../shared/constants/app'
|
||||
import migrations from './migrations'
|
||||
import Migrator from './lib/migrator'
|
||||
import ExtensionPlatform from './platforms/extension'
|
||||
@ -31,12 +37,6 @@ import rawFirstTimeState from './first-time-state'
|
||||
import getFirstPreferredLangCode from './lib/get-first-preferred-lang-code'
|
||||
import getObjStructure from './lib/getObjStructure'
|
||||
import setupEnsIpfsResolver from './lib/ens-ipfs/setup'
|
||||
|
||||
import {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
} from './lib/enums'
|
||||
/* eslint-enable import/first */
|
||||
|
||||
const { sentry } = global
|
||||
|
@ -2,12 +2,11 @@ import Web3 from 'web3'
|
||||
import contracts from '@metamask/contract-metadata'
|
||||
import { warn } from 'loglevel'
|
||||
import SINGLE_CALL_BALANCES_ABI from 'single-call-balance-checker-abi'
|
||||
import { MAINNET_CHAIN_ID } from './network/enums'
|
||||
import { MAINNET_CHAIN_ID } from '../../../shared/constants/network'
|
||||
import { SINGLE_CALL_BALANCES_ADDRESS } from '../constants/contracts'
|
||||
|
||||
// By default, poll every 3 minutes
|
||||
const DEFAULT_INTERVAL = 180 * 1000
|
||||
const SINGLE_CALL_BALANCES_ADDRESS =
|
||||
'0xb1f8e55c7f64d203c1400b9d8555d050f94adf39'
|
||||
|
||||
/**
|
||||
* A controller that polls for token exchange
|
||||
|
@ -3,7 +3,7 @@ import log from 'loglevel'
|
||||
import BN from 'bn.js'
|
||||
import createId from '../lib/random-id'
|
||||
import { bnToHex } from '../lib/util'
|
||||
import fetchWithTimeout from '../lib/fetch-with-timeout'
|
||||
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
|
||||
|
||||
import {
|
||||
TRANSACTION_CATEGORIES,
|
||||
@ -22,11 +22,9 @@ import {
|
||||
RINKEBY_CHAIN_ID,
|
||||
ROPSTEN,
|
||||
ROPSTEN_CHAIN_ID,
|
||||
} from './network/enums'
|
||||
} from '../../../shared/constants/network'
|
||||
|
||||
const fetch = fetchWithTimeout({
|
||||
timeout: 30000,
|
||||
})
|
||||
const fetchWithTimeout = getFetchWithTimeout(30000)
|
||||
|
||||
/**
|
||||
* This controller is responsible for retrieving incoming transactions. Etherscan is polled once every block to check
|
||||
@ -227,7 +225,7 @@ export default class IncomingTransactionsController {
|
||||
if (fromBlock) {
|
||||
url += `&startBlock=${parseInt(fromBlock, 10)}`
|
||||
}
|
||||
const response = await fetch(url)
|
||||
const response = await fetchWithTimeout(url)
|
||||
const parsedResponse = await response.json()
|
||||
|
||||
return {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { merge, omit } from 'lodash'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import { bufferToHex, sha3 } from 'ethereumjs-util'
|
||||
import { ENVIRONMENT_TYPE_BACKGROUND } from '../lib/enums'
|
||||
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app'
|
||||
import {
|
||||
METAMETRICS_ANONYMOUS_ID,
|
||||
METAMETRICS_BACKGROUND_PAGE_OBJECT,
|
||||
|
@ -8,7 +8,7 @@ import providerFromMiddleware from 'eth-json-rpc-middleware/providerFromMiddlewa
|
||||
import createInfuraMiddleware from 'eth-json-rpc-infura'
|
||||
import BlockTracker from 'eth-block-tracker'
|
||||
|
||||
import { NETWORK_TYPE_TO_ID_MAP } from './enums'
|
||||
import { NETWORK_TYPE_TO_ID_MAP } from '../../../../shared/constants/network'
|
||||
|
||||
export default function createInfuraClient({ network, projectId }) {
|
||||
const infuraMiddleware = createInfuraMiddleware({
|
||||
|
@ -9,25 +9,29 @@ import {
|
||||
createEventEmitterProxy,
|
||||
} from 'swappable-obj-proxy'
|
||||
import EthQuery from 'eth-query'
|
||||
import createMetamaskMiddleware from './createMetamaskMiddleware'
|
||||
import createInfuraClient from './createInfuraClient'
|
||||
import createJsonRpcClient from './createJsonRpcClient'
|
||||
|
||||
import {
|
||||
RINKEBY,
|
||||
MAINNET,
|
||||
INFURA_PROVIDER_TYPES,
|
||||
NETWORK_TYPE_RPC,
|
||||
NETWORK_TYPE_TO_ID_MAP,
|
||||
MAINNET_CHAIN_ID,
|
||||
RINKEBY_CHAIN_ID,
|
||||
} from './enums'
|
||||
} from '../../../../shared/constants/network'
|
||||
import {
|
||||
isPrefixedFormattedHexString,
|
||||
isSafeChainId,
|
||||
} from '../../../../shared/modules/utils'
|
||||
import createMetamaskMiddleware from './createMetamaskMiddleware'
|
||||
import createInfuraClient from './createInfuraClient'
|
||||
import createJsonRpcClient from './createJsonRpcClient'
|
||||
|
||||
const env = process.env.METAMASK_ENV
|
||||
|
||||
let defaultProviderConfigOpts
|
||||
if (process.env.IN_TEST === 'true') {
|
||||
defaultProviderConfigOpts = {
|
||||
type: 'rpc',
|
||||
type: NETWORK_TYPE_RPC,
|
||||
rpcUrl: 'http://localhost:8545',
|
||||
chainId: '0x539',
|
||||
nickname: 'Localhost 8545',
|
||||
@ -160,8 +164,16 @@ export default class NetworkController extends EventEmitter {
|
||||
}
|
||||
|
||||
setRpcTarget(rpcUrl, chainId, ticker = 'ETH', nickname = '', rpcPrefs) {
|
||||
assert.ok(
|
||||
isPrefixedFormattedHexString(chainId),
|
||||
`Invalid chain ID "${chainId}": invalid hex string.`,
|
||||
)
|
||||
assert.ok(
|
||||
isSafeChainId(parseInt(chainId, 16)),
|
||||
`Invalid chain ID "${chainId}": numerical value greater than max safe value.`,
|
||||
)
|
||||
this.setProviderConfig({
|
||||
type: 'rpc',
|
||||
type: NETWORK_TYPE_RPC,
|
||||
rpcUrl,
|
||||
chainId,
|
||||
ticker,
|
||||
@ -171,14 +183,14 @@ export default class NetworkController extends EventEmitter {
|
||||
}
|
||||
|
||||
async setProviderType(type, rpcUrl = '', ticker = 'ETH', nickname = '') {
|
||||
assert.notEqual(
|
||||
assert.notStrictEqual(
|
||||
type,
|
||||
'rpc',
|
||||
`NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`,
|
||||
NETWORK_TYPE_RPC,
|
||||
`NetworkController - cannot call "setProviderType" with type "${NETWORK_TYPE_RPC}". Use "setRpcTarget"`,
|
||||
)
|
||||
assert(
|
||||
assert.ok(
|
||||
INFURA_PROVIDER_TYPES.includes(type),
|
||||
`NetworkController - Unknown rpc type "${type}"`,
|
||||
`Unknown Infura provider type "${type}".`,
|
||||
)
|
||||
const { chainId } = NETWORK_TYPE_TO_ID_MAP[type]
|
||||
this.setProviderConfig({ type, rpcUrl, chainId, ticker, nickname })
|
||||
@ -209,7 +221,7 @@ export default class NetworkController extends EventEmitter {
|
||||
|
||||
getNetworkIdentifier() {
|
||||
const provider = this.providerStore.getState()
|
||||
return provider.type === 'rpc' ? provider.rpcUrl : provider.type
|
||||
return provider.type === NETWORK_TYPE_RPC ? provider.rpcUrl : provider.type
|
||||
}
|
||||
|
||||
//
|
||||
@ -228,7 +240,7 @@ export default class NetworkController extends EventEmitter {
|
||||
if (isInfura) {
|
||||
this._configureInfuraProvider(type, this._infuraProjectId)
|
||||
// url-based rpc endpoints
|
||||
} else if (type === 'rpc') {
|
||||
} else if (type === NETWORK_TYPE_RPC) {
|
||||
this._configureStandardProvider(rpcUrl, chainId)
|
||||
} else {
|
||||
throw new Error(
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NETWORK_TO_NAME_MAP } from './enums'
|
||||
import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network'
|
||||
|
||||
export const getNetworkDisplayName = (key) => NETWORK_TO_NAME_MAP[key]
|
||||
|
||||
|
@ -10,11 +10,6 @@ export const METADATA_STORE_KEY = 'domainMetadata'
|
||||
|
||||
export const METADATA_CACHE_MAX_SIZE = 100
|
||||
|
||||
export const CAVEAT_NAMES = {
|
||||
exposedAccounts: 'exposedAccounts',
|
||||
primaryAccountOnly: 'primaryAccountOnly',
|
||||
}
|
||||
|
||||
export const CAVEAT_TYPES = {
|
||||
limitResponseLength: 'limitResponseLength',
|
||||
filterResponse: 'filterResponse',
|
||||
|
@ -3,13 +3,10 @@ import { JsonRpcEngine } from 'json-rpc-engine'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import log from 'loglevel'
|
||||
import { CapabilitiesController as RpcCap } from 'rpc-cap'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
import createPermissionsMethodMiddleware from './permissionsMethodMiddleware'
|
||||
import PermissionsLogController from './permissionsLog'
|
||||
|
||||
// Methods that do not require any permissions to use:
|
||||
import { CAVEAT_NAMES } from '../../../../shared/constants/permissions'
|
||||
import {
|
||||
APPROVAL_TYPE,
|
||||
SAFE_METHODS, // methods that do not require any permissions to use
|
||||
@ -18,11 +15,13 @@ import {
|
||||
METADATA_CACHE_MAX_SIZE,
|
||||
LOG_STORE_KEY,
|
||||
HISTORY_STORE_KEY,
|
||||
CAVEAT_NAMES,
|
||||
NOTIFICATION_NAMES,
|
||||
CAVEAT_TYPES,
|
||||
} from './enums'
|
||||
|
||||
import createPermissionsMethodMiddleware from './permissionsMethodMiddleware'
|
||||
import PermissionsLogController from './permissionsLog'
|
||||
|
||||
// instanbul ignore next
|
||||
const noop = () => undefined
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { CAVEAT_NAMES } from '../../../../shared/constants/permissions'
|
||||
import {
|
||||
CAVEAT_NAMES,
|
||||
HISTORY_STORE_KEY,
|
||||
LOG_IGNORE_METHODS,
|
||||
LOG_LIMIT,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createAsyncMiddleware } from 'json-rpc-engine'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
|
||||
/**
|
||||
* Create middleware for handling certain methods and preprocessing permissions requests.
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { strict as assert } from 'assert'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import { normalize as normalizeAddress } from 'eth-sig-util'
|
||||
import { isValidAddress } from 'ethereumjs-util'
|
||||
import ethers from 'ethers'
|
||||
import log from 'loglevel'
|
||||
import { isPrefixedFormattedHexString } from '../lib/util'
|
||||
import { LISTED_CONTRACT_ADDRESSES } from '../../../shared/constants/tokens'
|
||||
import { NETWORK_TYPE_TO_ID_MAP } from './network/enums'
|
||||
import { NETWORK_TYPE_TO_ID_MAP } from '../../../shared/constants/network'
|
||||
import { isPrefixedFormattedHexString } from '../../../shared/modules/utils'
|
||||
|
||||
export default class PreferencesController {
|
||||
/**
|
||||
|
@ -2,6 +2,9 @@ import { ObservableStore } from '@metamask/obs-store'
|
||||
import log from 'loglevel'
|
||||
import { normalize as normalizeAddress } from 'eth-sig-util'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
|
||||
|
||||
const fetchWithTimeout = getFetchWithTimeout(30000)
|
||||
|
||||
// By default, poll every 3 minutes
|
||||
const DEFAULT_INTERVAL = 180 * 1000
|
||||
@ -34,7 +37,7 @@ export default class TokenRatesController {
|
||||
const query = `contract_addresses=${pairs}&vs_currencies=${nativeCurrency}`
|
||||
if (this._tokens.length > 0) {
|
||||
try {
|
||||
const response = await window.fetch(
|
||||
const response = await fetchWithTimeout(
|
||||
`https://api.coingecko.com/api/v3/simple/token_price/ethereum?${query}`,
|
||||
)
|
||||
const prices = await response.json()
|
||||
|
@ -3,7 +3,7 @@ import { ObservableStore } from '@metamask/obs-store'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import Transaction from 'ethereumjs-tx'
|
||||
import EthQuery from 'ethjs-query'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import abi from 'human-standard-token-abi'
|
||||
import { ethers } from 'ethers'
|
||||
import NonceTracker from 'nonce-tracker'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { isValidAddress } from 'ethereumjs-util'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import { addHexPrefix } from '../../../lib/util'
|
||||
import { TRANSACTION_STATUSES } from '../../../../../shared/constants/transaction'
|
||||
|
||||
|
@ -19,14 +19,14 @@ import {
|
||||
RINKEBY_CHAIN_ID,
|
||||
ROPSTEN_CHAIN_ID,
|
||||
KOVAN_CHAIN_ID,
|
||||
} from '../controllers/network/enums'
|
||||
} from '../../../shared/constants/network'
|
||||
|
||||
import {
|
||||
SINGLE_CALL_BALANCES_ADDRESS,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_RINKEBY,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_ROPSTEN,
|
||||
SINGLE_CALL_BALANCES_ADDRESS_KOVAN,
|
||||
} from '../controllers/network/contract-addresses'
|
||||
} from '../constants/contracts'
|
||||
import { bnToHex } from './util'
|
||||
|
||||
/**
|
||||
|
@ -1,11 +1,11 @@
|
||||
import EventEmitter from 'events'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import log from 'loglevel'
|
||||
import { MESSAGE_TYPE } from '../../../shared/constants/app'
|
||||
import { addHexPrefix } from './util'
|
||||
import createId from './random-id'
|
||||
import { MESSAGE_TYPE } from './enums'
|
||||
|
||||
const hexRe = /^[0-9A-Fa-f]+$/gu
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import EventEmitter from 'events'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import log from 'loglevel'
|
||||
import { MESSAGE_TYPE } from '../../../shared/constants/app'
|
||||
import createId from './random-id'
|
||||
import { MESSAGE_TYPE } from './enums'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_getEncryptionPublicKey' type request. These are created when
|
||||
|
@ -1,6 +1,9 @@
|
||||
import extension from 'extensionizer'
|
||||
import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'
|
||||
import resolveEnsToIpfsContentId from './resolver'
|
||||
|
||||
const fetchWithTimeout = getFetchWithTimeout(30000)
|
||||
|
||||
const supportedTopLevelDomains = ['eth']
|
||||
|
||||
export default function setupEnsIpfsResolver({
|
||||
@ -55,7 +58,9 @@ export default function setupEnsIpfsResolver({
|
||||
)}.${ipfsGateway}${pathname}${search || ''}${fragment || ''}`
|
||||
try {
|
||||
// check if ipfs gateway has result
|
||||
const response = await window.fetch(resolvedUrl, { method: 'HEAD' })
|
||||
const response = await fetchWithTimeout(resolvedUrl, {
|
||||
method: 'HEAD',
|
||||
})
|
||||
if (response.status === 200) {
|
||||
url = resolvedUrl
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import EventEmitter from 'events'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import { MESSAGE_TYPE } from '../../../shared/constants/app'
|
||||
import createId from './random-id'
|
||||
import { MESSAGE_TYPE } from './enums'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_sign' type signature request. These are created when a signature for
|
||||
|
@ -1,4 +1,7 @@
|
||||
import log from 'loglevel'
|
||||
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
|
||||
|
||||
const fetchWithTimeout = getFetchWithTimeout(30000)
|
||||
|
||||
const FIXTURE_SERVER_HOST = 'localhost'
|
||||
const FIXTURE_SERVER_PORT = 12345
|
||||
@ -24,7 +27,7 @@ export default class ReadOnlyNetworkStore {
|
||||
*/
|
||||
async _init() {
|
||||
try {
|
||||
const response = await window.fetch(FIXTURE_SERVER_URL)
|
||||
const response = await fetchWithTimeout(FIXTURE_SERVER_URL)
|
||||
if (response.ok) {
|
||||
this._state = await response.json()
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import EventEmitter from 'events'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import ethUtil from 'ethereumjs-util'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import log from 'loglevel'
|
||||
import { MESSAGE_TYPE } from '../../../shared/constants/app'
|
||||
import { addHexPrefix } from './util'
|
||||
import createId from './random-id'
|
||||
import { MESSAGE_TYPE } from './enums'
|
||||
|
||||
const hexRe = /^[0-9A-Fa-f]+$/gu
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MESSAGE_TYPE } from '../../enums'
|
||||
import { MESSAGE_TYPE } from '../../../../../shared/constants/app'
|
||||
|
||||
/**
|
||||
* This RPC method gets background state relevant to the provider.
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MESSAGE_TYPE } from '../../enums'
|
||||
import { MESSAGE_TYPE } from '../../../../../shared/constants/app'
|
||||
|
||||
/**
|
||||
* This RPC method is called by the inpage provider whenever it detects the
|
||||
@ -40,16 +40,18 @@ function logWeb3ShimUsageHandler(
|
||||
if (getWeb3ShimUsageState(origin) === undefined) {
|
||||
setWeb3ShimUsageRecorded(origin)
|
||||
|
||||
sendMetrics({
|
||||
event: `Website Accessed window.web3 Shim`,
|
||||
category: 'inpage_provider',
|
||||
eventContext: {
|
||||
sendMetrics(
|
||||
{
|
||||
event: `Website Accessed window.web3 Shim`,
|
||||
category: 'inpage_provider',
|
||||
referrer: {
|
||||
url: origin,
|
||||
},
|
||||
},
|
||||
excludeMetaMetricsId: true,
|
||||
})
|
||||
{
|
||||
excludeMetaMetricsId: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
res.result = true
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MESSAGE_TYPE } from '../../enums'
|
||||
import { MESSAGE_TYPE } from '../../../../../shared/constants/app'
|
||||
|
||||
const watchAsset = {
|
||||
methodNames: [MESSAGE_TYPE.WATCH_ASSET, MESSAGE_TYPE.WATCH_ASSET_LEGACY],
|
||||
|
@ -1,13 +1,13 @@
|
||||
import EventEmitter from 'events'
|
||||
import assert from 'assert'
|
||||
import { ObservableStore } from '@metamask/obs-store'
|
||||
import { ethErrors } from 'eth-json-rpc-errors'
|
||||
import { ethErrors } from 'eth-rpc-errors'
|
||||
import { typedSignatureHash, TYPED_MESSAGE_SCHEMA } from 'eth-sig-util'
|
||||
import { isValidAddress } from 'ethereumjs-util'
|
||||
import log from 'loglevel'
|
||||
import jsonschema from 'jsonschema'
|
||||
import { MESSAGE_TYPE } from '../../../shared/constants/app'
|
||||
import createId from './random-id'
|
||||
import { MESSAGE_TYPE } from './enums'
|
||||
|
||||
/**
|
||||
* Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
PLATFORM_CHROME,
|
||||
PLATFORM_EDGE,
|
||||
PLATFORM_BRAVE,
|
||||
} from './enums'
|
||||
} from '../../../shared/constants/app'
|
||||
|
||||
/**
|
||||
* @see {@link getEnvironmentType}
|
||||
@ -147,21 +147,6 @@ function checkForError() {
|
||||
return new Error(lastError.message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given value is a 0x-prefixed, non-zero, non-zero-padded,
|
||||
* hexadecimal string.
|
||||
*
|
||||
* @param {any} value - The value to check.
|
||||
* @returns {boolean} True if the value is a correctly formatted hex string,
|
||||
* false otherwise.
|
||||
*/
|
||||
function isPrefixedFormattedHexString(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return false
|
||||
}
|
||||
return /^0x[1-9a-f]+[0-9a-f]*$/iu.test(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefixes a hex string with '0x' or '-0x' and returns it. Idempotent.
|
||||
*
|
||||
@ -202,7 +187,6 @@ export {
|
||||
hexToBn,
|
||||
BnMultiplyByFraction,
|
||||
checkForError,
|
||||
isPrefixedFormattedHexString,
|
||||
addHexPrefix,
|
||||
bnToHex,
|
||||
}
|
||||
|
@ -2402,13 +2402,6 @@ export default class MetamaskController extends EventEmitter {
|
||||
nickname,
|
||||
rpcPrefs,
|
||||
) {
|
||||
await this.preferencesController.updateRpc({
|
||||
rpcUrl,
|
||||
chainId,
|
||||
ticker,
|
||||
nickname,
|
||||
rpcPrefs,
|
||||
})
|
||||
this.networkController.setRpcTarget(
|
||||
rpcUrl,
|
||||
chainId,
|
||||
@ -2416,6 +2409,13 @@ export default class MetamaskController extends EventEmitter {
|
||||
nickname,
|
||||
rpcPrefs,
|
||||
)
|
||||
await this.preferencesController.updateRpc({
|
||||
rpcUrl,
|
||||
chainId,
|
||||
ticker,
|
||||
nickname,
|
||||
rpcPrefs,
|
||||
})
|
||||
return rpcUrl
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { NETWORK_TYPE_TO_ID_MAP } from '../controllers/network/enums'
|
||||
import { NETWORK_TYPE_TO_ID_MAP } from '../../../shared/constants/network'
|
||||
|
||||
const version = 51
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import extension from 'extensionizer'
|
||||
import { createExplorerLink as explorerLink } from '@metamask/etherscan-link'
|
||||
import { getEnvironmentType, checkForError } from '../lib/util'
|
||||
import { ENVIRONMENT_TYPE_BACKGROUND } from '../lib/enums'
|
||||
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app'
|
||||
import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction'
|
||||
|
||||
export default class ExtensionPlatform {
|
||||
|
@ -12,12 +12,12 @@ import EthQuery from 'eth-query'
|
||||
import StreamProvider from 'web3-stream-provider'
|
||||
import log from 'loglevel'
|
||||
import launchMetaMaskUi from '../../ui'
|
||||
import ExtensionPlatform from './platforms/extension'
|
||||
import { setupMultiplex } from './lib/stream-utils'
|
||||
import {
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
} from './lib/enums'
|
||||
} from '../../shared/constants/app'
|
||||
import ExtensionPlatform from './platforms/extension'
|
||||
import { setupMultiplex } from './lib/stream-utils'
|
||||
import { getEnvironmentType } from './lib/util'
|
||||
|
||||
start().catch(log.error)
|
||||
|
2
app/vendor/trezor/content-script.js
vendored
2
app/vendor/trezor/content-script.js
vendored
@ -16,6 +16,6 @@ Passing messages from popup to background script
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
if (port && event.source === window && event.data) {
|
||||
port.postMessage(event.data);
|
||||
port.postMessage({ data: event.data });
|
||||
}
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ module.exports = function (api) {
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {
|
||||
browsers: ['chrome >= 63', 'firefox >= 56.2'],
|
||||
browsers: ['chrome >= 63', 'firefox >= 68'],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -375,12 +375,10 @@ function createScriptTasks({ browserPlatforms, livereload }) {
|
||||
METAMASK_DEBUG: opts.devMode,
|
||||
METAMASK_ENVIRONMENT: environment,
|
||||
METAMASK_VERSION: baseManifest.version,
|
||||
METAMETRICS_PROJECT_ID: process.env.METAMETRICS_PROJECT_ID,
|
||||
NODE_ENV: opts.devMode ? 'development' : 'production',
|
||||
IN_TEST: opts.testing ? 'true' : false,
|
||||
PUBNUB_SUB_KEY: process.env.PUBNUB_SUB_KEY || '',
|
||||
PUBNUB_PUB_KEY: process.env.PUBNUB_PUB_KEY || '',
|
||||
ETH_GAS_STATION_API_KEY: process.env.ETH_GAS_STATION_API_KEY || '',
|
||||
CONF: opts.devMode ? conf : {},
|
||||
SENTRY_DSN: process.env.SENTRY_DSN,
|
||||
INFURA_PROJECT_ID: opts.testing
|
||||
|
@ -1,7 +1,7 @@
|
||||
const pify = require('pify')
|
||||
const gulp = require('gulp')
|
||||
const sass = require('gulp-sass')
|
||||
sass.compiler = require('node-sass')
|
||||
sass.compiler = require('sass')
|
||||
const autoprefixer = require('gulp-autoprefixer')
|
||||
const gulpStylelint = require('gulp-stylelint')
|
||||
const watch = require('gulp-watch')
|
||||
|
44
development/lib/locales.js
Normal file
44
development/lib/locales.js
Normal file
@ -0,0 +1,44 @@
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { promisify } = require('util')
|
||||
|
||||
const log = require('loglevel')
|
||||
|
||||
const readFile = promisify(fs.readFile)
|
||||
|
||||
function getLocalePath(code) {
|
||||
return path.resolve(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'app',
|
||||
'_locales',
|
||||
code,
|
||||
'messages.json',
|
||||
)
|
||||
}
|
||||
|
||||
async function getLocale(code) {
|
||||
try {
|
||||
const localeFilePath = getLocalePath(code)
|
||||
const fileContents = await readFile(localeFilePath, 'utf8')
|
||||
return JSON.parse(fileContents)
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
log.error('Locale file not found')
|
||||
} else {
|
||||
log.error(`Error opening your locale ("${code}") file: `, e)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
function compareLocalesForMissingItems({ base, subject }) {
|
||||
return Object.keys(base).filter((key) => !subject[key])
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
compareLocalesForMissingItems,
|
||||
getLocale,
|
||||
getLocalePath,
|
||||
}
|
78
development/missing-locale-strings.js
Normal file
78
development/missing-locale-strings.js
Normal file
@ -0,0 +1,78 @@
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Reports on missing localized strings
|
||||
//
|
||||
// usage:
|
||||
//
|
||||
// node app/scripts/missing-locale-strings.js [<locale>] [--verbose]
|
||||
//
|
||||
// This script will report on any missing localized strings. It will compare the
|
||||
// chosen locale (or all locales, if none is chosen) with the `en` locale, and
|
||||
// report the coverage percentage.
|
||||
//
|
||||
// The optional '--verbose' argument will print the key for each localized string
|
||||
// to the console.
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const log = require('loglevel')
|
||||
const localeIndex = require('../app/_locales/index.json')
|
||||
const { compareLocalesForMissingItems, getLocale } = require('./lib/locales')
|
||||
|
||||
log.setDefaultLevel('info')
|
||||
|
||||
let verbose = false
|
||||
let specifiedLocale
|
||||
for (const arg of process.argv.slice(2)) {
|
||||
if (arg === '--verbose') {
|
||||
verbose = true
|
||||
} else {
|
||||
specifiedLocale = arg
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
log.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
async function main() {
|
||||
if (specifiedLocale === 'en') {
|
||||
throw new Error(
|
||||
`Can't compare 'en' locale to itself to find missing messages`,
|
||||
)
|
||||
} else if (specifiedLocale) {
|
||||
await reportMissingMessages(specifiedLocale)
|
||||
} else {
|
||||
const localeCodes = localeIndex
|
||||
.filter((localeMeta) => localeMeta.code !== 'en')
|
||||
.map((localeMeta) => localeMeta.code)
|
||||
|
||||
for (const code of localeCodes) {
|
||||
await reportMissingMessages(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function reportMissingMessages(code) {
|
||||
const englishLocale = await getLocale('en')
|
||||
const targetLocale = await getLocale(code)
|
||||
|
||||
const missingItems = compareLocalesForMissingItems({
|
||||
base: englishLocale,
|
||||
subject: targetLocale,
|
||||
})
|
||||
|
||||
const englishEntryCount = Object.keys(englishLocale).length
|
||||
const coveragePercent =
|
||||
(100 * (englishEntryCount - missingItems.length)) / englishEntryCount
|
||||
|
||||
log.info(`**${code}**: ${coveragePercent.toFixed(2)}% coverage`)
|
||||
if (missingItems.length && verbose) {
|
||||
console.log(`**${code}**: ${missingItems.length} missing messages`)
|
||||
log.info('Extra items that should not be localized:')
|
||||
missingItems.forEach(function (key) {
|
||||
log.info(` - [ ] ${key}`)
|
||||
})
|
||||
}
|
||||
}
|
@ -10,13 +10,12 @@
|
||||
// the English locale against string literals found under `ui/`, and it will check
|
||||
// other locales by comparing them to the English locale.
|
||||
//
|
||||
// A report will be printed to the console detailing any unused locales, and also
|
||||
// any missing messages in the non-English locales.
|
||||
// A report will be printed to the console detailing any unused messages.
|
||||
//
|
||||
// The if the optional '--fix' parameter is given, locales will be automatically
|
||||
// The if the optional '--fix' argument is given, locales will be automatically
|
||||
// updated to remove any unused messages.
|
||||
//
|
||||
// The optional '--quiet' parameter reduces the verbosity of the output, printing
|
||||
// The optional '--quiet' argument reduces the verbosity of the output, printing
|
||||
// just a single summary of results for each locale verified
|
||||
//
|
||||
// //////////////////////////////////////////////////////////////////////////////
|
||||
@ -27,6 +26,11 @@ const { promisify } = require('util')
|
||||
const log = require('loglevel')
|
||||
const matchAll = require('string.prototype.matchall').getPolyfill()
|
||||
const localeIndex = require('../app/_locales/index.json')
|
||||
const {
|
||||
compareLocalesForMissingItems,
|
||||
getLocale,
|
||||
getLocalePath,
|
||||
} = require('./lib/locales')
|
||||
|
||||
const readdir = promisify(fs.readdir)
|
||||
const readFile = promisify(fs.readFile)
|
||||
@ -54,15 +58,21 @@ main().catch((error) => {
|
||||
async function main() {
|
||||
if (specifiedLocale) {
|
||||
log.info(`Verifying selected locale "${specifiedLocale}":\n`)
|
||||
const locale = localeIndex.find(
|
||||
const localeEntry = localeIndex.find(
|
||||
(localeMeta) => localeMeta.code === specifiedLocale,
|
||||
)
|
||||
if (!localeEntry) {
|
||||
throw new Error(`No localize entry found for ${specifiedLocale}`)
|
||||
}
|
||||
|
||||
const failed =
|
||||
locale.code === 'en'
|
||||
specifiedLocale === 'en'
|
||||
? await verifyEnglishLocale()
|
||||
: await verifyLocale(locale)
|
||||
: await verifyLocale(specifiedLocale)
|
||||
if (failed) {
|
||||
process.exit(1)
|
||||
} else {
|
||||
console.log('No invalid entries!')
|
||||
}
|
||||
} else {
|
||||
log.info('Verifying all locales:\n')
|
||||
@ -72,33 +82,15 @@ async function main() {
|
||||
.map((localeMeta) => localeMeta.code)
|
||||
|
||||
for (const code of localeCodes) {
|
||||
log.info() // Separate each locale report by a newline when not in '--quiet' mode
|
||||
const localeFailed = await verifyLocale(code, fix)
|
||||
failed = failed || localeFailed
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getLocalePath(code) {
|
||||
return path.resolve(__dirname, '..', 'app', '_locales', code, 'messages.json')
|
||||
}
|
||||
|
||||
async function getLocale(code) {
|
||||
try {
|
||||
const localeFilePath = getLocalePath(code)
|
||||
const fileContents = await readFile(localeFilePath, 'utf8')
|
||||
return JSON.parse(fileContents)
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
log.error('Locale file not found')
|
||||
} else {
|
||||
log.error(`Error opening your locale ("${code}") file: `, e)
|
||||
console.log('No invalid entries!')
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,14 +120,6 @@ async function verifyLocale(code) {
|
||||
base: targetLocale,
|
||||
subject: englishLocale,
|
||||
})
|
||||
const missingItems = compareLocalesForMissingItems({
|
||||
base: englishLocale,
|
||||
subject: targetLocale,
|
||||
})
|
||||
|
||||
const englishEntryCount = Object.keys(englishLocale).length
|
||||
const coveragePercent =
|
||||
(100 * (englishEntryCount - missingItems.length)) / englishEntryCount
|
||||
|
||||
if (extraItems.length) {
|
||||
console.log(`**${code}**: ${extraItems.length} unused messages`)
|
||||
@ -143,20 +127,6 @@ async function verifyLocale(code) {
|
||||
extraItems.forEach(function (key) {
|
||||
log.info(` - [ ] ${key}`)
|
||||
})
|
||||
} else {
|
||||
log.info(`**${code}**: ${extraItems.length} unused messages`)
|
||||
}
|
||||
|
||||
log.info(`${coveragePercent.toFixed(2)}% coverage`)
|
||||
if (missingItems.length) {
|
||||
log.info(`Missing items not present in localized file:`)
|
||||
missingItems.forEach(function (key) {
|
||||
log.info(` - [ ] ${key}`)
|
||||
})
|
||||
}
|
||||
|
||||
if (!extraItems.length && !missingItems.length) {
|
||||
log.info('Full coverage : )')
|
||||
}
|
||||
|
||||
if (extraItems.length > 0) {
|
||||
@ -230,7 +200,6 @@ async function verifyEnglishLocale() {
|
||||
}
|
||||
|
||||
if (!unusedMessages.length && !templateUsage.length) {
|
||||
log.info('Full coverage : )')
|
||||
return false // failed === false
|
||||
}
|
||||
|
||||
@ -265,7 +234,3 @@ async function* getFileContents(filenames) {
|
||||
yield readFile(filename, 'utf8')
|
||||
}
|
||||
}
|
||||
|
||||
function compareLocalesForMissingItems({ base, subject }) {
|
||||
return Object.keys(base).filter((key) => !subject[key])
|
||||
}
|
||||
|
14
package.json
14
package.json
@ -76,7 +76,7 @@
|
||||
"@formatjs/intl-relativetimeformat": "^5.2.6",
|
||||
"@fortawesome/fontawesome-free": "^5.13.0",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@metamask/contract-metadata": "^1.21.0",
|
||||
"@metamask/contract-metadata": "^1.22.0",
|
||||
"@metamask/controllers": "^5.1.0",
|
||||
"@metamask/eth-ledger-bridge-keyring": "^0.2.6",
|
||||
"@metamask/eth-token-tracker": "^3.0.1",
|
||||
@ -86,7 +86,7 @@
|
||||
"@metamask/logo": "^2.5.0",
|
||||
"@metamask/obs-store": "^5.0.0",
|
||||
"@popperjs/core": "^2.4.0",
|
||||
"@reduxjs/toolkit": "^1.3.2",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"@sentry/browser": "^5.26.0",
|
||||
"@sentry/integrations": "^5.26.0",
|
||||
"@zxing/library": "^0.8.0",
|
||||
@ -105,7 +105,6 @@
|
||||
"end-of-stream": "^1.4.4",
|
||||
"eth-block-tracker": "^4.4.2",
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
"eth-json-rpc-errors": "^2.0.2",
|
||||
"eth-json-rpc-filters": "^4.2.1",
|
||||
"eth-json-rpc-infura": "^5.1.0",
|
||||
"eth-json-rpc-middleware": "^6.0.0",
|
||||
@ -113,8 +112,9 @@
|
||||
"eth-method-registry": "^2.0.0",
|
||||
"eth-phishing-detect": "^1.1.14",
|
||||
"eth-query": "^2.1.2",
|
||||
"eth-rpc-errors": "^4.0.2",
|
||||
"eth-sig-util": "^3.0.0",
|
||||
"eth-trezor-keyring": "^0.4.0",
|
||||
"eth-trezor-keyring": "^0.5.2",
|
||||
"ethereum-ens-network-map": "^1.0.2",
|
||||
"ethereumjs-abi": "^0.6.4",
|
||||
"ethereumjs-tx": "1.3.7",
|
||||
@ -246,7 +246,7 @@
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-replace": "^1.0.0",
|
||||
"gulp-rtlcss": "^1.4.0",
|
||||
"gulp-sass": "^4.0.0",
|
||||
"gulp-sass": "^4.1.0",
|
||||
"gulp-sourcemaps": "^2.6.0",
|
||||
"gulp-stylelint": "^13.0.0",
|
||||
"gulp-terser-js": "^5.2.2",
|
||||
@ -259,7 +259,6 @@
|
||||
"mocha": "^7.2.0",
|
||||
"nock": "^9.0.14",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-sass": "^4.14.1",
|
||||
"nyc": "^15.0.0",
|
||||
"polyfill-crypto.getrandomvalues": "^1.0.0",
|
||||
"prettier": "^2.1.1",
|
||||
@ -275,7 +274,8 @@
|
||||
"remote-redux-devtools": "^0.5.16",
|
||||
"remotedev-server": "^0.3.1",
|
||||
"resolve-url-loader": "^3.1.2",
|
||||
"sass-loader": "^7.0.1",
|
||||
"sass": "^1.32.4",
|
||||
"sass-loader": "^10.1.1",
|
||||
"selenium-webdriver": "4.0.0-alpha.7",
|
||||
"serve-handler": "^6.1.2",
|
||||
"ses": "0.11.0",
|
||||
|
@ -6,19 +6,18 @@
|
||||
* background - The background process that powers the extension
|
||||
* @typedef {'popup' | 'notification' | 'fullscreen' | 'background'} EnvironmentType
|
||||
*/
|
||||
export const ENVIRONMENT_TYPE_POPUP = 'popup'
|
||||
export const ENVIRONMENT_TYPE_NOTIFICATION = 'notification'
|
||||
export const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen'
|
||||
export const ENVIRONMENT_TYPE_BACKGROUND = 'background'
|
||||
|
||||
const ENVIRONMENT_TYPE_POPUP = 'popup'
|
||||
const ENVIRONMENT_TYPE_NOTIFICATION = 'notification'
|
||||
const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen'
|
||||
const ENVIRONMENT_TYPE_BACKGROUND = 'background'
|
||||
export const PLATFORM_BRAVE = 'Brave'
|
||||
export const PLATFORM_CHROME = 'Chrome'
|
||||
export const PLATFORM_EDGE = 'Edge'
|
||||
export const PLATFORM_FIREFOX = 'Firefox'
|
||||
export const PLATFORM_OPERA = 'Opera'
|
||||
|
||||
const PLATFORM_BRAVE = 'Brave'
|
||||
const PLATFORM_CHROME = 'Chrome'
|
||||
const PLATFORM_EDGE = 'Edge'
|
||||
const PLATFORM_FIREFOX = 'Firefox'
|
||||
const PLATFORM_OPERA = 'Opera'
|
||||
|
||||
const MESSAGE_TYPE = {
|
||||
export const MESSAGE_TYPE = {
|
||||
ETH_DECRYPT: 'eth_decrypt',
|
||||
ETH_GET_ENCRYPTION_PUBLIC_KEY: 'eth_getEncryptionPublicKey',
|
||||
ETH_SIGN: 'eth_sign',
|
||||
@ -29,16 +28,3 @@ const MESSAGE_TYPE = {
|
||||
WATCH_ASSET: 'wallet_watchAsset',
|
||||
WATCH_ASSET_LEGACY: 'metamask_watchAsset',
|
||||
}
|
||||
|
||||
export {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
ENVIRONMENT_TYPE_BACKGROUND,
|
||||
MESSAGE_TYPE,
|
||||
PLATFORM_BRAVE,
|
||||
PLATFORM_CHROME,
|
||||
PLATFORM_EDGE,
|
||||
PLATFORM_FIREFOX,
|
||||
PLATFORM_OPERA,
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// Type Imports
|
||||
/**
|
||||
* @typedef {import('../../app/scripts/lib/enums').EnvironmentType} EnvironmentType
|
||||
* @typedef {import('../../shared/constants/app').EnvironmentType} EnvironmentType
|
||||
*/
|
||||
|
||||
// Type Declarations
|
||||
|
@ -3,6 +3,7 @@ export const RINKEBY = 'rinkeby'
|
||||
export const KOVAN = 'kovan'
|
||||
export const MAINNET = 'mainnet'
|
||||
export const GOERLI = 'goerli'
|
||||
export const NETWORK_TYPE_RPC = 'rpc'
|
||||
|
||||
export const MAINNET_NETWORK_ID = '1'
|
||||
export const ROPSTEN_NETWORK_ID = '3'
|
||||
@ -16,6 +17,12 @@ export const RINKEBY_CHAIN_ID = '0x4'
|
||||
export const GOERLI_CHAIN_ID = '0x5'
|
||||
export const KOVAN_CHAIN_ID = '0x2a'
|
||||
|
||||
/**
|
||||
* The largest possible chain ID we can handle.
|
||||
* Explanation: https://gist.github.com/rekmarks/a47bd5f2525936c4b8eee31a16345553
|
||||
*/
|
||||
export const MAX_SAFE_CHAIN_ID = 4503599627370476
|
||||
|
||||
export const ROPSTEN_DISPLAY_NAME = 'Ropsten'
|
||||
export const RINKEBY_DISPLAY_NAME = 'Rinkeby'
|
||||
export const KOVAN_DISPLAY_NAME = 'Kovan'
|
4
shared/constants/permissions.js
Normal file
4
shared/constants/permissions.js
Normal file
@ -0,0 +1,4 @@
|
||||
export const CAVEAT_NAMES = {
|
||||
exposedAccounts: 'exposedAccounts',
|
||||
primaryAccountOnly: 'primaryAccountOnly',
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
### Shared Modules
|
||||
|
||||
This folder is reserved for modules that can be used globally within both the background and ui applications.
|
@ -1,4 +1,10 @@
|
||||
const fetchWithTimeout = ({ timeout = 120000 } = {}) => {
|
||||
import { memoize } from 'lodash'
|
||||
|
||||
const getFetchWithTimeout = memoize((timeout) => {
|
||||
if (!Number.isInteger(timeout) || timeout < 1) {
|
||||
throw new Error('Must specify positive integer timeout.')
|
||||
}
|
||||
|
||||
return async function _fetch(url, opts) {
|
||||
const abortController = new window.AbortController()
|
||||
const { signal } = abortController
|
||||
@ -18,6 +24,6 @@ const fetchWithTimeout = ({ timeout = 120000 } = {}) => {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default fetchWithTimeout
|
||||
export default getFetchWithTimeout
|
30
shared/modules/utils.js
Normal file
30
shared/modules/utils.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { MAX_SAFE_CHAIN_ID } from '../constants/network'
|
||||
|
||||
/**
|
||||
* Checks whether the given number primitive chain ID is safe.
|
||||
* Because some cryptographic libraries we use expect the chain ID to be a
|
||||
* number primitive, it must not exceed a certain size.
|
||||
*
|
||||
* @param {number} chainId - The chain ID to check for safety.
|
||||
* @returns {boolean} Whether the given chain ID is safe.
|
||||
*/
|
||||
export function isSafeChainId(chainId) {
|
||||
return (
|
||||
Number.isSafeInteger(chainId) && chainId > 0 && chainId <= MAX_SAFE_CHAIN_ID
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given value is a 0x-prefixed, non-zero, non-zero-padded,
|
||||
* hexadecimal string.
|
||||
*
|
||||
* @param {any} value - The value to check.
|
||||
* @returns {boolean} True if the value is a correctly formatted hex string,
|
||||
* false otherwise.
|
||||
*/
|
||||
export function isPrefixedFormattedHexString(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return false
|
||||
}
|
||||
return /^0x[1-9a-f]+[0-9a-f]*$/iu.test(value)
|
||||
}
|
@ -2,6 +2,7 @@ import assert from 'assert'
|
||||
import freeze from 'deep-freeze-strict'
|
||||
import reducers from '../../../ui/app/ducks'
|
||||
import * as actionConstants from '../../../ui/app/store/actionConstants'
|
||||
import { NETWORK_TYPE_RPC } from '../../../shared/constants/network'
|
||||
|
||||
describe('config view actions', function () {
|
||||
const initialState = {
|
||||
@ -25,7 +26,7 @@ describe('config view actions', function () {
|
||||
}
|
||||
|
||||
const result = reducers(initialState, action)
|
||||
assert.equal(result.metamask.provider.type, 'rpc')
|
||||
assert.equal(result.metamask.provider.type, NETWORK_TYPE_RPC)
|
||||
assert.equal(result.metamask.provider.rpcUrl, 'foo')
|
||||
})
|
||||
})
|
||||
|
@ -7,10 +7,7 @@ import BigNumber from 'bignumber.js'
|
||||
import DetectTokensController from '../../../../app/scripts/controllers/detect-tokens'
|
||||
import NetworkController from '../../../../app/scripts/controllers/network/network'
|
||||
import PreferencesController from '../../../../app/scripts/controllers/preferences'
|
||||
import {
|
||||
MAINNET,
|
||||
ROPSTEN,
|
||||
} from '../../../../app/scripts/controllers/network/enums'
|
||||
import { MAINNET, ROPSTEN } from '../../../../shared/constants/network'
|
||||
|
||||
describe('DetectTokensController', function () {
|
||||
const sandbox = sinon.createSandbox()
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
ROPSTEN,
|
||||
ROPSTEN_CHAIN_ID,
|
||||
ROPSTEN_NETWORK_ID,
|
||||
} from '../../../../app/scripts/controllers/network/enums'
|
||||
} from '../../../../shared/constants/network'
|
||||
import {
|
||||
TRANSACTION_CATEGORIES,
|
||||
TRANSACTION_STATUSES,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { strict as assert } from 'assert'
|
||||
import sinon from 'sinon'
|
||||
import MetaMetricsController from '../../../../app/scripts/controllers/metametrics'
|
||||
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../../app/scripts/lib/enums'
|
||||
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../../shared/constants/app'
|
||||
import { createSegmentMock } from '../../../../app/scripts/lib/segment'
|
||||
import {
|
||||
METAMETRICS_ANONYMOUS_ID,
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { ethErrors, ERROR_CODES } from 'eth-json-rpc-errors'
|
||||
import { ethErrors, errorCodes } from 'eth-rpc-errors'
|
||||
import deepFreeze from 'deep-freeze-strict'
|
||||
|
||||
import { ApprovalController } from '@metamask/controllers'
|
||||
|
||||
import _getRestrictedMethods from '../../../../../app/scripts/controllers/permissions/restrictedMethods'
|
||||
|
||||
import { CAVEAT_NAMES } from '../../../../../shared/constants/permissions'
|
||||
import {
|
||||
CAVEAT_NAMES,
|
||||
CAVEAT_TYPES,
|
||||
NOTIFICATION_NAMES,
|
||||
} from '../../../../../app/scripts/controllers/permissions/enums'
|
||||
@ -336,7 +336,7 @@ export const getters = deepFreeze({
|
||||
return {
|
||||
// name: 'EthereumRpcError',
|
||||
message: `Failed to add 'eth_accounts' to '${origin}'.`,
|
||||
code: ERROR_CODES.rpc.internal,
|
||||
code: errorCodes.rpc.internal,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -8,7 +8,7 @@ import { ObservableStore } from '@metamask/obs-store'
|
||||
import {
|
||||
ROPSTEN_NETWORK_ID,
|
||||
MAINNET_NETWORK_ID,
|
||||
} from '../../../../app/scripts/controllers/network/enums'
|
||||
} from '../../../../shared/constants/network'
|
||||
import { ETH_SWAPS_TOKEN_ADDRESS } from '../../../../ui/app/helpers/constants/swaps'
|
||||
import { createTestProviderTools } from '../../../stub/provider'
|
||||
import SwapsController, {
|
||||
|
@ -1,14 +1,16 @@
|
||||
import assert from 'assert'
|
||||
import nock from 'nock'
|
||||
|
||||
import fetchWithTimeout from '../../../app/scripts/lib/fetch-with-timeout'
|
||||
import getFetchWithTimeout from '../../../shared/modules/fetch-with-timeout'
|
||||
|
||||
describe('fetchWithTimeout', function () {
|
||||
describe('getFetchWithTimeout', function () {
|
||||
it('fetches a url', async function () {
|
||||
nock('https://api.infura.io').get('/money').reply(200, '{"hodl": false}')
|
||||
|
||||
const fetch = fetchWithTimeout()
|
||||
const response = await (await fetch('https://api.infura.io/money')).json()
|
||||
const fetchWithTimeout = getFetchWithTimeout(30000)
|
||||
const response = await (
|
||||
await fetchWithTimeout('https://api.infura.io/money')
|
||||
).json()
|
||||
assert.deepEqual(response, {
|
||||
hodl: false,
|
||||
})
|
||||
@ -20,12 +22,10 @@ describe('fetchWithTimeout', function () {
|
||||
.delay(2000)
|
||||
.reply(200, '{"moon": "2012-12-21T11:11:11Z"}')
|
||||
|
||||
const fetch = fetchWithTimeout({
|
||||
timeout: 123,
|
||||
})
|
||||
const fetchWithTimeout = getFetchWithTimeout(123)
|
||||
|
||||
try {
|
||||
await fetch('https://api.infura.io/moon').then((r) => r.json())
|
||||
await fetchWithTimeout('https://api.infura.io/moon').then((r) => r.json())
|
||||
assert.fail('Request should throw')
|
||||
} catch (e) {
|
||||
assert.ok(e)
|
||||
@ -38,15 +38,20 @@ describe('fetchWithTimeout', function () {
|
||||
.delay(2000)
|
||||
.reply(200, '{"moon": "2012-12-21T11:11:11Z"}')
|
||||
|
||||
const fetch = fetchWithTimeout({
|
||||
timeout: 123,
|
||||
})
|
||||
const fetchWithTimeout = getFetchWithTimeout(123)
|
||||
|
||||
try {
|
||||
await fetch('https://api.infura.io/moon').then((r) => r.json())
|
||||
await fetchWithTimeout('https://api.infura.io/moon').then((r) => r.json())
|
||||
assert.fail('Request should be aborted')
|
||||
} catch (e) {
|
||||
assert.deepEqual(e.message, 'Aborted')
|
||||
}
|
||||
})
|
||||
|
||||
it('throws on invalid timeout', async function () {
|
||||
assert.throws(() => getFetchWithTimeout(), 'should throw')
|
||||
assert.throws(() => getFetchWithTimeout(-1), 'should throw')
|
||||
assert.throws(() => getFetchWithTimeout({}), 'should throw')
|
||||
assert.throws(() => getFetchWithTimeout(true), 'should throw')
|
||||
})
|
||||
})
|
||||
|
@ -2,15 +2,15 @@ import { strict as assert } from 'assert'
|
||||
import {
|
||||
getEnvironmentType,
|
||||
sufficientBalance,
|
||||
isPrefixedFormattedHexString,
|
||||
} from '../../../app/scripts/lib/util'
|
||||
import { isPrefixedFormattedHexString } from '../../../shared/modules/utils'
|
||||
|
||||
import {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
ENVIRONMENT_TYPE_FULLSCREEN,
|
||||
ENVIRONMENT_TYPE_BACKGROUND,
|
||||
} from '../../../app/scripts/lib/enums'
|
||||
} from '../../../shared/constants/app'
|
||||
|
||||
describe('app utils', function () {
|
||||
describe('getEnvironmentType', function () {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { NETWORK_TYPE_RPC } from '../../shared/constants/network'
|
||||
|
||||
/**
|
||||
* @typedef {Object} FirstTimeState
|
||||
* @property {Object} config Initial configuration parameters
|
||||
@ -11,7 +13,7 @@ const initialState = {
|
||||
config: {},
|
||||
NetworkController: {
|
||||
provider: {
|
||||
type: 'rpc',
|
||||
type: NETWORK_TYPE_RPC,
|
||||
rpcUrl: 'http://localhost:8545',
|
||||
chainId: '0x539',
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ import migration51 from '../../../app/scripts/migrations/051'
|
||||
import {
|
||||
INFURA_PROVIDER_TYPES,
|
||||
NETWORK_TYPE_TO_ID_MAP,
|
||||
} from '../../../app/scripts/controllers/network/enums'
|
||||
} from '../../../shared/constants/network'
|
||||
|
||||
describe('migration #51', function () {
|
||||
it('should update the version metadata', async function () {
|
||||
|
@ -4,7 +4,7 @@ import { debounce } from 'lodash'
|
||||
import Fuse from 'fuse.js'
|
||||
import InputAdornment from '@material-ui/core/InputAdornment'
|
||||
import classnames from 'classnames'
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../app/scripts/lib/enums'
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../shared/constants/app'
|
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
|
||||
import Identicon from '../../ui/identicon'
|
||||
import SiteIcon from '../../ui/site-icon'
|
||||
|
@ -203,7 +203,7 @@
|
||||
}
|
||||
|
||||
&__check-mark-icon {
|
||||
background-image: url("images/check-white.svg");
|
||||
background-image: url("/images/check-white.svg");
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
|
||||
import {
|
||||
ENVIRONMENT_TYPE_POPUP,
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
} from '../../../../../../app/scripts/lib/enums'
|
||||
} from '../../../../../../shared/constants/app'
|
||||
import { getEnvironmentType } from '../../../../../../app/scripts/lib/util'
|
||||
import NetworkDisplay from '../../network-display'
|
||||
import Identicon from '../../../ui/identicon'
|
||||
|
@ -9,11 +9,10 @@ import {
|
||||
NETWORKS_ROUTE,
|
||||
NETWORKS_FORM_ROUTE,
|
||||
} from '../../../helpers/constants/routes'
|
||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../../app/scripts/lib/enums'
|
||||
import {
|
||||
getEnvironmentType,
|
||||
isPrefixedFormattedHexString,
|
||||
} from '../../../../../app/scripts/lib/util'
|
||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../../shared/constants/app'
|
||||
import { NETWORK_TYPE_RPC } from '../../../../../shared/constants/network'
|
||||
import { isPrefixedFormattedHexString } from '../../../../../shared/modules/utils'
|
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
|
||||
|
||||
import { Dropdown, DropdownMenuItem } from './components/dropdown'
|
||||
import NetworkDropdownIcon from './components/network-dropdown-icon'
|
||||
@ -117,7 +116,7 @@ class NetworkDropdown extends Component {
|
||||
return reversedRpcListDetail.map((entry) => {
|
||||
const { rpcUrl, chainId, ticker = 'ETH', nickname = '' } = entry
|
||||
const isCurrentRpcTarget =
|
||||
provider.type === 'rpc' && rpcUrl === provider.rpcUrl
|
||||
provider.type === NETWORK_TYPE_RPC && rpcUrl === provider.rpcUrl
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { connect } from 'react-redux'
|
||||
import { NETWORK_TYPE_RPC } from '../../../../../shared/constants/network'
|
||||
import * as actions from '../../../store/actions'
|
||||
import { getNetworkIdentifier } from '../../../selectors'
|
||||
import LoadingNetworkScreen from './loading-network-screen.component'
|
||||
@ -9,7 +10,9 @@ const mapStateToProps = (state) => {
|
||||
const { rpcUrl, chainId, ticker, nickname, type } = provider
|
||||
|
||||
const setProviderArgs =
|
||||
type === 'rpc' ? [rpcUrl, chainId, ticker, nickname] : [provider.type]
|
||||
type === NETWORK_TYPE_RPC
|
||||
? [rpcUrl, chainId, ticker, nickname]
|
||||
: [provider.type]
|
||||
|
||||
return {
|
||||
isLoadingNetwork: network === 'loading',
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext'
|
||||
import { useMetricEvent } from '../../../hooks/useMetricEvent'
|
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
|
||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../../app/scripts/lib/enums'
|
||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../../shared/constants/app'
|
||||
|
||||
export default function AccountOptionsMenu({ anchorElement, onClose }) {
|
||||
const t = useI18nContext()
|
||||
|
@ -5,7 +5,7 @@ import { useSelector } from 'react-redux'
|
||||
import SelectedAccount from '../selected-account'
|
||||
import ConnectedStatusIndicator from '../connected-status-indicator'
|
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../app/scripts/lib/enums'
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../shared/constants/app'
|
||||
import { CONNECTED_ACCOUNTS_ROUTE } from '../../../helpers/constants/routes'
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext'
|
||||
import { useMetricEvent } from '../../../hooks/useMetricEvent'
|
||||
|
@ -6,7 +6,7 @@ import * as actions from '../../../store/actions'
|
||||
import { resetCustomData as resetCustomGasData } from '../../../ducks/gas/gas.duck'
|
||||
import isMobileView from '../../../../lib/is-mobile-view'
|
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../app/scripts/lib/enums'
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../../shared/constants/app'
|
||||
|
||||
// Modal Components
|
||||
import ConfirmCustomizeGasModal from '../gas-customization/gas-modal-page-container'
|
||||
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
|
||||
import log from 'loglevel'
|
||||
import { BrowserQRCodeReader } from '@zxing/library'
|
||||
import { getEnvironmentType } from '../../../../../../app/scripts/lib/util'
|
||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../../../app/scripts/lib/enums'
|
||||
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../../../shared/constants/app'
|
||||
import Spinner from '../../../ui/spinner'
|
||||
import WebcamUtils from '../../../../../lib/webcam-utils'
|
||||
import PageContainerFooter from '../../../ui/page-container/page-container-footer/page-container-footer.component'
|
||||
|
@ -1,21 +1,7 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {
|
||||
MAINNET_NETWORK_ID,
|
||||
ROPSTEN_NETWORK_ID,
|
||||
RINKEBY_NETWORK_ID,
|
||||
KOVAN_NETWORK_ID,
|
||||
GOERLI_NETWORK_ID,
|
||||
} from '../../../../../app/scripts/controllers/network/enums'
|
||||
|
||||
const networkIdToTypeMap = {
|
||||
[MAINNET_NETWORK_ID]: 'mainnet',
|
||||
[ROPSTEN_NETWORK_ID]: 'ropsten',
|
||||
[RINKEBY_NETWORK_ID]: 'rinkeby',
|
||||
[GOERLI_NETWORK_ID]: 'goerli',
|
||||
[KOVAN_NETWORK_ID]: 'kovan',
|
||||
}
|
||||
import { NETWORK_TYPE_RPC } from '../../../../../shared/constants/network'
|
||||
|
||||
export default class NetworkDisplay extends Component {
|
||||
static defaultProps = {
|
||||
@ -23,9 +9,9 @@ export default class NetworkDisplay extends Component {
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
networkNickname: PropTypes.string.isRequired,
|
||||
networkType: PropTypes.string.isRequired,
|
||||
colored: PropTypes.bool,
|
||||
network: PropTypes.string,
|
||||
provider: PropTypes.object,
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
@ -33,12 +19,11 @@ export default class NetworkDisplay extends Component {
|
||||
}
|
||||
|
||||
renderNetworkIcon() {
|
||||
const { network } = this.props
|
||||
const networkClass = networkIdToTypeMap[network]
|
||||
const { networkType } = this.props
|
||||
|
||||
return networkClass ? (
|
||||
return networkType ? (
|
||||
<div
|
||||
className={`network-display__icon network-display__icon--${networkClass}`}
|
||||
className={`network-display__icon network-display__icon--${networkType}`}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@ -52,24 +37,19 @@ export default class NetworkDisplay extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
colored,
|
||||
network,
|
||||
provider: { type, nickname },
|
||||
} = this.props
|
||||
const networkClass = networkIdToTypeMap[network]
|
||||
const { colored, networkNickname, networkType } = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('network-display__container', {
|
||||
'network-display__container--colored': colored,
|
||||
[`network-display__container--${networkClass}`]:
|
||||
colored && networkClass,
|
||||
[`network-display__container--${networkType}`]:
|
||||
colored && networkType,
|
||||
})}
|
||||
>
|
||||
{networkClass ? (
|
||||
{networkType ? (
|
||||
<div
|
||||
className={`network-display__icon network-display__icon--${networkClass}`}
|
||||
className={`network-display__icon network-display__icon--${networkType}`}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@ -81,7 +61,9 @@ export default class NetworkDisplay extends Component {
|
||||
/>
|
||||
)}
|
||||
<div className="network-display__name">
|
||||
{type === 'rpc' && nickname ? nickname : this.context.t(type)}
|
||||
{networkType === NETWORK_TYPE_RPC && networkNickname
|
||||
? networkNickname
|
||||
: this.context.t(networkType)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { connect } from 'react-redux'
|
||||
import NetworkDisplay from './network-display.component'
|
||||
|
||||
const mapStateToProps = ({ metamask: { network, provider } }) => {
|
||||
const mapStateToProps = ({
|
||||
metamask: {
|
||||
provider: { nickname, type },
|
||||
},
|
||||
}) => {
|
||||
return {
|
||||
network,
|
||||
provider,
|
||||
networkNickname: nickname,
|
||||
networkType: type,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { ObjectInspector } from 'react-inspector'
|
||||
import {
|
||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||
MESSAGE_TYPE,
|
||||
} from '../../../../../app/scripts/lib/enums'
|
||||
} from '../../../../../shared/constants/app'
|
||||
import { getEnvironmentType } from '../../../../../app/scripts/lib/util'
|
||||
import Identicon from '../../ui/identicon'
|
||||
import AccountListItem from '../account-list-item'
|
||||
|
@ -2,7 +2,7 @@ import { connect } from 'react-redux'
|
||||
import { compose } from 'redux'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
|
||||
import { MESSAGE_TYPE } from '../../../../../app/scripts/lib/enums'
|
||||
import { MESSAGE_TYPE } from '../../../../../shared/constants/app'
|
||||
import { goHome } from '../../../store/actions'
|
||||
import {
|
||||
accountsWithSendEtherInfoSelector,
|
||||
|
@ -1,3 +1,3 @@
|
||||
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../../app/scripts/lib/enums'
|
||||
import { ENVIRONMENT_TYPE_NOTIFICATION } from '../../../../../shared/constants/app'
|
||||
|
||||
export { ENVIRONMENT_TYPE_NOTIFICATION }
|
||||
|
@ -2,7 +2,7 @@ import { connect } from 'react-redux'
|
||||
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck'
|
||||
import { accountsWithSendEtherInfoSelector } from '../../../selectors'
|
||||
import { getAccountByAddress } from '../../../helpers/utils/util'
|
||||
import { MESSAGE_TYPE } from '../../../../../app/scripts/lib/enums'
|
||||
import { MESSAGE_TYPE } from '../../../../../shared/constants/app'
|
||||
import SignatureRequest from './signature-request.component'
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
@ -34,7 +34,7 @@ import {
|
||||
setSwapsFromToken,
|
||||
} from '../../../ducks/swaps/swaps'
|
||||
import IconButton from '../../ui/icon-button'
|
||||
import { MAINNET_CHAIN_ID } from '../../../../../app/scripts/controllers/network/enums'
|
||||
import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network'
|
||||
import WalletOverview from './wallet-overview'
|
||||
|
||||
const EthOverview = ({ className }) => {
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
getCurrentKeyring,
|
||||
getCurrentChainId,
|
||||
} from '../../../selectors/selectors'
|
||||
import { MAINNET_CHAIN_ID } from '../../../../../app/scripts/controllers/network/enums'
|
||||
import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network'
|
||||
|
||||
import SwapIcon from '../../ui/icon/swap-icon.component'
|
||||
import SendIcon from '../../ui/icon/overview-send-icon.component'
|
||||
|
55
ui/app/components/ui/chip/chip.js
Normal file
55
ui/app/components/ui/chip/chip.js
Normal file
@ -0,0 +1,55 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import { omit } from 'lodash'
|
||||
import Typography from '../typography'
|
||||
import { COLORS } from '../../../helpers/constants/design-system'
|
||||
|
||||
export default function Chip({
|
||||
className,
|
||||
children,
|
||||
borderColor = COLORS.UI1,
|
||||
label,
|
||||
labelProps = {},
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
onClick,
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={classnames(className, 'chip', {
|
||||
'chip--with-left-icon': Boolean(leftIcon),
|
||||
'chip--with-right-icon': Boolean(rightIcon),
|
||||
[`chip--${borderColor}`]: true,
|
||||
})}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{leftIcon && <div className="chip__left-icon">{leftIcon}</div>}
|
||||
{children ?? (
|
||||
<Typography
|
||||
className="chip__label"
|
||||
variant="h6"
|
||||
tag="span"
|
||||
color="UI4"
|
||||
{...labelProps}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
{rightIcon && <div className="chip__right-icon">{rightIcon}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Chip.propTypes = {
|
||||
borderColor: PropTypes.oneOf(Object.values(COLORS)),
|
||||
label: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
labelProps: PropTypes.shape(omit(Typography.propTypes, ['className'])),
|
||||
leftIcon: PropTypes.node,
|
||||
rightIcon: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
}
|
48
ui/app/components/ui/chip/chip.scss
Normal file
48
ui/app/components/ui/chip/chip.scss
Normal file
@ -0,0 +1,48 @@
|
||||
@use "design-system";
|
||||
|
||||
.chip {
|
||||
$self: &;
|
||||
|
||||
border-radius: 100px;
|
||||
border: 1px solid design-system.$ui-1;
|
||||
padding: 8px 16px;
|
||||
margin: 0 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: max-content;
|
||||
|
||||
&__left-icon,
|
||||
&__right-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@each $variant, $color in design-system.$color-map {
|
||||
&--#{$variant} {
|
||||
border-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
&--with-left-icon,
|
||||
&--with-right-icon {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
&--with-left-icon {
|
||||
padding-left: 4px;
|
||||
|
||||
#{$self}__label {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&--with-right-icon {
|
||||
padding-right: 4px;
|
||||
#{$self}__label {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
82
ui/app/components/ui/chip/chip.stories.js
Normal file
82
ui/app/components/ui/chip/chip.stories.js
Normal file
@ -0,0 +1,82 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
|
||||
import React from 'react'
|
||||
import { select, text } from '@storybook/addon-knobs'
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
|
||||
import ApproveIcon from '../icon/approve-icon.component'
|
||||
import Identicon from '../identicon/identicon.component'
|
||||
import Chip from '.'
|
||||
|
||||
export default {
|
||||
title: 'Chip',
|
||||
}
|
||||
|
||||
export const Plain = ({
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
label = 'Hello',
|
||||
borderColor = COLORS.UI1,
|
||||
fontColor = COLORS.BLACK,
|
||||
}) => (
|
||||
<Chip
|
||||
leftIcon={leftIcon}
|
||||
rightIcon={rightIcon}
|
||||
label={text('label', label)}
|
||||
labelProps={{
|
||||
color: select('color', COLORS, fontColor),
|
||||
variant: select('typography', TYPOGRAPHY, TYPOGRAPHY.H6),
|
||||
}}
|
||||
borderColor={select('borderColor', COLORS, borderColor)}
|
||||
/>
|
||||
)
|
||||
|
||||
export const WithLeftIcon = () => (
|
||||
<Plain
|
||||
label="Done!"
|
||||
borderColor={COLORS.SUCCESS3}
|
||||
fontColor={COLORS.SUCCESS3}
|
||||
leftIcon={<ApproveIcon size={24} color="#4cd964" />}
|
||||
/>
|
||||
)
|
||||
|
||||
export const WithRightIcon = () => (
|
||||
<Plain
|
||||
label="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
borderColor={COLORS.UI4}
|
||||
fontColor={COLORS.UI4}
|
||||
rightIcon={
|
||||
<Identicon
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
diameter={25}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
export const WithBothIcons = () => (
|
||||
<Plain
|
||||
label="Account 1"
|
||||
borderColor={COLORS.UI4}
|
||||
fontColor={COLORS.UI4}
|
||||
rightIcon={
|
||||
<svg
|
||||
width="10"
|
||||
height="6"
|
||||
viewBox="0 0 10 6"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.45759 0.857142C9.45759 0.785714 9.42188 0.705357 9.3683 0.651785L8.92188 0.205357C8.8683 0.151785 8.78795 0.116071 8.71652 0.116071C8.64509 0.116071 8.56473 0.151785 8.51116 0.205357L5.00223 3.71429L1.4933 0.205357C1.43973 0.151785 1.35938 0.116071 1.28795 0.116071C1.20759 0.116071 1.13616 0.151785 1.08259 0.205357L0.636161 0.651785C0.582589 0.705357 0.546875 0.785714 0.546875 0.857142C0.546875 0.928571 0.582589 1.00893 0.636161 1.0625L4.79688 5.22321C4.85045 5.27679 4.9308 5.3125 5.00223 5.3125C5.07366 5.3125 5.15402 5.27679 5.20759 5.22321L9.3683 1.0625C9.42188 1.00893 9.45759 0.928571 9.45759 0.857142Z"
|
||||
fill="#24292E"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
leftIcon={
|
||||
<Identicon
|
||||
address="0x5CfE73b6021E818B776b421B1c4Db2474086a7e1"
|
||||
diameter={25}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
1
ui/app/components/ui/chip/index.js
Normal file
1
ui/app/components/ui/chip/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './chip'
|
50
ui/app/components/ui/color-indicator/color-indicator.js
Normal file
50
ui/app/components/ui/color-indicator/color-indicator.js
Normal file
@ -0,0 +1,50 @@
|
||||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import { COLORS } from '../../../helpers/constants/design-system'
|
||||
|
||||
export default function ColorIndicator({
|
||||
size = 'small',
|
||||
type = 'outlined',
|
||||
color = COLORS.UI4,
|
||||
borderColor,
|
||||
iconClassName,
|
||||
}) {
|
||||
const colorIndicatorClassName = classnames('color-indicator', {
|
||||
'color-indicator--filled': type === 'filled' || Boolean(iconClassName),
|
||||
'color-indicator--partial-filled': type === 'partial-filled',
|
||||
[`color-indicator--border-color-${borderColor}`]: Boolean(borderColor),
|
||||
[`color-indicator--color-${color}`]: true,
|
||||
[`color-indicator--size-${size}`]: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={colorIndicatorClassName}>
|
||||
{iconClassName ? (
|
||||
<i className={classnames('color-indicator__icon', iconClassName)} />
|
||||
) : (
|
||||
<span className="color-indicator__inner-circle" />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ColorIndicator.SIZES = {
|
||||
LARGE: 'large',
|
||||
MEDIUM: 'medium',
|
||||
SMALL: 'small,',
|
||||
}
|
||||
|
||||
ColorIndicator.TYPES = {
|
||||
FILLED: 'filled',
|
||||
PARTIAL: 'partial-filled',
|
||||
OUTLINE: 'outline',
|
||||
}
|
||||
|
||||
ColorIndicator.propTypes = {
|
||||
color: PropTypes.oneOf(Object.values(COLORS)),
|
||||
borderColor: PropTypes.oneOf(Object.values(COLORS)),
|
||||
size: PropTypes.oneOf(Object.values(ColorIndicator.SIZES)),
|
||||
iconClassName: PropTypes.string,
|
||||
type: PropTypes.oneOf(Object.values(ColorIndicator.TYPES)),
|
||||
}
|
61
ui/app/components/ui/color-indicator/color-indicator.scss
Normal file
61
ui/app/components/ui/color-indicator/color-indicator.scss
Normal file
@ -0,0 +1,61 @@
|
||||
@use "utilities";
|
||||
@use "design-system";
|
||||
|
||||
$sizes: (
|
||||
'large': 6,
|
||||
'medium': 5,
|
||||
'small': 4,
|
||||
);
|
||||
|
||||
.color-indicator {
|
||||
$self: &;
|
||||
|
||||
border: 1px solid transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&__inner-circle {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@each $variant, $size in $sizes {
|
||||
&--size-#{$variant} {
|
||||
height: #{2 * $size}px;
|
||||
width: #{2 * $size}px;
|
||||
border-radius: #{$size}px;
|
||||
|
||||
#{$self}__inner-circle {
|
||||
border-radius: #{$size}px;
|
||||
height: #{$size}px;
|
||||
width: #{$size}px;
|
||||
}
|
||||
|
||||
#{$self}__icon {
|
||||
font-size: #{1.25 * $size}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@each $variant, $color in design-system.$color-map {
|
||||
&--color-#{$variant} {
|
||||
border-color: $color;
|
||||
&#{$self}--partial-filled #{$self}__inner-circle {
|
||||
background-color: $color;
|
||||
}
|
||||
&#{$self}--filled {
|
||||
background-color: $color;
|
||||
}
|
||||
& #{$self}__icon {
|
||||
color: #{utilities.choose-contrast-color($color)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// separate iterator to ensure borderColor takes precedence
|
||||
@each $variant, $color in design-system.$color-map {
|
||||
&--border-color-#{$variant} {
|
||||
border-color: $color;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import { select } from '@storybook/addon-knobs'
|
||||
import { COLORS } from '../../../helpers/constants/design-system'
|
||||
import ColorIndicator from './color-indicator'
|
||||
|
||||
export default {
|
||||
title: 'ColorIndicator',
|
||||
}
|
||||
|
||||
export const colorIndicator = () => (
|
||||
<ColorIndicator
|
||||
size={select('size', ColorIndicator.SIZES, ColorIndicator.SIZES.LARGE)}
|
||||
type={select('type', ColorIndicator.TYPES, ColorIndicator.TYPES.FILLED)}
|
||||
color={select('color', COLORS, COLORS.PRIMARY1)}
|
||||
borderColor={select('borderColor', { NONE: undefined, ...COLORS })}
|
||||
/>
|
||||
)
|
||||
|
||||
export const withIcon = () => (
|
||||
<ColorIndicator
|
||||
size={select('size', ColorIndicator.SIZES, ColorIndicator.SIZES.LARGE)}
|
||||
type={select('type', ColorIndicator.TYPES, ColorIndicator.TYPES.FILLED)}
|
||||
color={select('color', COLORS, COLORS.PRIMARY1)}
|
||||
iconClassName="fa fa-question"
|
||||
borderColor={select('borderColor', { NONE: undefined, ...COLORS })}
|
||||
/>
|
||||
)
|
1
ui/app/components/ui/color-indicator/index.js
Normal file
1
ui/app/components/ui/color-indicator/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './color-indicator'
|
1
ui/app/components/ui/typography/index.js
Normal file
1
ui/app/components/ui/typography/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './typography'
|
58
ui/app/components/ui/typography/typography.js
Normal file
58
ui/app/components/ui/typography/typography.js
Normal file
@ -0,0 +1,58 @@
|
||||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
|
||||
|
||||
const { H6, H7, H8, H9 } = TYPOGRAPHY
|
||||
|
||||
export default function Typography({
|
||||
variant = TYPOGRAPHY.Paragraph,
|
||||
className,
|
||||
color = COLORS.BLACK,
|
||||
tag,
|
||||
children,
|
||||
spacing = 1,
|
||||
fontWeight = 'normal',
|
||||
align,
|
||||
}) {
|
||||
const computedClassName = classnames(
|
||||
'typography',
|
||||
className,
|
||||
`typography--${variant}`,
|
||||
`typography--align-${align}`,
|
||||
`typography--spacing-${spacing}`,
|
||||
`typography--color-${color}`,
|
||||
`typography--weight-${fontWeight}`,
|
||||
)
|
||||
|
||||
let Tag = tag ?? variant
|
||||
|
||||
if (Tag === TYPOGRAPHY.Paragraph) {
|
||||
Tag = 'p'
|
||||
} else if ([H7, H8, H9].includes(Tag)) {
|
||||
Tag = H6
|
||||
}
|
||||
|
||||
return <Tag className={computedClassName}>{children}</Tag>
|
||||
}
|
||||
|
||||
Typography.propTypes = {
|
||||
variant: PropTypes.oneOf(Object.values(TYPOGRAPHY)),
|
||||
children: PropTypes.node.isRequired,
|
||||
color: PropTypes.oneOf(Object.values(COLORS)),
|
||||
className: PropTypes.string,
|
||||
align: PropTypes.oneOf(['center', 'right']),
|
||||
spacing: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
fontWeight: PropTypes.oneOf(['bold', 'normal']),
|
||||
tag: PropTypes.oneOf([
|
||||
'p',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'span',
|
||||
'div',
|
||||
]),
|
||||
}
|
38
ui/app/components/ui/typography/typography.scss
Normal file
38
ui/app/components/ui/typography/typography.scss
Normal file
@ -0,0 +1,38 @@
|
||||
@use "design-system";
|
||||
@use "sass:map";
|
||||
|
||||
.typography {
|
||||
@include design-system.Paragraph;
|
||||
|
||||
@each $variant in map.keys(design-system.$typography-variants) {
|
||||
&--#{$variant} {
|
||||
@include design-system.typography($variant);
|
||||
}
|
||||
}
|
||||
|
||||
@each $variant, $color in design-system.$color-map {
|
||||
&--color-#{$variant} {
|
||||
color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
@each $variant, $weight in design-system.$typography-font-weights {
|
||||
&--weight-#{$variant} {
|
||||
font-weight: $weight;
|
||||
}
|
||||
}
|
||||
|
||||
&--align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&--align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@for $i from 1 through 8 {
|
||||
&--spacing-#{$i} {
|
||||
margin: #{$i * 4}px auto;
|
||||
}
|
||||
}
|
||||
}
|
53
ui/app/components/ui/typography/typography.stories.js
Normal file
53
ui/app/components/ui/typography/typography.stories.js
Normal file
@ -0,0 +1,53 @@
|
||||
import React from 'react'
|
||||
import { number, select, text } from '@storybook/addon-knobs'
|
||||
import { COLORS, TYPOGRAPHY } from '../../../helpers/constants/design-system'
|
||||
import Typography from '.'
|
||||
|
||||
export default {
|
||||
title: 'Typography',
|
||||
}
|
||||
|
||||
const fontWeightOptions = {
|
||||
bold: 'bold',
|
||||
normal: 'normal',
|
||||
}
|
||||
|
||||
const alignOptions = {
|
||||
left: undefined,
|
||||
center: 'center',
|
||||
right: 'right',
|
||||
}
|
||||
|
||||
export const list = () => (
|
||||
<div style={{ width: '80%', flexDirection: 'column' }}>
|
||||
{Object.values(TYPOGRAPHY).map((variant) => (
|
||||
<div key={variant} style={{ width: '100%' }}>
|
||||
<Typography
|
||||
variant={variant}
|
||||
color={select('color', COLORS, COLORS.BLACK)}
|
||||
spacing={number('spacing', 1, { range: true, min: 1, max: 8 })}
|
||||
align={select('align', alignOptions, undefined)}
|
||||
fontWeight={select('font weight', fontWeightOptions, 'normal')}
|
||||
>
|
||||
{variant}
|
||||
</Typography>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
export const TheQuickOrangeFox = () => (
|
||||
<div style={{ width: '80%', flexDirection: 'column' }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<Typography
|
||||
color={select('color', COLORS, COLORS.BLACK)}
|
||||
variant={select('variant', TYPOGRAPHY, TYPOGRAPHY.Paragraph)}
|
||||
spacing={number('spacing', 1, { range: true, min: 1, max: 8 })}
|
||||
align={select('align', alignOptions, undefined)}
|
||||
fontWeight={select('font weight', fontWeightOptions, 'normal')}
|
||||
>
|
||||
{text('content', 'The quick orange fox jumped over the lazy dog.')}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
@ -7,7 +7,9 @@
|
||||
@import 'button/buttons';
|
||||
@import 'card/index';
|
||||
@import 'check-box/index';
|
||||
@import 'chip/chip';
|
||||
@import 'circle-icon/index';
|
||||
@import 'color-indicator/color-indicator';
|
||||
@import 'currency-display/index';
|
||||
@import 'currency-input/index';
|
||||
@import 'dialog/dialog';
|
||||
@ -37,5 +39,6 @@
|
||||
@import 'toggle-button/index';
|
||||
@import 'token-balance/index';
|
||||
@import 'tooltip/index';
|
||||
@import 'typography/typography';
|
||||
@import 'unit-input/index';
|
||||
@import 'url-icon/index';
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user