mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Merge pull request #11439 from MetaMask/Version-v9.8.0
Version v9.8.0 RC
This commit is contained in:
commit
c87dce9ce5
11
CHANGELOG.md
11
CHANGELOG.md
@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [9.8.0]
|
||||||
|
### Added
|
||||||
|
- [#11435](https://github.com/MetaMask/metamask-extension/pull/11435): Add gas limit buffers for optimism network
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- [#11210](https://github.com/MetaMask/metamask-extension/pull/11210): Disable sending ERC-721 assets (NFTs)
|
||||||
|
- [#11418](https://github.com/MetaMask/metamask-extension/pull/11418): Use network gas estimate for gas limits of simple sends on custom networks
|
||||||
|
|
||||||
## [9.7.1]
|
## [9.7.1]
|
||||||
### Fixed
|
### Fixed
|
||||||
- [#11426](https://github.com/MetaMask/metamask-extension/pull/11426): Fixed bug that broke transaction speed up and cancel, when attempting those actions immediately after opening MetaMask
|
- [#11426](https://github.com/MetaMask/metamask-extension/pull/11426): Fixed bug that broke transaction speed up and cancel, when attempting those actions immediately after opening MetaMask
|
||||||
@ -2321,7 +2329,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Uncategorized
|
### Uncategorized
|
||||||
- Added the ability to restore accounts from seed words.
|
- Added the ability to restore accounts from seed words.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v9.7.1...HEAD
|
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v9.8.0...HEAD
|
||||||
|
[9.8.0]: https://github.com/MetaMask/metamask-extension/compare/v9.7.1...v9.8.0
|
||||||
[9.7.1]: https://github.com/MetaMask/metamask-extension/compare/v9.7.0...v9.7.1
|
[9.7.1]: https://github.com/MetaMask/metamask-extension/compare/v9.7.0...v9.7.1
|
||||||
[9.7.0]: https://github.com/MetaMask/metamask-extension/compare/v9.6.1...v9.7.0
|
[9.7.0]: https://github.com/MetaMask/metamask-extension/compare/v9.6.1...v9.7.0
|
||||||
[9.6.1]: https://github.com/MetaMask/metamask-extension/compare/v9.6.0...v9.6.1
|
[9.6.1]: https://github.com/MetaMask/metamask-extension/compare/v9.6.0...v9.6.1
|
||||||
|
@ -751,9 +751,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "የቅርብ ጊዜያት"
|
"message": "የቅርብ ጊዜያት"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "የተቀባይ አድራሻ"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "ፍለጋ፣ ለሕዝብ ክፍት የሆነ አድራሻ (0x), ወይም ENS"
|
"message": "ፍለጋ፣ ለሕዝብ ክፍት የሆነ አድራሻ (0x), ወይም ENS"
|
||||||
},
|
},
|
||||||
|
@ -747,9 +747,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "الحديث"
|
"message": "الحديث"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "عنوان المستلم"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "البحث، العنوان العام (0x)، أو ENS"
|
"message": "البحث، العنوان العام (0x)، أو ENS"
|
||||||
},
|
},
|
||||||
|
@ -750,9 +750,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Скорошни"
|
"message": "Скорошни"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Адрес на получателя"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Търсене, публичен адрес (0x) или ENS"
|
"message": "Търсене, публичен адрес (0x) или ENS"
|
||||||
},
|
},
|
||||||
|
@ -754,9 +754,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "সাম্প্রতিকগুলি"
|
"message": "সাম্প্রতিকগুলি"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "প্রাপকের ঠিকানা"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "অনুসন্ধান, সার্বজনীন ঠিকানা (0x), বা ENS"
|
"message": "অনুসন্ধান, সার্বজনীন ঠিকানা (0x), বা ENS"
|
||||||
},
|
},
|
||||||
|
@ -732,9 +732,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "Pots tornar a afegir aquesta fitxa en el futur anant a \"Afegir fitxa\" al menu d'opcions dels teus comptes."
|
"message": "Pots tornar a afegir aquesta fitxa en el futur anant a \"Afegir fitxa\" al menu d'opcions dels teus comptes."
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adreça del destinatari"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Cerca, adreça pública (0x), o ENS"
|
"message": "Cerca, adreça pública (0x), o ENS"
|
||||||
},
|
},
|
||||||
|
@ -308,9 +308,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "Tento token můžete v budoucnu přidat zpět s „Přidat token“ v nastavení účtu."
|
"message": "Tento token můžete v budoucnu přidat zpět s „Přidat token“ v nastavení účtu."
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adresa příjemce"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "Odmítnout"
|
"message": "Odmítnout"
|
||||||
},
|
},
|
||||||
|
@ -735,9 +735,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Seneste"
|
"message": "Seneste"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Modtagerens adresse"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Søg, offentlig adresse (0x) eller ENS"
|
"message": "Søg, offentlig adresse (0x) eller ENS"
|
||||||
},
|
},
|
||||||
|
@ -723,9 +723,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Letzte"
|
"message": "Letzte"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Empfängeradresse"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Suchen, öffentliche Adresse (0x) oder ENS"
|
"message": "Suchen, öffentliche Adresse (0x) oder ENS"
|
||||||
},
|
},
|
||||||
|
@ -751,9 +751,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Πρόσφατα"
|
"message": "Πρόσφατα"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Διεύθυνση Παραλήπτη"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Αναζήτηση, δημόσια διεύθυνση (0x) ή ENS"
|
"message": "Αναζήτηση, δημόσια διεύθυνση (0x) ή ENS"
|
||||||
},
|
},
|
||||||
|
@ -650,12 +650,21 @@
|
|||||||
"message": "The endpoint returned a different chain ID: $1",
|
"message": "The endpoint returned a different chain ID: $1",
|
||||||
"description": "$1 is the return value of eth_chainId from an RPC endpoint"
|
"description": "$1 is the return value of eth_chainId from an RPC endpoint"
|
||||||
},
|
},
|
||||||
|
"ensIllegalCharacter": {
|
||||||
|
"message": "Illegal Character for ENS."
|
||||||
|
},
|
||||||
"ensNotFoundOnCurrentNetwork": {
|
"ensNotFoundOnCurrentNetwork": {
|
||||||
"message": "ENS name not found on the current network. Try switching to Ethereum Mainnet."
|
"message": "ENS name not found on the current network. Try switching to Ethereum Mainnet."
|
||||||
},
|
},
|
||||||
|
"ensNotSupportedOnNetwork": {
|
||||||
|
"message": "Network does not support ENS"
|
||||||
|
},
|
||||||
"ensRegistrationError": {
|
"ensRegistrationError": {
|
||||||
"message": "Error in ENS name registration"
|
"message": "Error in ENS name registration"
|
||||||
},
|
},
|
||||||
|
"ensUnknownError": {
|
||||||
|
"message": "ENS Lookup failed."
|
||||||
|
},
|
||||||
"enterAnAlias": {
|
"enterAnAlias": {
|
||||||
"message": "Enter an alias"
|
"message": "Enter an alias"
|
||||||
},
|
},
|
||||||
@ -1174,6 +1183,9 @@
|
|||||||
"networkNameEthereum": {
|
"networkNameEthereum": {
|
||||||
"message": "Ethereum"
|
"message": "Ethereum"
|
||||||
},
|
},
|
||||||
|
"networkNamePolygon": {
|
||||||
|
"message": "Polygon"
|
||||||
|
},
|
||||||
"networkNameTestnet": {
|
"networkNameTestnet": {
|
||||||
"message": "Testnet"
|
"message": "Testnet"
|
||||||
},
|
},
|
||||||
@ -1451,9 +1463,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Recents"
|
"message": "Recents"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Recipient Address"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Search, public address (0x), or ENS"
|
"message": "Search, public address (0x), or ENS"
|
||||||
},
|
},
|
||||||
@ -2369,6 +2378,10 @@
|
|||||||
"message": "verify the network details",
|
"message": "verify the network details",
|
||||||
"description": "Serves as link text for the 'unrecognizedChain' key. This text will be embedded inside the translation for that key."
|
"description": "Serves as link text for the 'unrecognizedChain' key. This text will be embedded inside the translation for that key."
|
||||||
},
|
},
|
||||||
|
"unsendableAsset": {
|
||||||
|
"message": "Sending collectible (ERC-721) tokens is not currently supported",
|
||||||
|
"description": "This is an error message we show the user if they attempt to send a collectible asset type, for which currently don't support sending"
|
||||||
|
},
|
||||||
"updatedWithDate": {
|
"updatedWithDate": {
|
||||||
"message": "Updated $1"
|
"message": "Updated $1"
|
||||||
},
|
},
|
||||||
|
@ -1411,9 +1411,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Recientes"
|
"message": "Recientes"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Dirección del destinatario"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Búsqueda, dirección pública (0x) o ENS"
|
"message": "Búsqueda, dirección pública (0x) o ENS"
|
||||||
},
|
},
|
||||||
|
@ -1419,9 +1419,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Recientes"
|
"message": "Recientes"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Dirección del destinatario"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Búsqueda, dirección pública (0x) o ENS"
|
"message": "Búsqueda, dirección pública (0x) o ENS"
|
||||||
},
|
},
|
||||||
|
@ -744,9 +744,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Hiljutised"
|
"message": "Hiljutised"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Saaja aadress"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Otsing, avalik aadress (0x) või ENS"
|
"message": "Otsing, avalik aadress (0x) või ENS"
|
||||||
},
|
},
|
||||||
|
@ -754,9 +754,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "واپسین"
|
"message": "واپسین"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "آدرس دریافت کننده"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "جستجو، آدرس عمومی (0x)، یا ENS"
|
"message": "جستجو، آدرس عمومی (0x)، یا ENS"
|
||||||
},
|
},
|
||||||
|
@ -751,9 +751,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Viimeaikaiset"
|
"message": "Viimeaikaiset"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Vastaanottajan osoite"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Haku, julkinen osoite (0x) tai ENS"
|
"message": "Haku, julkinen osoite (0x) tai ENS"
|
||||||
},
|
},
|
||||||
|
@ -678,9 +678,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Kamakailan"
|
"message": "Kamakailan"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Address ng Recipient"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Maghanap, pampublikong address (0x), o ENS"
|
"message": "Maghanap, pampublikong address (0x), o ENS"
|
||||||
},
|
},
|
||||||
|
@ -736,9 +736,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Récents"
|
"message": "Récents"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adresse du destinataire"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Recherche, adresse publique (0x) ou ENS"
|
"message": "Recherche, adresse publique (0x) ou ENS"
|
||||||
},
|
},
|
||||||
|
@ -751,9 +751,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "אחרונים"
|
"message": "אחרונים"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "כתובת הנמען"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "חיפוש, כתובת ציבורית (0x), או ENS"
|
"message": "חיפוש, כתובת ציבורית (0x), או ENS"
|
||||||
},
|
},
|
||||||
|
@ -1411,9 +1411,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "हाल ही के"
|
"message": "हाल ही के"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "प्राप्तकर्ता का पता"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "खोज, सार्वजनिक पता (0x) या ENS"
|
"message": "खोज, सार्वजनिक पता (0x) या ENS"
|
||||||
},
|
},
|
||||||
|
@ -285,9 +285,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "आप अपने खाता विकल्प मेनू में .टोकन जोड़ें. पर जाकर भविष्य में इस टोकन को वापस जोड़ सकते हैं।"
|
"message": "आप अपने खाता विकल्प मेनू में .टोकन जोड़ें. पर जाकर भविष्य में इस टोकन को वापस जोड़ सकते हैं।"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "प्राप्तकर्ता पता"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "अस्वीकार"
|
"message": "अस्वीकार"
|
||||||
},
|
},
|
||||||
|
@ -747,9 +747,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Nedavno"
|
"message": "Nedavno"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adresa primatelja"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Pretraži, javne adrese (0x) ili ENS"
|
"message": "Pretraži, javne adrese (0x) ili ENS"
|
||||||
},
|
},
|
||||||
|
@ -450,9 +450,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "Ou ka ajoute token sa aprè sa ankò ou prale nan \"Ajoute token\" nan opsyon meni kont ou an."
|
"message": "Ou ka ajoute token sa aprè sa ankò ou prale nan \"Ajoute token\" nan opsyon meni kont ou an."
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adrès pou resevwa"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "Rejte"
|
"message": "Rejte"
|
||||||
},
|
},
|
||||||
|
@ -747,9 +747,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Legutóbbiak"
|
"message": "Legutóbbiak"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Címzett címe"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Keresés, nyilvános cím (0x) vagy ENS"
|
"message": "Keresés, nyilvános cím (0x) vagy ENS"
|
||||||
},
|
},
|
||||||
|
@ -1411,9 +1411,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Terkini"
|
"message": "Terkini"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Alamat Penerima"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Cari, alamat publik (0x), atau ENS"
|
"message": "Cari, alamat publik (0x), atau ENS"
|
||||||
},
|
},
|
||||||
|
@ -1201,9 +1201,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Recenti"
|
"message": "Recenti"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Indirizzo Destinatario"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Ricerca, indirizzo pubblico (0x) o ENS"
|
"message": "Ricerca, indirizzo pubblico (0x) o ENS"
|
||||||
},
|
},
|
||||||
|
@ -1411,9 +1411,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "最近"
|
"message": "最近"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "受信者のアドレス"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "検索、パブリック アドレス (0x)、または ENS"
|
"message": "検索、パブリック アドレス (0x)、または ENS"
|
||||||
},
|
},
|
||||||
|
@ -754,9 +754,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "ಇತ್ತೀಚಿನವುಗಳು"
|
"message": "ಇತ್ತೀಚಿನವುಗಳು"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "ಸ್ವೀಕರಿಸುವವರ ವಿಳಾಸ"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "ಸಾರ್ವಜನಿಕ ವಿಳಾಸ (0x) ಅಥವಾ ENS ಹುಡುಕಿ"
|
"message": "ಸಾರ್ವಜನಿಕ ವಿಳಾಸ (0x) ಅಥವಾ ENS ಹುಡುಕಿ"
|
||||||
},
|
},
|
||||||
|
@ -1415,9 +1415,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "최근"
|
"message": "최근"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "수신인 주소"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "검색, 공개 주소(0x) 또는 ENS"
|
"message": "검색, 공개 주소(0x) 또는 ENS"
|
||||||
},
|
},
|
||||||
|
@ -754,9 +754,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Naujausi"
|
"message": "Naujausi"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Gavėjo adresas"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Ieška, viešieji adresai (0x) arba ENS"
|
"message": "Ieška, viešieji adresai (0x) arba ENS"
|
||||||
},
|
},
|
||||||
|
@ -750,9 +750,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Nesenie"
|
"message": "Nesenie"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Saņēmēja adrese"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Meklēšana, publiskā adrese (0x) vai ENS"
|
"message": "Meklēšana, publiskā adrese (0x) vai ENS"
|
||||||
},
|
},
|
||||||
|
@ -731,9 +731,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Baru-baru ini"
|
"message": "Baru-baru ini"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Alamat Penerima"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Cari, alamat awam (0x), atau ENS"
|
"message": "Cari, alamat awam (0x), atau ENS"
|
||||||
},
|
},
|
||||||
|
@ -272,9 +272,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "U kunt dit token in de toekomst weer toevoegen door naar \"Token toevoegen\" te gaan in het menu met accountopties."
|
"message": "U kunt dit token in de toekomst weer toevoegen door naar \"Token toevoegen\" te gaan in het menu met accountopties."
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Geadresseerde adres"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "Afwijzen"
|
"message": "Afwijzen"
|
||||||
},
|
},
|
||||||
|
@ -741,9 +741,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Nylige"
|
"message": "Nylige"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Mottakeradresse"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Søk, offentlig adresse (0x) eller ENS"
|
"message": "Søk, offentlig adresse (0x) eller ENS"
|
||||||
},
|
},
|
||||||
|
@ -1419,9 +1419,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Mga Kamakailan"
|
"message": "Mga Kamakailan"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Address ng Tatanggap"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Maghanap, pampublikong address (0x), o ENS"
|
"message": "Maghanap, pampublikong address (0x), o ENS"
|
||||||
},
|
},
|
||||||
|
@ -748,9 +748,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Ostatnie"
|
"message": "Ostatnie"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adres odbiorcy"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Szukaj, adres publiczny (0x) lub ENS"
|
"message": "Szukaj, adres publiczny (0x) lub ENS"
|
||||||
},
|
},
|
||||||
|
@ -282,9 +282,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "Pode adicionar este token de novo clicando na opção “Adicionar token” no menu de opções da sua conta."
|
"message": "Pode adicionar este token de novo clicando na opção “Adicionar token” no menu de opções da sua conta."
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Endereço do Destinatário"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "Rejeitar"
|
"message": "Rejeitar"
|
||||||
},
|
},
|
||||||
|
@ -1405,9 +1405,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Recentes"
|
"message": "Recentes"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Endereço do destinatário"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Busca, endereço público (0x) ou ENS"
|
"message": "Busca, endereço público (0x) ou ENS"
|
||||||
},
|
},
|
||||||
|
@ -741,9 +741,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Recente"
|
"message": "Recente"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adresă destinatar"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Căutare, adresa publică (0x) sau ENS"
|
"message": "Căutare, adresa publică (0x) sau ENS"
|
||||||
},
|
},
|
||||||
|
@ -1411,9 +1411,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Недавние"
|
"message": "Недавние"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Адрес получателя"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Поиск, публичный адрес (0x) или ENS"
|
"message": "Поиск, публичный адрес (0x) или ENS"
|
||||||
},
|
},
|
||||||
|
@ -723,9 +723,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Posledné"
|
"message": "Posledné"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adresa příjemce"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Vyhľadávať verejnú adresu (0x) alebo ENS"
|
"message": "Vyhľadávať verejnú adresu (0x) alebo ENS"
|
||||||
},
|
},
|
||||||
|
@ -742,9 +742,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Nedavno"
|
"message": "Nedavno"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Prejemnikov naslov"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Iskanje, javni naslov (0x) ali ENS"
|
"message": "Iskanje, javni naslov (0x) ali ENS"
|
||||||
},
|
},
|
||||||
|
@ -745,9 +745,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Skorašnje"
|
"message": "Skorašnje"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Adresa primaoca"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Pretraga, javna adresa (0x) ili ENS"
|
"message": "Pretraga, javna adresa (0x) ili ENS"
|
||||||
},
|
},
|
||||||
|
@ -738,9 +738,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Senaste"
|
"message": "Senaste"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Mottagaradress"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Sök, allmän adress (0x) eller ENS"
|
"message": "Sök, allmän adress (0x) eller ENS"
|
||||||
},
|
},
|
||||||
|
@ -732,9 +732,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Za hivi karibuni"
|
"message": "Za hivi karibuni"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Anwani ya Mpokeaji"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Tafuta, anwani za umma (0x), au ENS"
|
"message": "Tafuta, anwani za umma (0x), au ENS"
|
||||||
},
|
},
|
||||||
|
@ -372,9 +372,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "உங்கள் கணக்கு விருப்பங்கள் மெனுவில் \"டோக்கனைச் சேர்\" என்பதன் மூலம் நீங்கள் எதிர்காலத்தில் இந்த டோக்கனை மீண்டும் சேர்க்கலாம்."
|
"message": "உங்கள் கணக்கு விருப்பங்கள் மெனுவில் \"டோக்கனைச் சேர்\" என்பதன் மூலம் நீங்கள் எதிர்காலத்தில் இந்த டோக்கனை மீண்டும் சேர்க்கலாம்."
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "பெறுநர் முகவரி"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "நிராகரி"
|
"message": "நிராகரி"
|
||||||
},
|
},
|
||||||
|
@ -375,9 +375,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "คุณสามารถเพิ่มโทเค็นนี้ในอนาคตได้โดยไปที่ “เพิ่มโทเค็น” ในเมนูตัวเลือกบัญชีของคุณ"
|
"message": "คุณสามารถเพิ่มโทเค็นนี้ในอนาคตได้โดยไปที่ “เพิ่มโทเค็น” ในเมนูตัวเลือกบัญชีของคุณ"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "แอดแดรสผู้รับ"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "ปฏิเสธ"
|
"message": "ปฏิเสธ"
|
||||||
},
|
},
|
||||||
|
@ -1192,9 +1192,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Mga Kamakailan"
|
"message": "Mga Kamakailan"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Address ng Tatanggap"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Maghanap, pampublikong address (0x), o ENS"
|
"message": "Maghanap, pampublikong address (0x), o ENS"
|
||||||
},
|
},
|
||||||
|
@ -324,9 +324,6 @@
|
|||||||
"readdToken": {
|
"readdToken": {
|
||||||
"message": "Gelecekte Bu jetonu hesap seçenekleri menüsünde “Jeton ekle”'ye giderek geri ekleyebilirsiniz."
|
"message": "Gelecekte Bu jetonu hesap seçenekleri menüsünde “Jeton ekle”'ye giderek geri ekleyebilirsiniz."
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Alıcı adresi"
|
|
||||||
},
|
|
||||||
"reject": {
|
"reject": {
|
||||||
"message": "Reddetmek"
|
"message": "Reddetmek"
|
||||||
},
|
},
|
||||||
|
@ -754,9 +754,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Останні"
|
"message": "Останні"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Адреса отримувача"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Пошук, публічна адреса (0x), або ENS"
|
"message": "Пошук, публічна адреса (0x), або ENS"
|
||||||
},
|
},
|
||||||
|
@ -1411,9 +1411,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "Gần đây"
|
"message": "Gần đây"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "Địa chỉ người nhận"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "Tìm kiếm, địa chỉ công khai (0x) hoặc ENS"
|
"message": "Tìm kiếm, địa chỉ công khai (0x) hoặc ENS"
|
||||||
},
|
},
|
||||||
|
@ -1195,9 +1195,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "最近记录"
|
"message": "最近记录"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "接收地址"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "查找、公用地址 (0x) 或 ENS"
|
"message": "查找、公用地址 (0x) 或 ENS"
|
||||||
},
|
},
|
||||||
|
@ -751,9 +751,6 @@
|
|||||||
"recents": {
|
"recents": {
|
||||||
"message": "最近"
|
"message": "最近"
|
||||||
},
|
},
|
||||||
"recipientAddress": {
|
|
||||||
"message": "接收位址"
|
|
||||||
},
|
|
||||||
"recipientAddressPlaceholder": {
|
"recipientAddressPlaceholder": {
|
||||||
"message": "搜尋,公開地址 (0x),或 ENS"
|
"message": "搜尋,公開地址 (0x),或 ENS"
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,7 @@ import PreferencesController from './preferences';
|
|||||||
|
|
||||||
describe('DetectTokensController', function () {
|
describe('DetectTokensController', function () {
|
||||||
const sandbox = sinon.createSandbox();
|
const sandbox = sinon.createSandbox();
|
||||||
let keyringMemStore, network, preferences;
|
let keyringMemStore, network, preferences, provider;
|
||||||
|
|
||||||
const noop = () => undefined;
|
const noop = () => undefined;
|
||||||
|
|
||||||
@ -23,12 +23,16 @@ describe('DetectTokensController', function () {
|
|||||||
keyringMemStore = new ObservableStore({ isUnlocked: false });
|
keyringMemStore = new ObservableStore({ isUnlocked: false });
|
||||||
network = new NetworkController();
|
network = new NetworkController();
|
||||||
network.setInfuraProjectId('foo');
|
network.setInfuraProjectId('foo');
|
||||||
preferences = new PreferencesController({ network });
|
network.initializeProvider(networkControllerProviderConfig);
|
||||||
|
provider = network.getProviderAndBlockTracker().provider;
|
||||||
|
preferences = new PreferencesController({ network, provider });
|
||||||
preferences.setAddresses([
|
preferences.setAddresses([
|
||||||
'0x7e57e2',
|
'0x7e57e2',
|
||||||
'0xbc86727e770de68b1060c91f6bb6945c73e10388',
|
'0xbc86727e770de68b1060c91f6bb6945c73e10388',
|
||||||
]);
|
]);
|
||||||
network.initializeProvider(networkControllerProviderConfig);
|
sandbox
|
||||||
|
.stub(preferences, '_detectIsERC721')
|
||||||
|
.returns(Promise.resolve(false));
|
||||||
});
|
});
|
||||||
|
|
||||||
after(function () {
|
after(function () {
|
||||||
@ -125,6 +129,7 @@ describe('DetectTokensController', function () {
|
|||||||
address: existingTokenAddress.toLowerCase(),
|
address: existingTokenAddress.toLowerCase(),
|
||||||
decimals: existingToken.decimals,
|
decimals: existingToken.decimals,
|
||||||
symbol: existingToken.symbol,
|
symbol: existingToken.symbol,
|
||||||
|
isERC721: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -177,11 +182,13 @@ describe('DetectTokensController', function () {
|
|||||||
address: existingTokenAddress.toLowerCase(),
|
address: existingTokenAddress.toLowerCase(),
|
||||||
decimals: existingToken.decimals,
|
decimals: existingToken.decimals,
|
||||||
symbol: existingToken.symbol,
|
symbol: existingToken.symbol,
|
||||||
|
isERC721: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
address: tokenAddressToAdd.toLowerCase(),
|
address: tokenAddressToAdd.toLowerCase(),
|
||||||
decimals: tokenToAdd.decimals,
|
decimals: tokenToAdd.decimals,
|
||||||
symbol: tokenToAdd.symbol,
|
symbol: tokenToAdd.symbol,
|
||||||
|
isERC721: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -234,11 +241,13 @@ describe('DetectTokensController', function () {
|
|||||||
address: existingTokenAddress.toLowerCase(),
|
address: existingTokenAddress.toLowerCase(),
|
||||||
decimals: existingToken.decimals,
|
decimals: existingToken.decimals,
|
||||||
symbol: existingToken.symbol,
|
symbol: existingToken.symbol,
|
||||||
|
isERC721: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
address: tokenAddressToAdd.toLowerCase(),
|
address: tokenAddressToAdd.toLowerCase(),
|
||||||
decimals: tokenToAdd.decimals,
|
decimals: tokenToAdd.decimals,
|
||||||
symbol: tokenToAdd.symbol,
|
symbol: tokenToAdd.symbol,
|
||||||
|
isERC721: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -2,14 +2,21 @@ import { strict as assert } from 'assert';
|
|||||||
import { ObservableStore } from '@metamask/obs-store';
|
import { ObservableStore } from '@metamask/obs-store';
|
||||||
import { ethErrors } from 'eth-rpc-errors';
|
import { ethErrors } from 'eth-rpc-errors';
|
||||||
import { normalize as normalizeAddress } from 'eth-sig-util';
|
import { normalize as normalizeAddress } from 'eth-sig-util';
|
||||||
import ethers from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
|
import abiERC721 from 'human-standard-collectible-abi';
|
||||||
|
import contractsMap from '@metamask/contract-metadata';
|
||||||
import { LISTED_CONTRACT_ADDRESSES } from '../../../shared/constants/tokens';
|
import { LISTED_CONTRACT_ADDRESSES } from '../../../shared/constants/tokens';
|
||||||
import { NETWORK_TYPE_TO_ID_MAP } from '../../../shared/constants/network';
|
import { NETWORK_TYPE_TO_ID_MAP } from '../../../shared/constants/network';
|
||||||
import { isPrefixedFormattedHexString } from '../../../shared/modules/network.utils';
|
import { isPrefixedFormattedHexString } from '../../../shared/modules/network.utils';
|
||||||
import { isValidHexAddress } from '../../../shared/modules/hexstring-utils';
|
import {
|
||||||
|
isValidHexAddress,
|
||||||
|
toChecksumHexAddress,
|
||||||
|
} from '../../../shared/modules/hexstring-utils';
|
||||||
import { NETWORK_EVENTS } from './network';
|
import { NETWORK_EVENTS } from './network';
|
||||||
|
|
||||||
|
const ERC721METADATA_INTERFACE_ID = '0x5b5e139f';
|
||||||
|
|
||||||
export default class PreferencesController {
|
export default class PreferencesController {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -73,11 +80,18 @@ export default class PreferencesController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.network = opts.network;
|
this.network = opts.network;
|
||||||
|
this.ethersProvider = new ethers.providers.Web3Provider(opts.provider);
|
||||||
this.store = new ObservableStore(initState);
|
this.store = new ObservableStore(initState);
|
||||||
this.store.setMaxListeners(12);
|
this.store.setMaxListeners(12);
|
||||||
this.openPopup = opts.openPopup;
|
this.openPopup = opts.openPopup;
|
||||||
this.migrateAddressBookState = opts.migrateAddressBookState;
|
this.migrateAddressBookState = opts.migrateAddressBookState;
|
||||||
this._subscribeToNetworkDidChange();
|
|
||||||
|
this.network.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
|
||||||
|
const { tokens, hiddenTokens } = this._getTokenRelatedStates();
|
||||||
|
this.ethersProvider = new ethers.providers.Web3Provider(opts.provider);
|
||||||
|
this._updateAccountTokens(tokens, this.getAssetImages(), hiddenTokens);
|
||||||
|
});
|
||||||
|
|
||||||
this._subscribeToInfuraAvailability();
|
this._subscribeToInfuraAvailability();
|
||||||
|
|
||||||
global.setPreference = (key, value) => {
|
global.setPreference = (key, value) => {
|
||||||
@ -393,6 +407,8 @@ export default class PreferencesController {
|
|||||||
});
|
});
|
||||||
const previousIndex = tokens.indexOf(previousEntry);
|
const previousIndex = tokens.indexOf(previousEntry);
|
||||||
|
|
||||||
|
newEntry.isERC721 = await this._detectIsERC721(newEntry.address);
|
||||||
|
|
||||||
if (previousEntry) {
|
if (previousEntry) {
|
||||||
tokens[previousIndex] = newEntry;
|
tokens[previousIndex] = newEntry;
|
||||||
} else {
|
} else {
|
||||||
@ -403,6 +419,24 @@ export default class PreferencesController {
|
|||||||
return Promise.resolve(tokens);
|
return Promise.resolve(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds isERC721 field to token object
|
||||||
|
* (Called when a user attempts to add tokens that were previously added which do not yet had isERC721 field)
|
||||||
|
*
|
||||||
|
* @param {string} tokenAddress - The contract address of the token requiring the isERC721 field added.
|
||||||
|
* @returns {Promise<object>} The new token object with the added isERC721 field.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async updateTokenType(tokenAddress) {
|
||||||
|
const { tokens } = this.store.getState();
|
||||||
|
const tokenIndex = tokens.findIndex((token) => {
|
||||||
|
return token.address === tokenAddress;
|
||||||
|
});
|
||||||
|
tokens[tokenIndex].isERC721 = await this._detectIsERC721(tokenAddress);
|
||||||
|
this.store.updateState({ tokens });
|
||||||
|
return Promise.resolve(tokens[tokenIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a specified token from the tokens array and adds it to hiddenTokens array
|
* Removes a specified token from the tokens array and adds it to hiddenTokens array
|
||||||
*
|
*
|
||||||
@ -480,11 +514,8 @@ export default class PreferencesController {
|
|||||||
let addressBookKey = rpcDetail.chainId;
|
let addressBookKey = rpcDetail.chainId;
|
||||||
if (!addressBookKey) {
|
if (!addressBookKey) {
|
||||||
// We need to find the networkId to determine what these addresses were keyed by
|
// We need to find the networkId to determine what these addresses were keyed by
|
||||||
const provider = new ethers.providers.JsonRpcProvider(
|
|
||||||
rpcDetail.rpcUrl,
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
addressBookKey = await provider.send('net_version');
|
addressBookKey = await this.ethersProvider.send('net_version');
|
||||||
assert(typeof addressBookKey === 'string');
|
assert(typeof addressBookKey === 'string');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.debug(error);
|
log.debug(error);
|
||||||
@ -701,17 +732,6 @@ export default class PreferencesController {
|
|||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
//
|
//
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle updating token list to reflect current network by listening for the
|
|
||||||
* NETWORK_DID_CHANGE event.
|
|
||||||
*/
|
|
||||||
_subscribeToNetworkDidChange() {
|
|
||||||
this.network.on(NETWORK_EVENTS.NETWORK_DID_CHANGE, () => {
|
|
||||||
const { tokens, hiddenTokens } = this._getTokenRelatedStates();
|
|
||||||
this._updateAccountTokens(tokens, this.getAssetImages(), hiddenTokens);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_subscribeToInfuraAvailability() {
|
_subscribeToInfuraAvailability() {
|
||||||
this.network.on(NETWORK_EVENTS.INFURA_IS_BLOCKED, () => {
|
this.network.on(NETWORK_EVENTS.INFURA_IS_BLOCKED, () => {
|
||||||
this._setInfuraBlocked(true);
|
this._setInfuraBlocked(true);
|
||||||
@ -763,6 +783,43 @@ export default class PreferencesController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects whether or not a token is ERC-721 compatible.
|
||||||
|
*
|
||||||
|
* @param {string} tokensAddress - the token contract address.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async _detectIsERC721(tokenAddress) {
|
||||||
|
const checksumAddress = toChecksumHexAddress(tokenAddress);
|
||||||
|
// if this token is already in our contract metadata map we don't need
|
||||||
|
// to check against the contract
|
||||||
|
if (contractsMap[checksumAddress]?.erc721 === true) {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
const tokenContract = await this._createEthersContract(
|
||||||
|
tokenAddress,
|
||||||
|
abiERC721,
|
||||||
|
this.ethersProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
return await tokenContract
|
||||||
|
.supportsInterface(ERC721METADATA_INTERFACE_ID)
|
||||||
|
.catch((error) => {
|
||||||
|
console.log('error', error);
|
||||||
|
log.debug(error);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async _createEthersContract(tokenAddress, abi, ethersProvider) {
|
||||||
|
const tokenContract = await new ethers.Contract(
|
||||||
|
tokenAddress,
|
||||||
|
abi,
|
||||||
|
ethersProvider,
|
||||||
|
);
|
||||||
|
return tokenContract;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates `tokens` and `hiddenTokens` of current account and network.
|
* Updates `tokens` and `hiddenTokens` of current account and network.
|
||||||
*
|
*
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
import contractMaps from '@metamask/contract-metadata';
|
||||||
|
import abiERC721 from 'human-standard-collectible-abi';
|
||||||
import {
|
import {
|
||||||
MAINNET_CHAIN_ID,
|
MAINNET_CHAIN_ID,
|
||||||
RINKEBY_CHAIN_ID,
|
RINKEBY_CHAIN_ID,
|
||||||
} from '../../../shared/constants/network';
|
} from '../../../shared/constants/network';
|
||||||
import PreferencesController from './preferences';
|
import PreferencesController from './preferences';
|
||||||
|
import NetworkController from './network';
|
||||||
|
|
||||||
describe('preferences controller', function () {
|
describe('preferences controller', function () {
|
||||||
let preferencesController;
|
let preferencesController;
|
||||||
@ -13,19 +16,32 @@ describe('preferences controller', function () {
|
|||||||
let triggerNetworkChange;
|
let triggerNetworkChange;
|
||||||
let switchToMainnet;
|
let switchToMainnet;
|
||||||
let switchToRinkeby;
|
let switchToRinkeby;
|
||||||
|
let provider;
|
||||||
const migrateAddressBookState = sinon.stub();
|
const migrateAddressBookState = sinon.stub();
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
currentChainId = MAINNET_CHAIN_ID;
|
currentChainId = MAINNET_CHAIN_ID;
|
||||||
network = {
|
const networkControllerProviderConfig = {
|
||||||
getCurrentChainId: () => currentChainId,
|
getAccounts: () => undefined,
|
||||||
on: sinon.spy(),
|
|
||||||
};
|
};
|
||||||
|
network = new NetworkController();
|
||||||
|
network.setInfuraProjectId('foo');
|
||||||
|
network.initializeProvider(networkControllerProviderConfig);
|
||||||
|
provider = network.getProviderAndBlockTracker().provider;
|
||||||
|
|
||||||
|
sandbox.stub(network, 'getCurrentChainId').callsFake(() => currentChainId);
|
||||||
|
sandbox
|
||||||
|
.stub(network, 'getProviderConfig')
|
||||||
|
.callsFake(() => ({ type: 'mainnet' }));
|
||||||
|
const spy = sandbox.spy(network, 'on');
|
||||||
|
|
||||||
preferencesController = new PreferencesController({
|
preferencesController = new PreferencesController({
|
||||||
migrateAddressBookState,
|
migrateAddressBookState,
|
||||||
network,
|
network,
|
||||||
|
provider,
|
||||||
});
|
});
|
||||||
triggerNetworkChange = network.on.firstCall.args[1];
|
triggerNetworkChange = spy.firstCall.args[1];
|
||||||
switchToMainnet = () => {
|
switchToMainnet = () => {
|
||||||
currentChainId = MAINNET_CHAIN_ID;
|
currentChainId = MAINNET_CHAIN_ID;
|
||||||
triggerNetworkChange();
|
triggerNetworkChange();
|
||||||
@ -86,6 +102,104 @@ describe('preferences controller', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('updateTokenType', function () {
|
||||||
|
it('should add isERC721 = true to token object in state when token is collectible and in our contract-metadata repo', async function () {
|
||||||
|
const contractAddresses = Object.keys(contractMaps);
|
||||||
|
const erc721ContractAddresses = contractAddresses.filter(
|
||||||
|
(contractAddress) => contractMaps[contractAddress].erc721 === true,
|
||||||
|
);
|
||||||
|
const address = erc721ContractAddresses[0];
|
||||||
|
const { symbol, decimals } = contractMaps[address];
|
||||||
|
preferencesController.store.updateState({
|
||||||
|
tokens: [{ address, symbol, decimals }],
|
||||||
|
});
|
||||||
|
const result = await preferencesController.updateTokenType(address);
|
||||||
|
assert.equal(result.isERC721, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add isERC721 = true to token object in state when token is collectible and not in our contract-metadata repo', async function () {
|
||||||
|
const tokenAddress = '0xda5584cc586d07c7141aa427224a4bd58e64af7d';
|
||||||
|
preferencesController.store.updateState({
|
||||||
|
tokens: [
|
||||||
|
{
|
||||||
|
address: tokenAddress,
|
||||||
|
symbol: 'TESTNFT',
|
||||||
|
decimals: '0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
sinon
|
||||||
|
.stub(preferencesController, '_detectIsERC721')
|
||||||
|
.callsFake(() => true);
|
||||||
|
|
||||||
|
const result = await preferencesController.updateTokenType(tokenAddress);
|
||||||
|
assert.equal(
|
||||||
|
preferencesController._detectIsERC721.getCall(0).args[0],
|
||||||
|
tokenAddress,
|
||||||
|
);
|
||||||
|
assert.equal(result.isERC721, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('_detectIsERC721', function () {
|
||||||
|
it('should return true when token is in our contract-metadata repo', async function () {
|
||||||
|
const tokenAddress = '0x06012c8cf97BEaD5deAe237070F9587f8E7A266d';
|
||||||
|
|
||||||
|
const result = await preferencesController._detectIsERC721(tokenAddress);
|
||||||
|
assert.equal(result, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when the token is not in our contract-metadata repo but tokenContract.supportsInterface returns true', async function () {
|
||||||
|
const tokenAddress = '0xda5584cc586d07c7141aa427224a4bd58e64af7d';
|
||||||
|
|
||||||
|
const supportsInterfaceStub = sinon.stub().returns(Promise.resolve(true));
|
||||||
|
sinon
|
||||||
|
.stub(preferencesController, '_createEthersContract')
|
||||||
|
.callsFake(() => ({ supportsInterface: supportsInterfaceStub }));
|
||||||
|
|
||||||
|
const result = await preferencesController._detectIsERC721(tokenAddress);
|
||||||
|
assert.equal(
|
||||||
|
preferencesController._createEthersContract.getCall(0).args[0],
|
||||||
|
tokenAddress,
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
preferencesController._createEthersContract.getCall(0).args[1],
|
||||||
|
abiERC721,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
preferencesController._createEthersContract.getCall(0).args[2],
|
||||||
|
preferencesController.ethersProvider,
|
||||||
|
);
|
||||||
|
assert.equal(result, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when the token is not in our contract-metadata repo and tokenContract.supportsInterface returns false', async function () {
|
||||||
|
const tokenAddress = '0xda5584cc586d07c7141aa427224a4bd58e64af7d';
|
||||||
|
|
||||||
|
const supportsInterfaceStub = sinon
|
||||||
|
.stub()
|
||||||
|
.returns(Promise.resolve(false));
|
||||||
|
sinon
|
||||||
|
.stub(preferencesController, '_createEthersContract')
|
||||||
|
.callsFake(() => ({ supportsInterface: supportsInterfaceStub }));
|
||||||
|
|
||||||
|
const result = await preferencesController._detectIsERC721(tokenAddress);
|
||||||
|
assert.equal(
|
||||||
|
preferencesController._createEthersContract.getCall(0).args[0],
|
||||||
|
tokenAddress,
|
||||||
|
);
|
||||||
|
assert.deepEqual(
|
||||||
|
preferencesController._createEthersContract.getCall(0).args[1],
|
||||||
|
abiERC721,
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
preferencesController._createEthersContract.getCall(0).args[2],
|
||||||
|
preferencesController.ethersProvider,
|
||||||
|
);
|
||||||
|
assert.equal(result, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('removeAddress', function () {
|
describe('removeAddress', function () {
|
||||||
it('should remove an address from state', function () {
|
it('should remove an address from state', function () {
|
||||||
preferencesController.setAddresses(['0xda22le', '0x7e57e2']);
|
preferencesController.setAddresses(['0xda22le', '0x7e57e2']);
|
||||||
@ -291,7 +405,12 @@ describe('preferences controller', function () {
|
|||||||
assert.equal(tokens.length, 1, 'one token removed');
|
assert.equal(tokens.length, 1, 'one token removed');
|
||||||
|
|
||||||
const [token1] = tokens;
|
const [token1] = tokens;
|
||||||
assert.deepEqual(token1, { address: '0xb', symbol: 'B', decimals: 5 });
|
assert.deepEqual(token1, {
|
||||||
|
address: '0xb',
|
||||||
|
symbol: 'B',
|
||||||
|
decimals: 5,
|
||||||
|
isERC721: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should remove a token from its state on corresponding address', async function () {
|
it('should remove a token from its state on corresponding address', async function () {
|
||||||
@ -310,7 +429,12 @@ describe('preferences controller', function () {
|
|||||||
assert.equal(tokensFirst.length, 1, 'one token removed in account');
|
assert.equal(tokensFirst.length, 1, 'one token removed in account');
|
||||||
|
|
||||||
const [token1] = tokensFirst;
|
const [token1] = tokensFirst;
|
||||||
assert.deepEqual(token1, { address: '0xb', symbol: 'B', decimals: 5 });
|
assert.deepEqual(token1, {
|
||||||
|
address: '0xb',
|
||||||
|
symbol: 'B',
|
||||||
|
decimals: 5,
|
||||||
|
isERC721: false,
|
||||||
|
});
|
||||||
|
|
||||||
await preferencesController.setSelectedAddress('0x7e57e3');
|
await preferencesController.setSelectedAddress('0x7e57e3');
|
||||||
const tokensSecond = preferencesController.getTokens();
|
const tokensSecond = preferencesController.getTokens();
|
||||||
@ -335,7 +459,12 @@ describe('preferences controller', function () {
|
|||||||
assert.equal(tokensFirst.length, 1, 'one token removed in network');
|
assert.equal(tokensFirst.length, 1, 'one token removed in network');
|
||||||
|
|
||||||
const [token1] = tokensFirst;
|
const [token1] = tokensFirst;
|
||||||
assert.deepEqual(token1, { address: '0xb', symbol: 'B', decimals: 5 });
|
assert.deepEqual(token1, {
|
||||||
|
address: '0xb',
|
||||||
|
symbol: 'B',
|
||||||
|
decimals: 5,
|
||||||
|
isERC721: false,
|
||||||
|
});
|
||||||
|
|
||||||
switchToRinkeby();
|
switchToRinkeby();
|
||||||
const tokensSecond = preferencesController.getTokens();
|
const tokensSecond = preferencesController.getTokens();
|
||||||
|
@ -19,7 +19,6 @@ import { isSwapsDefaultTokenAddress } from '../../../shared/modules/swaps.utils'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
fetchTradesInfo as defaultFetchTradesInfo,
|
fetchTradesInfo as defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness as defaultFetchSwapsFeatureLiveness,
|
|
||||||
fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime,
|
fetchSwapsQuoteRefreshTime as defaultFetchSwapsQuoteRefreshTime,
|
||||||
} from '../../../ui/pages/swaps/swaps.util';
|
} from '../../../ui/pages/swaps/swaps.util';
|
||||||
import { MINUTE, SECOND } from '../../../shared/constants/time';
|
import { MINUTE, SECOND } from '../../../shared/constants/time';
|
||||||
@ -73,6 +72,7 @@ const initialState = {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: true,
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: false,
|
||||||
swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
|
swapsQuoteRefreshTime: FALLBACK_QUOTE_REFRESH_TIME,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -85,7 +85,6 @@ export default class SwapsController {
|
|||||||
getProviderConfig,
|
getProviderConfig,
|
||||||
tokenRatesStore,
|
tokenRatesStore,
|
||||||
fetchTradesInfo = defaultFetchTradesInfo,
|
fetchTradesInfo = defaultFetchTradesInfo,
|
||||||
fetchSwapsFeatureLiveness = defaultFetchSwapsFeatureLiveness,
|
|
||||||
fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime,
|
fetchSwapsQuoteRefreshTime = defaultFetchSwapsQuoteRefreshTime,
|
||||||
getCurrentChainId,
|
getCurrentChainId,
|
||||||
}) {
|
}) {
|
||||||
@ -94,7 +93,6 @@ export default class SwapsController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this._fetchTradesInfo = fetchTradesInfo;
|
this._fetchTradesInfo = fetchTradesInfo;
|
||||||
this._fetchSwapsFeatureLiveness = fetchSwapsFeatureLiveness;
|
|
||||||
this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime;
|
this._fetchSwapsQuoteRefreshTime = fetchSwapsQuoteRefreshTime;
|
||||||
this._getCurrentChainId = getCurrentChainId;
|
this._getCurrentChainId = getCurrentChainId;
|
||||||
|
|
||||||
@ -119,15 +117,19 @@ export default class SwapsController {
|
|||||||
// Sets the refresh rate for quote updates from the MetaSwap API
|
// Sets the refresh rate for quote updates from the MetaSwap API
|
||||||
async _setSwapsQuoteRefreshTime() {
|
async _setSwapsQuoteRefreshTime() {
|
||||||
const chainId = this._getCurrentChainId();
|
const chainId = this._getCurrentChainId();
|
||||||
|
const { swapsState } = this.store.getState();
|
||||||
|
|
||||||
// Default to fallback time unless API returns valid response
|
// Default to fallback time unless API returns valid response
|
||||||
let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME;
|
let swapsQuoteRefreshTime = FALLBACK_QUOTE_REFRESH_TIME;
|
||||||
try {
|
try {
|
||||||
swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime(chainId);
|
swapsQuoteRefreshTime = await this._fetchSwapsQuoteRefreshTime(
|
||||||
|
chainId,
|
||||||
|
swapsState.useNewSwapsApi,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Request for swaps quote refresh time failed: ', e);
|
console.error('Request for swaps quote refresh time failed: ', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { swapsState } = this.store.getState();
|
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: { ...swapsState, swapsQuoteRefreshTime },
|
swapsState: { ...swapsState, swapsQuoteRefreshTime },
|
||||||
});
|
});
|
||||||
@ -162,6 +164,9 @@ export default class SwapsController {
|
|||||||
isPolledRequest,
|
isPolledRequest,
|
||||||
) {
|
) {
|
||||||
const { chainId } = fetchParamsMetaData;
|
const { chainId } = fetchParamsMetaData;
|
||||||
|
const {
|
||||||
|
swapsState: { useNewSwapsApi },
|
||||||
|
} = this.store.getState();
|
||||||
|
|
||||||
if (!fetchParams) {
|
if (!fetchParams) {
|
||||||
return null;
|
return null;
|
||||||
@ -182,7 +187,10 @@ export default class SwapsController {
|
|||||||
this.indexOfNewestCallInFlight = indexOfCurrentCall;
|
this.indexOfNewestCallInFlight = indexOfCurrentCall;
|
||||||
|
|
||||||
let [newQuotes] = await Promise.all([
|
let [newQuotes] = await Promise.all([
|
||||||
this._fetchTradesInfo(fetchParams, fetchParamsMetaData),
|
this._fetchTradesInfo(fetchParams, {
|
||||||
|
...fetchParamsMetaData,
|
||||||
|
useNewSwapsApi,
|
||||||
|
}),
|
||||||
this._setSwapsQuoteRefreshTime(),
|
this._setSwapsQuoteRefreshTime(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -449,22 +457,23 @@ export default class SwapsController {
|
|||||||
this.store.updateState({ swapsState: { ...swapsState, routeState } });
|
this.store.updateState({ swapsState: { ...swapsState, routeState } });
|
||||||
}
|
}
|
||||||
|
|
||||||
setSwapsLiveness(swapsFeatureIsLive) {
|
setSwapsLiveness(swapsLiveness) {
|
||||||
const { swapsState } = this.store.getState();
|
const { swapsState } = this.store.getState();
|
||||||
|
const { swapsFeatureIsLive, useNewSwapsApi } = swapsLiveness;
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: { ...swapsState, swapsFeatureIsLive },
|
swapsState: { ...swapsState, swapsFeatureIsLive, useNewSwapsApi },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
resetPostFetchState() {
|
resetPostFetchState() {
|
||||||
const { swapsState } = this.store.getState();
|
const { swapsState } = this.store.getState();
|
||||||
|
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: {
|
swapsState: {
|
||||||
...initialState.swapsState,
|
...initialState.swapsState,
|
||||||
tokens: swapsState.tokens,
|
tokens: swapsState.tokens,
|
||||||
fetchParams: swapsState.fetchParams,
|
fetchParams: swapsState.fetchParams,
|
||||||
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
swapsFeatureIsLive: swapsState.swapsFeatureIsLive,
|
||||||
|
useNewSwapsApi: swapsState.useNewSwapsApi,
|
||||||
swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime,
|
swapsQuoteRefreshTime: swapsState.swapsQuoteRefreshTime,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -473,7 +482,6 @@ export default class SwapsController {
|
|||||||
|
|
||||||
resetSwapsState() {
|
resetSwapsState() {
|
||||||
const { swapsState } = this.store.getState();
|
const { swapsState } = this.store.getState();
|
||||||
|
|
||||||
this.store.updateState({
|
this.store.updateState({
|
||||||
swapsState: {
|
swapsState: {
|
||||||
...initialState.swapsState,
|
...initialState.swapsState,
|
||||||
|
@ -128,13 +128,13 @@ const EMPTY_INIT_STATE = {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: true,
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: false,
|
||||||
swapsQuoteRefreshTime: 60000,
|
swapsQuoteRefreshTime: 60000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sandbox = sinon.createSandbox();
|
const sandbox = sinon.createSandbox();
|
||||||
const fetchTradesInfoStub = sandbox.stub();
|
const fetchTradesInfoStub = sandbox.stub();
|
||||||
const fetchSwapsFeatureLivenessStub = sandbox.stub();
|
|
||||||
const fetchSwapsQuoteRefreshTimeStub = sandbox.stub();
|
const fetchSwapsQuoteRefreshTimeStub = sandbox.stub();
|
||||||
const getCurrentChainIdStub = sandbox.stub();
|
const getCurrentChainIdStub = sandbox.stub();
|
||||||
getCurrentChainIdStub.returns(MAINNET_CHAIN_ID);
|
getCurrentChainIdStub.returns(MAINNET_CHAIN_ID);
|
||||||
@ -150,7 +150,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub,
|
fetchSwapsQuoteRefreshTime: fetchSwapsQuoteRefreshTimeStub,
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
@ -201,7 +200,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
@ -226,7 +224,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
@ -251,7 +248,6 @@ describe('SwapsController', function () {
|
|||||||
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
|
||||||
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
tokenRatesStore: MOCK_TOKEN_RATES_STORE,
|
||||||
fetchTradesInfo: fetchTradesInfoStub,
|
fetchTradesInfo: fetchTradesInfoStub,
|
||||||
fetchSwapsFeatureLiveness: fetchSwapsFeatureLivenessStub,
|
|
||||||
getCurrentChainId: getCurrentChainIdStub,
|
getCurrentChainId: getCurrentChainIdStub,
|
||||||
});
|
});
|
||||||
const currentEthersInstance = swapsController.ethersProvider;
|
const currentEthersInstance = swapsController.ethersProvider;
|
||||||
@ -658,6 +654,7 @@ describe('SwapsController', function () {
|
|||||||
const quotes = await swapsController.fetchAndSetQuotes(undefined);
|
const quotes = await swapsController.fetchAndSetQuotes(undefined);
|
||||||
assert.strictEqual(quotes, null);
|
assert.strictEqual(quotes, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls fetchTradesInfo with the given fetchParams and returns the correct quotes', async function () {
|
it('calls fetchTradesInfo with the given fetchParams and returns the correct quotes', async function () {
|
||||||
fetchTradesInfoStub.resolves(getMockQuotes());
|
fetchTradesInfoStub.resolves(getMockQuotes());
|
||||||
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime());
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime());
|
||||||
@ -695,15 +692,15 @@ describe('SwapsController', function () {
|
|||||||
metaMaskFeeInEth: '0.5050505050505050505',
|
metaMaskFeeInEth: '0.5050505050505050505',
|
||||||
ethValueOfTokens: '50',
|
ethValueOfTokens: '50',
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
fetchTradesInfoStub.calledOnceWithExactly(
|
fetchTradesInfoStub.calledOnceWithExactly(MOCK_FETCH_PARAMS, {
|
||||||
MOCK_FETCH_PARAMS,
|
...MOCK_FETCH_METADATA,
|
||||||
MOCK_FETCH_METADATA,
|
useNewSwapsApi: false,
|
||||||
),
|
}),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('performs the allowance check', async function () {
|
it('performs the allowance check', async function () {
|
||||||
fetchTradesInfoStub.resolves(getMockQuotes());
|
fetchTradesInfoStub.resolves(getMockQuotes());
|
||||||
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime());
|
fetchSwapsQuoteRefreshTimeStub.resolves(getMockQuoteRefreshTime());
|
||||||
@ -878,12 +875,14 @@ describe('SwapsController', function () {
|
|||||||
const tokens = 'test';
|
const tokens = 'test';
|
||||||
const fetchParams = 'test';
|
const fetchParams = 'test';
|
||||||
const swapsFeatureIsLive = false;
|
const swapsFeatureIsLive = false;
|
||||||
|
const useNewSwapsApi = false;
|
||||||
const swapsQuoteRefreshTime = 0;
|
const swapsQuoteRefreshTime = 0;
|
||||||
swapsController.store.updateState({
|
swapsController.store.updateState({
|
||||||
swapsState: {
|
swapsState: {
|
||||||
tokens,
|
tokens,
|
||||||
fetchParams,
|
fetchParams,
|
||||||
swapsFeatureIsLive,
|
swapsFeatureIsLive,
|
||||||
|
useNewSwapsApi,
|
||||||
swapsQuoteRefreshTime,
|
swapsQuoteRefreshTime,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -25,6 +25,7 @@ export default class ThreeBoxController {
|
|||||||
addressBookController,
|
addressBookController,
|
||||||
version,
|
version,
|
||||||
getKeyringControllerState,
|
getKeyringControllerState,
|
||||||
|
trackMetaMetricsEvent,
|
||||||
} = opts;
|
} = opts;
|
||||||
|
|
||||||
this.preferencesController = preferencesController;
|
this.preferencesController = preferencesController;
|
||||||
@ -59,6 +60,7 @@ export default class ThreeBoxController {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
this._trackMetaMetricsEvent = trackMetaMetricsEvent;
|
||||||
|
|
||||||
const initState = {
|
const initState = {
|
||||||
threeBoxSyncingAllowed: false,
|
threeBoxSyncingAllowed: false,
|
||||||
@ -83,6 +85,12 @@ export default class ThreeBoxController {
|
|||||||
async init() {
|
async init() {
|
||||||
const accounts = await this.keyringController.getAccounts();
|
const accounts = await this.keyringController.getAccounts();
|
||||||
this.address = accounts[0];
|
this.address = accounts[0];
|
||||||
|
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Initiated',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
|
|
||||||
if (this.address && !(this.box && this.store.getState().threeBoxSynced)) {
|
if (this.address && !(this.box && this.store.getState().threeBoxSynced)) {
|
||||||
await this.new3Box();
|
await this.new3Box();
|
||||||
}
|
}
|
||||||
@ -140,8 +148,18 @@ export default class ThreeBoxController {
|
|||||||
backupExists = threeBoxConfig.spaces && threeBoxConfig.spaces.metamask;
|
backupExists = threeBoxConfig.spaces && threeBoxConfig.spaces.metamask;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message.match(/^Error: Invalid response \(404\)/u)) {
|
if (e.message.match(/^Error: Invalid response \(404\)/u)) {
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Backup does not exist',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
|
|
||||||
backupExists = false;
|
backupExists = false;
|
||||||
} else {
|
} else {
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Config Error',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,9 +193,19 @@ export default class ThreeBoxController {
|
|||||||
this.store.updateState(stateUpdate);
|
this.store.updateState(stateUpdate);
|
||||||
|
|
||||||
log.debug('3Box space sync done');
|
log.debug('3Box space sync done');
|
||||||
|
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Synced',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Initiation Error',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
|
|
||||||
console.error(e);
|
console.error(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -216,13 +244,28 @@ export default class ThreeBoxController {
|
|||||||
preferences && this.preferencesController.store.updateState(preferences);
|
preferences && this.preferencesController.store.updateState(preferences);
|
||||||
addressBook && this.addressBookController.update(addressBook, true);
|
addressBook && this.addressBookController.update(addressBook, true);
|
||||||
this.setShowRestorePromptToFalse();
|
this.setShowRestorePromptToFalse();
|
||||||
|
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Restored Data',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
turnThreeBoxSyncingOn() {
|
turnThreeBoxSyncingOn() {
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Sync Turned On',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
|
|
||||||
this._registerUpdates();
|
this._registerUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
turnThreeBoxSyncingOff() {
|
turnThreeBoxSyncingOff() {
|
||||||
|
this._trackMetaMetricsEvent({
|
||||||
|
event: '3Box Sync Turned Off',
|
||||||
|
category: '3Box',
|
||||||
|
});
|
||||||
|
|
||||||
this.box.logout();
|
this.box.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
bnToHex,
|
bnToHex,
|
||||||
BnMultiplyByFraction,
|
BnMultiplyByFraction,
|
||||||
addHexPrefix,
|
addHexPrefix,
|
||||||
|
getChainType,
|
||||||
} from '../../lib/util';
|
} from '../../lib/util';
|
||||||
import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/helpers/constants/error-keys';
|
import { TRANSACTION_NO_CONTRACT_ERROR_KEY } from '../../../../ui/helpers/constants/error-keys';
|
||||||
import { getSwapsTokensReceivedFromTxMeta } from '../../../../ui/pages/swaps/swaps.util';
|
import { getSwapsTokensReceivedFromTxMeta } from '../../../../ui/pages/swaps/swaps.util';
|
||||||
@ -24,6 +25,7 @@ import {
|
|||||||
} from '../../../../shared/constants/transaction';
|
} from '../../../../shared/constants/transaction';
|
||||||
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller';
|
||||||
import { GAS_LIMITS } from '../../../../shared/constants/gas';
|
import { GAS_LIMITS } from '../../../../shared/constants/gas';
|
||||||
|
import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../../shared/constants/network';
|
||||||
import { isEIP1559Transaction } from '../../../../shared/modules/transaction.utils';
|
import { isEIP1559Transaction } from '../../../../shared/modules/transaction.utils';
|
||||||
import TransactionStateManager from './tx-state-manager';
|
import TransactionStateManager from './tx-state-manager';
|
||||||
import TxGasUtil from './tx-gas-utils';
|
import TxGasUtil from './tx-gas-utils';
|
||||||
@ -356,11 +358,16 @@ export default class TransactionController extends EventEmitter {
|
|||||||
* @returns {Promise<Object>} Object containing the default gas limit, or the simulation failure object
|
* @returns {Promise<Object>} Object containing the default gas limit, or the simulation failure object
|
||||||
*/
|
*/
|
||||||
async _getDefaultGasLimit(txMeta, getCodeResponse) {
|
async _getDefaultGasLimit(txMeta, getCodeResponse) {
|
||||||
|
const chainId = this._getCurrentChainId();
|
||||||
|
const customNetworkGasBuffer = CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId];
|
||||||
|
const chainType = getChainType(chainId);
|
||||||
|
|
||||||
if (txMeta.txParams.gas) {
|
if (txMeta.txParams.gas) {
|
||||||
return {};
|
return {};
|
||||||
} else if (
|
} else if (
|
||||||
txMeta.txParams.to &&
|
txMeta.txParams.to &&
|
||||||
txMeta.type === TRANSACTION_TYPES.SENT_ETHER
|
txMeta.type === TRANSACTION_TYPES.SENT_ETHER &&
|
||||||
|
chainType !== 'custom'
|
||||||
) {
|
) {
|
||||||
// if there's data in the params, but there's no contract code, it's not a valid transaction
|
// if there's data in the params, but there's no contract code, it's not a valid transaction
|
||||||
if (txMeta.txParams.data) {
|
if (txMeta.txParams.data) {
|
||||||
@ -389,6 +396,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
const gasLimit = this.txGasUtil.addGasBuffer(
|
const gasLimit = this.txGasUtil.addGasBuffer(
|
||||||
addHexPrefix(estimatedGasHex),
|
addHexPrefix(estimatedGasHex),
|
||||||
blockGasLimit,
|
blockGasLimit,
|
||||||
|
customNetworkGasBuffer,
|
||||||
);
|
);
|
||||||
return { gasLimit, simulationFails };
|
return { gasLimit, simulationFails };
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,10 @@ import extension from 'extensionizer';
|
|||||||
import { stripHexPrefix } from 'ethereumjs-util';
|
import { stripHexPrefix } from 'ethereumjs-util';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { memoize } from 'lodash';
|
import { memoize } from 'lodash';
|
||||||
|
import {
|
||||||
|
MAINNET_CHAIN_ID,
|
||||||
|
TEST_CHAINS,
|
||||||
|
} from '../../../shared/constants/network';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ENVIRONMENT_TYPE_POPUP,
|
ENVIRONMENT_TYPE_POPUP,
|
||||||
@ -180,6 +184,15 @@ function bnToHex(inputBn) {
|
|||||||
return addHexPrefix(inputBn.toString(16));
|
return addHexPrefix(inputBn.toString(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getChainType(chainId) {
|
||||||
|
if (chainId === MAINNET_CHAIN_ID) {
|
||||||
|
return 'mainnet';
|
||||||
|
} else if (TEST_CHAINS.includes(chainId)) {
|
||||||
|
return 'testnet';
|
||||||
|
}
|
||||||
|
return 'custom';
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getPlatform,
|
getPlatform,
|
||||||
getEnvironmentType,
|
getEnvironmentType,
|
||||||
@ -189,4 +202,5 @@ export {
|
|||||||
checkForError,
|
checkForError,
|
||||||
addHexPrefix,
|
addHexPrefix,
|
||||||
bnToHex,
|
bnToHex,
|
||||||
|
getChainType,
|
||||||
};
|
};
|
||||||
|
@ -132,11 +132,17 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.networkController = new NetworkController(initState.NetworkController);
|
this.networkController = new NetworkController(initState.NetworkController);
|
||||||
this.networkController.setInfuraProjectId(opts.infuraProjectId);
|
this.networkController.setInfuraProjectId(opts.infuraProjectId);
|
||||||
|
|
||||||
|
// now we can initialize the RPC provider, which other controllers require
|
||||||
|
this.initializeProvider();
|
||||||
|
this.provider = this.networkController.getProviderAndBlockTracker().provider;
|
||||||
|
this.blockTracker = this.networkController.getProviderAndBlockTracker().blockTracker;
|
||||||
|
|
||||||
this.preferencesController = new PreferencesController({
|
this.preferencesController = new PreferencesController({
|
||||||
initState: initState.PreferencesController,
|
initState: initState.PreferencesController,
|
||||||
initLangCode: opts.initLangCode,
|
initLangCode: opts.initLangCode,
|
||||||
openPopup: opts.openPopup,
|
openPopup: opts.openPopup,
|
||||||
network: this.networkController,
|
network: this.networkController,
|
||||||
|
provider: this.provider,
|
||||||
migrateAddressBookState: this.migrateAddressBookState.bind(this),
|
migrateAddressBookState: this.migrateAddressBookState.bind(this),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -183,11 +189,6 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
initState.NotificationController,
|
initState.NotificationController,
|
||||||
);
|
);
|
||||||
|
|
||||||
// now we can initialize the RPC provider, which other controllers require
|
|
||||||
this.initializeProvider();
|
|
||||||
this.provider = this.networkController.getProviderAndBlockTracker().provider;
|
|
||||||
this.blockTracker = this.networkController.getProviderAndBlockTracker().blockTracker;
|
|
||||||
|
|
||||||
// token exchange rate tracker
|
// token exchange rate tracker
|
||||||
this.tokenRatesController = new TokenRatesController({
|
this.tokenRatesController = new TokenRatesController({
|
||||||
preferences: this.preferencesController.store,
|
preferences: this.preferencesController.store,
|
||||||
@ -314,6 +315,9 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
this.keyringController.memStore,
|
this.keyringController.memStore,
|
||||||
),
|
),
|
||||||
version,
|
version,
|
||||||
|
trackMetaMetricsEvent: this.metaMetricsController.trackEvent.bind(
|
||||||
|
this.metaMetricsController,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.txController = new TransactionController({
|
this.txController = new TransactionController({
|
||||||
@ -727,6 +731,10 @@ export default class MetamaskController extends EventEmitter {
|
|||||||
preferencesController,
|
preferencesController,
|
||||||
),
|
),
|
||||||
addToken: nodeify(preferencesController.addToken, preferencesController),
|
addToken: nodeify(preferencesController.addToken, preferencesController),
|
||||||
|
updateTokenType: nodeify(
|
||||||
|
preferencesController.updateTokenType,
|
||||||
|
preferencesController,
|
||||||
|
),
|
||||||
removeToken: nodeify(
|
removeToken: nodeify(
|
||||||
preferencesController.removeToken,
|
preferencesController.removeToken,
|
||||||
preferencesController,
|
preferencesController,
|
||||||
|
@ -6,9 +6,9 @@ module.exports = {
|
|||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
branches: 32.75,
|
branches: 32.75,
|
||||||
functions: 42.9,
|
functions: 40,
|
||||||
lines: 43.12,
|
lines: 42.29,
|
||||||
statements: 43.67,
|
statements: 42.83,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setupFiles: ['./test/setup.js', './test/env.js'],
|
setupFiles: ['./test/setup.js', './test/env.js'],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "metamask-crx",
|
"name": "metamask-crx",
|
||||||
"version": "9.7.1",
|
"version": "9.8.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -97,7 +97,7 @@
|
|||||||
"@lavamoat/preinstall-always-fail": "^1.0.0",
|
"@lavamoat/preinstall-always-fail": "^1.0.0",
|
||||||
"@material-ui/core": "^4.11.0",
|
"@material-ui/core": "^4.11.0",
|
||||||
"@metamask/contract-metadata": "^1.26.0",
|
"@metamask/contract-metadata": "^1.26.0",
|
||||||
"@metamask/controllers": "^9.0.0",
|
"@metamask/controllers": "^10.0.0",
|
||||||
"@metamask/eth-ledger-bridge-keyring": "^0.5.0",
|
"@metamask/eth-ledger-bridge-keyring": "^0.5.0",
|
||||||
"@metamask/eth-token-tracker": "^3.0.1",
|
"@metamask/eth-token-tracker": "^3.0.1",
|
||||||
"@metamask/etherscan-link": "^2.1.0",
|
"@metamask/etherscan-link": "^2.1.0",
|
||||||
@ -151,6 +151,7 @@
|
|||||||
"fast-safe-stringify": "^2.0.7",
|
"fast-safe-stringify": "^2.0.7",
|
||||||
"fuse.js": "^3.2.0",
|
"fuse.js": "^3.2.0",
|
||||||
"globalthis": "^1.0.1",
|
"globalthis": "^1.0.1",
|
||||||
|
"human-standard-collectible-abi": "^1.0.2",
|
||||||
"human-standard-token-abi": "^2.0.0",
|
"human-standard-token-abi": "^2.0.0",
|
||||||
"immer": "^8.0.1",
|
"immer": "^8.0.1",
|
||||||
"json-rpc-engine": "^6.1.0",
|
"json-rpc-engine": "^6.1.0",
|
||||||
|
@ -19,6 +19,9 @@ export const GOERLI_CHAIN_ID = '0x5';
|
|||||||
export const KOVAN_CHAIN_ID = '0x2a';
|
export const KOVAN_CHAIN_ID = '0x2a';
|
||||||
export const LOCALHOST_CHAIN_ID = '0x539';
|
export const LOCALHOST_CHAIN_ID = '0x539';
|
||||||
export const BSC_CHAIN_ID = '0x38';
|
export const BSC_CHAIN_ID = '0x38';
|
||||||
|
export const OPTIMISM_CHAIN_ID = '0xa';
|
||||||
|
export const OPTIMISM_TESTNET_CHAIN_ID = '0x45';
|
||||||
|
export const POLYGON_CHAIN_ID = '0x89';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The largest possible chain ID we can handle.
|
* The largest possible chain ID we can handle.
|
||||||
@ -120,3 +123,8 @@ export const NATIVE_CURRENCY_TOKEN_IMAGE_MAP = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const INFURA_BLOCKED_KEY = 'countryBlocked';
|
export const INFURA_BLOCKED_KEY = 'countryBlocked';
|
||||||
|
|
||||||
|
export const CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP = {
|
||||||
|
[OPTIMISM_CHAIN_ID]: 1,
|
||||||
|
[OPTIMISM_TESTNET_CHAIN_ID]: 1,
|
||||||
|
};
|
||||||
|
@ -93,3 +93,7 @@ export const SWAPS_CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP = {
|
|||||||
[BSC_CHAIN_ID]: BSC_DEFAULT_BLOCK_EXPLORER_URL,
|
[BSC_CHAIN_ID]: BSC_DEFAULT_BLOCK_EXPLORER_URL,
|
||||||
[MAINNET_CHAIN_ID]: MAINNET_DEFAULT_BLOCK_EXPLORER_URL,
|
[MAINNET_CHAIN_ID]: MAINNET_DEFAULT_BLOCK_EXPLORER_URL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ETHEREUM = 'ethereum';
|
||||||
|
export const POLYGON = 'polygon';
|
||||||
|
export const BSC = 'bsc';
|
||||||
|
@ -8,9 +8,21 @@
|
|||||||
"mockMetaMetricsResponse": true
|
"mockMetaMetricsResponse": true
|
||||||
},
|
},
|
||||||
"swaps": {
|
"swaps": {
|
||||||
"featureFlag": {
|
"featureFlags": {
|
||||||
"status": {
|
"bsc": {
|
||||||
"active": true
|
"mobile_active": false,
|
||||||
|
"extension_active": true,
|
||||||
|
"fallback_to_v1": true
|
||||||
|
},
|
||||||
|
"ethereum": {
|
||||||
|
"mobile_active": false,
|
||||||
|
"extension_active": true,
|
||||||
|
"fallback_to_v1": true
|
||||||
|
},
|
||||||
|
"polygon": {
|
||||||
|
"mobile_active": false,
|
||||||
|
"extension_active": true,
|
||||||
|
"fallback_to_v1": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
219
test/e2e/tests/send-eth.spec.js
Normal file
219
test/e2e/tests/send-eth.spec.js
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
const { strict: assert } = require('assert');
|
||||||
|
const { withFixtures, regularDelayMs } = require('../helpers');
|
||||||
|
|
||||||
|
describe('Send ETH from inside MetaMask using default gas', function () {
|
||||||
|
const ganacheOptions = {
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
secretKey:
|
||||||
|
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
|
||||||
|
balance: 25000000000000000000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
it('finds the transaction in the transactions list', async function () {
|
||||||
|
await withFixtures(
|
||||||
|
{
|
||||||
|
fixtures: 'imported-account',
|
||||||
|
ganacheOptions,
|
||||||
|
title: this.test.title,
|
||||||
|
},
|
||||||
|
async ({ driver }) => {
|
||||||
|
await driver.navigate();
|
||||||
|
await driver.fill('#password', 'correct horse battery staple');
|
||||||
|
await driver.press('#password', driver.Key.ENTER);
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="eth-overview-send"]');
|
||||||
|
|
||||||
|
await driver.fill(
|
||||||
|
'input[placeholder="Search, public address (0x), or ENS"]',
|
||||||
|
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
|
||||||
|
);
|
||||||
|
|
||||||
|
const inputAmount = await driver.findElement('.unit-input__input');
|
||||||
|
await inputAmount.fill('1000');
|
||||||
|
|
||||||
|
const errorAmount = await driver.findElement('.send-v2__error-amount');
|
||||||
|
assert.equal(
|
||||||
|
await errorAmount.getText(),
|
||||||
|
'Insufficient funds.',
|
||||||
|
'send screen should render an insufficient fund error message',
|
||||||
|
);
|
||||||
|
|
||||||
|
await inputAmount.press(driver.Key.BACK_SPACE);
|
||||||
|
await inputAmount.press(driver.Key.BACK_SPACE);
|
||||||
|
await inputAmount.press(driver.Key.BACK_SPACE);
|
||||||
|
await driver.delay(regularDelayMs);
|
||||||
|
|
||||||
|
await driver.assertElementNotPresent('.send-v2__error-amount');
|
||||||
|
|
||||||
|
const amountMax = await driver.findClickableElement(
|
||||||
|
'.send-v2__amount-max',
|
||||||
|
);
|
||||||
|
await amountMax.click();
|
||||||
|
|
||||||
|
let inputValue = await inputAmount.getAttribute('value');
|
||||||
|
|
||||||
|
assert(Number(inputValue) > 24);
|
||||||
|
|
||||||
|
await amountMax.click();
|
||||||
|
|
||||||
|
assert.equal(await inputAmount.isEnabled(), true);
|
||||||
|
|
||||||
|
await inputAmount.fill('1');
|
||||||
|
|
||||||
|
inputValue = await inputAmount.getAttribute('value');
|
||||||
|
assert.equal(inputValue, '1');
|
||||||
|
|
||||||
|
// Continue to next screen
|
||||||
|
await driver.clickElement({ text: 'Next', tag: 'button' });
|
||||||
|
|
||||||
|
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="home__activity-tab"]');
|
||||||
|
await driver.wait(async () => {
|
||||||
|
const confirmedTxes = await driver.findElements(
|
||||||
|
'.transaction-list__completed-transactions .transaction-list-item',
|
||||||
|
);
|
||||||
|
return confirmedTxes.length === 1;
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
await driver.waitForSelector({
|
||||||
|
css: '.transaction-list-item__primary-currency',
|
||||||
|
text: '-1 ETH',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Send ETH from inside MetaMask using fast gas option', function () {
|
||||||
|
const ganacheOptions = {
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
secretKey:
|
||||||
|
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
|
||||||
|
balance: 25000000000000000000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
it('finds the transaction in the transactions list', async function () {
|
||||||
|
await withFixtures(
|
||||||
|
{
|
||||||
|
fixtures: 'imported-account',
|
||||||
|
ganacheOptions,
|
||||||
|
title: this.test.title,
|
||||||
|
},
|
||||||
|
async ({ driver }) => {
|
||||||
|
await driver.navigate();
|
||||||
|
await driver.fill('#password', 'correct horse battery staple');
|
||||||
|
await driver.press('#password', driver.Key.ENTER);
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="eth-overview-send"]');
|
||||||
|
|
||||||
|
await driver.fill(
|
||||||
|
'input[placeholder="Search, public address (0x), or ENS"]',
|
||||||
|
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
|
||||||
|
);
|
||||||
|
|
||||||
|
const inputAmount = await driver.findElement('.unit-input__input');
|
||||||
|
await inputAmount.fill('1');
|
||||||
|
|
||||||
|
const inputValue = await inputAmount.getAttribute('value');
|
||||||
|
assert.equal(inputValue, '1');
|
||||||
|
|
||||||
|
// Set the gas price
|
||||||
|
await driver.clickElement({ text: 'Fast', tag: 'button/div/div' });
|
||||||
|
|
||||||
|
// Continue to next screen
|
||||||
|
await driver.clickElement({ text: 'Next', tag: 'button' });
|
||||||
|
|
||||||
|
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
||||||
|
|
||||||
|
await driver.waitForSelector(
|
||||||
|
'.transaction-list__completed-transactions .transaction-list-item',
|
||||||
|
);
|
||||||
|
await driver.waitForSelector({
|
||||||
|
css: '.transaction-list-item__primary-currency',
|
||||||
|
text: '-1 ETH',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Send ETH from inside MetaMask using advanced gas modal', function () {
|
||||||
|
const ganacheOptions = {
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
secretKey:
|
||||||
|
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
|
||||||
|
balance: 25000000000000000000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
it('finds the transaction in the transactions list', async function () {
|
||||||
|
await withFixtures(
|
||||||
|
{
|
||||||
|
fixtures: 'imported-account',
|
||||||
|
ganacheOptions,
|
||||||
|
title: this.test.title,
|
||||||
|
},
|
||||||
|
async ({ driver }) => {
|
||||||
|
await driver.navigate();
|
||||||
|
await driver.fill('#password', 'correct horse battery staple');
|
||||||
|
await driver.press('#password', driver.Key.ENTER);
|
||||||
|
|
||||||
|
await driver.clickElement('[data-testid="eth-overview-send"]');
|
||||||
|
|
||||||
|
await driver.fill(
|
||||||
|
'input[placeholder="Search, public address (0x), or ENS"]',
|
||||||
|
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
|
||||||
|
);
|
||||||
|
|
||||||
|
const inputAmount = await driver.findElement('.unit-input__input');
|
||||||
|
await inputAmount.fill('1');
|
||||||
|
|
||||||
|
const inputValue = await inputAmount.getAttribute('value');
|
||||||
|
assert.equal(inputValue, '1');
|
||||||
|
|
||||||
|
// Set the gas limit
|
||||||
|
await driver.clickElement('.advanced-gas-options-btn');
|
||||||
|
|
||||||
|
// wait for gas modal to be visible
|
||||||
|
const gasModal = await driver.findVisibleElement('span .modal');
|
||||||
|
|
||||||
|
await driver.clickElement({ text: 'Save', tag: 'button' });
|
||||||
|
|
||||||
|
// Wait for gas modal to be removed from DOM
|
||||||
|
await gasModal.waitForElementState('hidden');
|
||||||
|
|
||||||
|
// Continue to next screen
|
||||||
|
await driver.clickElement({ text: 'Next', tag: 'button' });
|
||||||
|
|
||||||
|
const transactionAmounts = await driver.findElements(
|
||||||
|
'.currency-display-component__text',
|
||||||
|
);
|
||||||
|
const transactionAmount = transactionAmounts[0];
|
||||||
|
assert.equal(await transactionAmount.getText(), '1');
|
||||||
|
|
||||||
|
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
||||||
|
|
||||||
|
await driver.wait(async () => {
|
||||||
|
const confirmedTxes = await driver.findElements(
|
||||||
|
'.transaction-list__completed-transactions .transaction-list-item',
|
||||||
|
);
|
||||||
|
return confirmedTxes.length === 1;
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
await driver.waitForSelector(
|
||||||
|
{
|
||||||
|
css: '.transaction-list-item__primary-currency',
|
||||||
|
text: '-1 ETH',
|
||||||
|
},
|
||||||
|
{ timeout: 10000 },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@ -48,9 +48,9 @@ async function setupFetchMocking(driver) {
|
|||||||
return { json: async () => clone(mockResponses.gasPricesBasic) };
|
return { json: async () => clone(mockResponses.gasPricesBasic) };
|
||||||
} else if (url.match(/chromeextensionmm/u)) {
|
} else if (url.match(/chromeextensionmm/u)) {
|
||||||
return { json: async () => clone(mockResponses.metametrics) };
|
return { json: async () => clone(mockResponses.metametrics) };
|
||||||
} else if (url.match(/^https:\/\/(api\.metaswap|.*airswap-dev)/u)) {
|
} else if (url.match(/^https:\/\/(api2\.metaswap\.codefi\.network)/u)) {
|
||||||
if (url.match(/featureFlag$/u)) {
|
if (url.match(/featureFlags$/u)) {
|
||||||
return { json: async () => clone(mockResponses.swaps.featureFlag) };
|
return { json: async () => clone(mockResponses.swaps.featureFlags) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return window.origFetch(...args);
|
return window.origFetch(...args);
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export const METASWAP_BASE_URL = 'https://api.metaswap.codefi.network';
|
export const METASWAP_BASE_URL = 'https://api.metaswap.codefi.network';
|
||||||
|
export const METASWAP_API_V2_BASE_URL = 'https://api2.metaswap.codefi.network';
|
||||||
|
@ -106,6 +106,7 @@ export const createSwapsMockStore = () => {
|
|||||||
topAggId: null,
|
topAggId: null,
|
||||||
routeState: '',
|
routeState: '',
|
||||||
swapsFeatureIsLive: false,
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -59,3 +59,23 @@ export const TOKENS_GET_RESPONSE = [
|
|||||||
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
|
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const createFeatureFlagsResponse = () => {
|
||||||
|
return {
|
||||||
|
bsc: {
|
||||||
|
mobile_active: false,
|
||||||
|
extension_active: true,
|
||||||
|
fallback_to_v1: true,
|
||||||
|
},
|
||||||
|
ethereum: {
|
||||||
|
mobile_active: false,
|
||||||
|
extension_active: true,
|
||||||
|
fallback_to_v1: true,
|
||||||
|
},
|
||||||
|
polygon: {
|
||||||
|
mobile_active: false,
|
||||||
|
extension_active: true,
|
||||||
|
fallback_to_v1: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -10,7 +10,7 @@ import InfoIcon from '../../ui/icon/info-icon.component';
|
|||||||
import Button from '../../ui/button';
|
import Button from '../../ui/button';
|
||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
import { useMetricEvent } from '../../../hooks/useMetricEvent';
|
import { useMetricEvent } from '../../../hooks/useMetricEvent';
|
||||||
import { updateSendToken } from '../../../ducks/send/send.duck';
|
import { ASSET_TYPES, updateSendAsset } from '../../../ducks/send';
|
||||||
import { SEND_ROUTE } from '../../../helpers/constants/routes';
|
import { SEND_ROUTE } from '../../../helpers/constants/routes';
|
||||||
import { SEVERITIES } from '../../../helpers/constants/design-system';
|
import { SEVERITIES } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ const AssetListItem = ({
|
|||||||
primary,
|
primary,
|
||||||
secondary,
|
secondary,
|
||||||
identiconBorder,
|
identiconBorder,
|
||||||
|
isERC721,
|
||||||
}) => {
|
}) => {
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -68,13 +69,17 @@ const AssetListItem = ({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
sendTokenEvent();
|
sendTokenEvent();
|
||||||
dispatch(
|
dispatch(
|
||||||
updateSendToken({
|
updateSendAsset({
|
||||||
|
type: ASSET_TYPES.TOKEN,
|
||||||
|
details: {
|
||||||
address: tokenAddress,
|
address: tokenAddress,
|
||||||
decimals: tokenDecimals,
|
decimals: tokenDecimals,
|
||||||
symbol: tokenSymbol,
|
symbol: tokenSymbol,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
).then(() => {
|
||||||
history.push(SEND_ROUTE);
|
history.push(SEND_ROUTE);
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('sendSpecifiedTokens', [tokenSymbol])}
|
{t('sendSpecifiedTokens', [tokenSymbol])}
|
||||||
@ -107,7 +112,7 @@ const AssetListItem = ({
|
|||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
titleIcon={titleIcon}
|
titleIcon={titleIcon}
|
||||||
subtitle={<h3 title={secondary}>{secondary}</h3>}
|
subtitle={secondary ? <h3 title={secondary}>{secondary}</h3> : null}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
icon={
|
icon={
|
||||||
<Identicon
|
<Identicon
|
||||||
@ -121,10 +126,12 @@ const AssetListItem = ({
|
|||||||
}
|
}
|
||||||
midContent={midContent}
|
midContent={midContent}
|
||||||
rightContent={
|
rightContent={
|
||||||
|
!isERC721 && (
|
||||||
<>
|
<>
|
||||||
<i className="fas fa-chevron-right asset-list-item__chevron-right" />
|
<i className="fas fa-chevron-right asset-list-item__chevron-right" />
|
||||||
{sendTokenButton}
|
{sendTokenButton}
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -143,6 +150,7 @@ AssetListItem.propTypes = {
|
|||||||
'primary': PropTypes.string,
|
'primary': PropTypes.string,
|
||||||
'secondary': PropTypes.string,
|
'secondary': PropTypes.string,
|
||||||
'identiconBorder': PropTypes.bool,
|
'identiconBorder': PropTypes.bool,
|
||||||
|
'isERC721': PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
AssetListItem.defaultProps = {
|
AssetListItem.defaultProps = {
|
||||||
|
@ -56,13 +56,13 @@ const AssetList = ({ onClickAsset }) => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const [secondaryCurrencyDisplay] = useCurrencyDisplay(
|
const [
|
||||||
selectedAccountBalance,
|
secondaryCurrencyDisplay,
|
||||||
{
|
secondaryCurrencyProperties,
|
||||||
|
] = useCurrencyDisplay(selectedAccountBalance, {
|
||||||
numberOfDecimals: secondaryNumberOfDecimals,
|
numberOfDecimals: secondaryNumberOfDecimals,
|
||||||
currency: secondaryCurrency,
|
currency: secondaryCurrency,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const primaryTokenImage = useSelector(getNativeCurrencyImage);
|
const primaryTokenImage = useSelector(getNativeCurrencyImage);
|
||||||
|
|
||||||
@ -71,7 +71,9 @@ const AssetList = ({ onClickAsset }) => {
|
|||||||
<AssetListItem
|
<AssetListItem
|
||||||
onClick={() => onClickAsset(nativeCurrency)}
|
onClick={() => onClickAsset(nativeCurrency)}
|
||||||
data-testid="wallet-balance"
|
data-testid="wallet-balance"
|
||||||
primary={primaryCurrencyProperties.value}
|
primary={
|
||||||
|
primaryCurrencyProperties.value ?? secondaryCurrencyProperties.value
|
||||||
|
}
|
||||||
tokenSymbol={primaryCurrencyProperties.suffix}
|
tokenSymbol={primaryCurrencyProperties.suffix}
|
||||||
secondary={showFiat ? secondaryCurrencyDisplay : undefined}
|
secondary={showFiat ? secondaryCurrencyDisplay : undefined}
|
||||||
tokenImage={primaryTokenImage}
|
tokenImage={primaryTokenImage}
|
||||||
|
@ -9,10 +9,10 @@ import {
|
|||||||
} from '../../../../ducks/gas/gas.duck';
|
} from '../../../../ducks/gas/gas.duck';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
hideGasButtonGroup,
|
useCustomGas,
|
||||||
setGasLimit,
|
updateGasLimit,
|
||||||
setGasPrice,
|
updateGasPrice,
|
||||||
} from '../../../../ducks/send/send.duck';
|
} from '../../../../ducks/send';
|
||||||
|
|
||||||
let mapDispatchToProps;
|
let mapDispatchToProps;
|
||||||
let mergeProps;
|
let mergeProps;
|
||||||
@ -32,8 +32,6 @@ jest.mock('../../../../selectors', () => ({
|
|||||||
`mockRenderableBasicEstimateData:${Object.keys(s).length}`,
|
`mockRenderableBasicEstimateData:${Object.keys(s).length}`,
|
||||||
getDefaultActiveButtonIndex: (a, b) => a + b,
|
getDefaultActiveButtonIndex: (a, b) => a + b,
|
||||||
getCurrentEthBalance: (state) => state.metamask.balance || '0x0',
|
getCurrentEthBalance: (state) => state.metamask.balance || '0x0',
|
||||||
getSendToken: () => null,
|
|
||||||
getTokenBalance: (state) => state.send.tokenBalance || '0x0',
|
|
||||||
getCustomGasPrice: (state) => state.gas.customData.price || '0x0',
|
getCustomGasPrice: (state) => state.gas.customData.price || '0x0',
|
||||||
getCustomGasLimit: (state) => state.gas.customData.limit || '0x0',
|
getCustomGasLimit: (state) => state.gas.customData.limit || '0x0',
|
||||||
getCurrentCurrency: jest.fn().mockReturnValue('usd'),
|
getCurrentCurrency: jest.fn().mockReturnValue('usd'),
|
||||||
@ -57,11 +55,15 @@ jest.mock('../../../../ducks/gas/gas.duck', () => ({
|
|||||||
resetCustomData: jest.fn(),
|
resetCustomData: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../../../../ducks/send/send.duck', () => ({
|
jest.mock('../../../../ducks/send', () => {
|
||||||
hideGasButtonGroup: jest.fn(),
|
const { ASSET_TYPES } = jest.requireActual('../../../../ducks/send');
|
||||||
setGasLimit: jest.fn(),
|
return {
|
||||||
setGasPrice: jest.fn(),
|
useCustomGas: jest.fn(),
|
||||||
}));
|
updateGasLimit: jest.fn(),
|
||||||
|
updateGasPrice: jest.fn(),
|
||||||
|
getSendAsset: jest.fn(() => ({ type: ASSET_TYPES.NATIVE })),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
require('./gas-modal-page-container.container');
|
require('./gas-modal-page-container.container');
|
||||||
|
|
||||||
@ -79,11 +81,11 @@ describe('gas-modal-page-container container', () => {
|
|||||||
dispatchSpy.resetHistory();
|
dispatchSpy.resetHistory();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hideGasButtonGroup()', () => {
|
describe('useCustomGas()', () => {
|
||||||
it('should dispatch a hideGasButtonGroup action', () => {
|
it('should dispatch a useCustomGas action', () => {
|
||||||
mapDispatchToPropsObject.hideGasButtonGroup();
|
mapDispatchToPropsObject.useCustomGas();
|
||||||
expect(dispatchSpy.calledOnce).toStrictEqual(true);
|
expect(dispatchSpy.calledOnce).toStrictEqual(true);
|
||||||
expect(hideGasButtonGroup).toHaveBeenCalled();
|
expect(useCustomGas).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -126,13 +128,13 @@ describe('gas-modal-page-container container', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('setGasData()', () => {
|
describe('setGasData()', () => {
|
||||||
it('should dispatch a setGasPrice and setGasLimit action with the correct props', () => {
|
it('should dispatch a updateGasPrice and updateGasLimit action with the correct props', () => {
|
||||||
mapDispatchToPropsObject.setGasData('ffff', 'aaaa');
|
mapDispatchToPropsObject.setGasData('ffff', 'aaaa');
|
||||||
expect(dispatchSpy.calledTwice).toStrictEqual(true);
|
expect(dispatchSpy.calledTwice).toStrictEqual(true);
|
||||||
expect(setGasPrice).toHaveBeenCalled();
|
expect(updateGasPrice).toHaveBeenCalled();
|
||||||
expect(setGasLimit).toHaveBeenCalled();
|
expect(updateGasLimit).toHaveBeenCalled();
|
||||||
expect(setGasLimit).toHaveBeenCalledWith('ffff');
|
expect(updateGasLimit).toHaveBeenCalledWith('ffff');
|
||||||
expect(setGasPrice).toHaveBeenCalledWith('aaaa');
|
expect(updateGasPrice).toHaveBeenCalledWith('aaaa');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -165,7 +167,7 @@ describe('gas-modal-page-container container', () => {
|
|||||||
};
|
};
|
||||||
dispatchProps = {
|
dispatchProps = {
|
||||||
updateCustomGasPrice: sinon.spy(),
|
updateCustomGasPrice: sinon.spy(),
|
||||||
hideGasButtonGroup: sinon.spy(),
|
useCustomGas: sinon.spy(),
|
||||||
setGasData: sinon.spy(),
|
setGasData: sinon.spy(),
|
||||||
updateConfirmTxGasAndCalculate: sinon.spy(),
|
updateConfirmTxGasAndCalculate: sinon.spy(),
|
||||||
someOtherDispatchProp: sinon.spy(),
|
someOtherDispatchProp: sinon.spy(),
|
||||||
@ -194,7 +196,7 @@ describe('gas-modal-page-container container', () => {
|
|||||||
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
||||||
).toStrictEqual(0);
|
).toStrictEqual(0);
|
||||||
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.hideGasButtonGroup.callCount).toStrictEqual(0);
|
expect(dispatchProps.useCustomGas.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.hideModal.callCount).toStrictEqual(0);
|
expect(dispatchProps.hideModal.callCount).toStrictEqual(0);
|
||||||
|
|
||||||
result.onSubmit();
|
result.onSubmit();
|
||||||
@ -203,7 +205,7 @@ describe('gas-modal-page-container container', () => {
|
|||||||
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
||||||
).toStrictEqual(1);
|
).toStrictEqual(1);
|
||||||
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.hideGasButtonGroup.callCount).toStrictEqual(0);
|
expect(dispatchProps.useCustomGas.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.hideModal.callCount).toStrictEqual(1);
|
expect(dispatchProps.hideModal.callCount).toStrictEqual(1);
|
||||||
|
|
||||||
expect(dispatchProps.updateCustomGasPrice.callCount).toStrictEqual(0);
|
expect(dispatchProps.updateCustomGasPrice.callCount).toStrictEqual(0);
|
||||||
@ -238,7 +240,7 @@ describe('gas-modal-page-container container', () => {
|
|||||||
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
||||||
).toStrictEqual(0);
|
).toStrictEqual(0);
|
||||||
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.hideGasButtonGroup.callCount).toStrictEqual(0);
|
expect(dispatchProps.useCustomGas.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.cancelAndClose.callCount).toStrictEqual(0);
|
expect(dispatchProps.cancelAndClose.callCount).toStrictEqual(0);
|
||||||
|
|
||||||
result.onSubmit('mockNewLimit', 'mockNewPrice');
|
result.onSubmit('mockNewLimit', 'mockNewPrice');
|
||||||
@ -251,7 +253,7 @@ describe('gas-modal-page-container container', () => {
|
|||||||
'mockNewLimit',
|
'mockNewLimit',
|
||||||
'mockNewPrice',
|
'mockNewPrice',
|
||||||
]);
|
]);
|
||||||
expect(dispatchProps.hideGasButtonGroup.callCount).toStrictEqual(1);
|
expect(dispatchProps.useCustomGas.callCount).toStrictEqual(1);
|
||||||
expect(dispatchProps.cancelAndClose.callCount).toStrictEqual(1);
|
expect(dispatchProps.cancelAndClose.callCount).toStrictEqual(1);
|
||||||
|
|
||||||
expect(dispatchProps.updateCustomGasPrice.callCount).toStrictEqual(0);
|
expect(dispatchProps.updateCustomGasPrice.callCount).toStrictEqual(0);
|
||||||
@ -278,7 +280,7 @@ describe('gas-modal-page-container container', () => {
|
|||||||
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
dispatchProps.updateConfirmTxGasAndCalculate.callCount,
|
||||||
).toStrictEqual(0);
|
).toStrictEqual(0);
|
||||||
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
expect(dispatchProps.setGasData.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.hideGasButtonGroup.callCount).toStrictEqual(0);
|
expect(dispatchProps.useCustomGas.callCount).toStrictEqual(0);
|
||||||
expect(dispatchProps.cancelAndClose.callCount).toStrictEqual(1);
|
expect(dispatchProps.cancelAndClose.callCount).toStrictEqual(1);
|
||||||
|
|
||||||
expect(dispatchProps.createSpeedUpTransaction.callCount).toStrictEqual(1);
|
expect(dispatchProps.createSpeedUpTransaction.callCount).toStrictEqual(1);
|
||||||
|
@ -14,20 +14,21 @@ import {
|
|||||||
fetchBasicGasEstimates,
|
fetchBasicGasEstimates,
|
||||||
} from '../../../../ducks/gas/gas.duck';
|
} from '../../../../ducks/gas/gas.duck';
|
||||||
import {
|
import {
|
||||||
hideGasButtonGroup,
|
getSendMaxModeState,
|
||||||
setGasLimit,
|
getGasLimit,
|
||||||
setGasPrice,
|
getGasPrice,
|
||||||
setGasTotal,
|
getSendAmount,
|
||||||
updateSendAmount,
|
updateGasLimit,
|
||||||
updateSendErrors,
|
updateGasPrice,
|
||||||
} from '../../../../ducks/send/send.duck';
|
useCustomGas,
|
||||||
|
getSendAsset,
|
||||||
|
ASSET_TYPES,
|
||||||
|
} from '../../../../ducks/send';
|
||||||
import {
|
import {
|
||||||
conversionRateSelector as getConversionRate,
|
conversionRateSelector as getConversionRate,
|
||||||
getCurrentCurrency,
|
getCurrentCurrency,
|
||||||
getCurrentEthBalance,
|
getCurrentEthBalance,
|
||||||
getIsMainnet,
|
getIsMainnet,
|
||||||
getSendToken,
|
|
||||||
getPreferences,
|
|
||||||
getIsTestnet,
|
getIsTestnet,
|
||||||
getBasicGasEstimateLoadingStatus,
|
getBasicGasEstimateLoadingStatus,
|
||||||
getCustomGasLimit,
|
getCustomGasLimit,
|
||||||
@ -35,13 +36,12 @@ import {
|
|||||||
getDefaultActiveButtonIndex,
|
getDefaultActiveButtonIndex,
|
||||||
getRenderableBasicEstimateData,
|
getRenderableBasicEstimateData,
|
||||||
isCustomPriceSafe,
|
isCustomPriceSafe,
|
||||||
getTokenBalance,
|
|
||||||
getSendMaxModeState,
|
|
||||||
isCustomPriceSafeForCustomNetwork,
|
isCustomPriceSafeForCustomNetwork,
|
||||||
getAveragePriceEstimateInHexWEI,
|
getAveragePriceEstimateInHexWEI,
|
||||||
isCustomPriceExcessive,
|
isCustomPriceExcessive,
|
||||||
getIsGasEstimatesFetched,
|
getIsGasEstimatesFetched,
|
||||||
getIsCustomNetworkGasPriceFetched,
|
getIsCustomNetworkGasPriceFetched,
|
||||||
|
getShouldShowFiat,
|
||||||
} from '../../../../selectors';
|
} from '../../../../selectors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -57,16 +57,15 @@ import {
|
|||||||
isBalanceSufficient,
|
isBalanceSufficient,
|
||||||
} from '../../../../pages/send/send.utils';
|
} from '../../../../pages/send/send.utils';
|
||||||
import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants';
|
import { MIN_GAS_LIMIT_DEC } from '../../../../pages/send/send.constants';
|
||||||
import { calcMaxAmount } from '../../../../pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.utils';
|
|
||||||
import { TRANSACTION_STATUSES } from '../../../../../shared/constants/transaction';
|
import { TRANSACTION_STATUSES } from '../../../../../shared/constants/transaction';
|
||||||
import { GAS_LIMITS } from '../../../../../shared/constants/gas';
|
import { GAS_LIMITS } from '../../../../../shared/constants/gas';
|
||||||
import GasModalPageContainer from './gas-modal-page-container.component';
|
import GasModalPageContainer from './gas-modal-page-container.component';
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
const mapStateToProps = (state, ownProps) => {
|
||||||
const {
|
const gasLimit = getGasLimit(state);
|
||||||
metamask: { currentNetworkTxList },
|
const gasPrice = getGasPrice(state);
|
||||||
send,
|
const amount = getSendAmount(state);
|
||||||
} = state;
|
const { currentNetworkTxList } = state.metamask;
|
||||||
const { modalState: { props: modalProps } = {} } = state.appState.modal || {};
|
const { modalState: { props: modalProps } = {} } = state.appState.modal || {};
|
||||||
const { txData = {} } = modalProps || {};
|
const { txData = {} } = modalProps || {};
|
||||||
const { transaction = {}, onSubmit } = ownProps;
|
const { transaction = {}, onSubmit } = ownProps;
|
||||||
@ -74,15 +73,15 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
({ id }) => id === (transaction.id || txData.id),
|
({ id }) => id === (transaction.id || txData.id),
|
||||||
);
|
);
|
||||||
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state);
|
const buttonDataLoading = getBasicGasEstimateLoadingStatus(state);
|
||||||
const sendToken = getSendToken(state);
|
const asset = getSendAsset(state);
|
||||||
|
|
||||||
// a "default" txParams is used during the send flow, since the transaction doesn't exist yet in that case
|
// a "default" txParams is used during the send flow, since the transaction doesn't exist yet in that case
|
||||||
const txParams = selectedTransaction?.txParams
|
const txParams = selectedTransaction?.txParams
|
||||||
? selectedTransaction.txParams
|
? selectedTransaction.txParams
|
||||||
: {
|
: {
|
||||||
gas: send.gasLimit || GAS_LIMITS.SIMPLE,
|
gas: gasLimit || GAS_LIMITS.SIMPLE,
|
||||||
gasPrice: send.gasPrice || getAveragePriceEstimateInHexWEI(state, true),
|
gasPrice: gasPrice || getAveragePriceEstimateInHexWEI(state, true),
|
||||||
value: sendToken ? '0x0' : send.amount,
|
value: asset.type === ASSET_TYPES.TOKEN ? '0x0' : amount,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { gasPrice: currentGasPrice, gas: currentGasLimit } = txParams;
|
const { gasPrice: currentGasPrice, gas: currentGasLimit } = txParams;
|
||||||
@ -116,20 +115,18 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
|
|
||||||
const balance = getCurrentEthBalance(state);
|
const balance = getCurrentEthBalance(state);
|
||||||
|
|
||||||
const { showFiatInTestnets } = getPreferences(state);
|
|
||||||
const isMainnet = getIsMainnet(state);
|
const isMainnet = getIsMainnet(state);
|
||||||
const showFiat = Boolean(isMainnet || showFiatInTestnets);
|
const showFiat = getShouldShowFiat(state);
|
||||||
|
|
||||||
const isSendTokenSet = Boolean(sendToken);
|
|
||||||
const isTestnet = getIsTestnet(state);
|
const isTestnet = getIsTestnet(state);
|
||||||
|
|
||||||
const newTotalEth =
|
const newTotalEth =
|
||||||
maxModeOn && !isSendTokenSet
|
maxModeOn && asset.type === ASSET_TYPES.NATIVE
|
||||||
? sumHexWEIsToRenderableEth([balance, '0x0'])
|
? sumHexWEIsToRenderableEth([balance, '0x0'])
|
||||||
: sumHexWEIsToRenderableEth([value, customGasTotal]);
|
: sumHexWEIsToRenderableEth([value, customGasTotal]);
|
||||||
|
|
||||||
const sendAmount =
|
const sendAmount =
|
||||||
maxModeOn && !isSendTokenSet
|
maxModeOn && asset.type === ASSET_TYPES.NATIVE
|
||||||
? subtractHexWEIsFromRenderableEth(balance, customGasTotal)
|
? subtractHexWEIsFromRenderableEth(balance, customGasTotal)
|
||||||
: sumHexWEIsToRenderableEth([value, '0x0']);
|
: sumHexWEIsToRenderableEth([value, '0x0']);
|
||||||
|
|
||||||
@ -194,9 +191,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
txId: transaction.id,
|
txId: transaction.id,
|
||||||
insufficientBalance,
|
insufficientBalance,
|
||||||
isMainnet,
|
isMainnet,
|
||||||
sendToken,
|
|
||||||
balance,
|
balance,
|
||||||
tokenBalance: getTokenBalance(state),
|
|
||||||
conversionRate,
|
conversionRate,
|
||||||
value,
|
value,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
@ -213,12 +208,13 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
dispatch(hideModal());
|
dispatch(hideModal());
|
||||||
},
|
},
|
||||||
hideModal: () => dispatch(hideModal()),
|
hideModal: () => dispatch(hideModal()),
|
||||||
|
useCustomGas: () => dispatch(useCustomGas()),
|
||||||
updateCustomGasPrice,
|
updateCustomGasPrice,
|
||||||
updateCustomGasLimit: (newLimit) =>
|
updateCustomGasLimit: (newLimit) =>
|
||||||
dispatch(setCustomGasLimit(addHexPrefix(newLimit))),
|
dispatch(setCustomGasLimit(addHexPrefix(newLimit))),
|
||||||
setGasData: (newLimit, newPrice) => {
|
setGasData: (newLimit, newPrice) => {
|
||||||
dispatch(setGasLimit(newLimit));
|
dispatch(updateGasLimit(newLimit));
|
||||||
dispatch(setGasPrice(newPrice));
|
dispatch(updateGasPrice(newPrice));
|
||||||
},
|
},
|
||||||
updateConfirmTxGasAndCalculate: (gasLimit, gasPrice, updatedTx) => {
|
updateConfirmTxGasAndCalculate: (gasLimit, gasPrice, updatedTx) => {
|
||||||
updateCustomGasPrice(gasPrice);
|
updateCustomGasPrice(gasPrice);
|
||||||
@ -231,14 +227,8 @@ const mapDispatchToProps = (dispatch) => {
|
|||||||
createSpeedUpTransaction: (txId, gasPrice, gasLimit) => {
|
createSpeedUpTransaction: (txId, gasPrice, gasLimit) => {
|
||||||
return dispatch(createSpeedUpTransaction(txId, gasPrice, gasLimit));
|
return dispatch(createSpeedUpTransaction(txId, gasPrice, gasLimit));
|
||||||
},
|
},
|
||||||
hideGasButtonGroup: () => dispatch(hideGasButtonGroup()),
|
|
||||||
hideSidebar: () => dispatch(hideSidebar()),
|
hideSidebar: () => dispatch(hideSidebar()),
|
||||||
fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()),
|
fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()),
|
||||||
setGasTotal: (total) => dispatch(setGasTotal(total)),
|
|
||||||
setAmountToMax: (maxAmountDataObject) => {
|
|
||||||
dispatch(updateSendErrors({ amount: null }));
|
|
||||||
dispatch(updateSendAmount(calcMaxAmount(maxAmountDataObject)));
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -251,17 +241,12 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
|||||||
isSpeedUp,
|
isSpeedUp,
|
||||||
isRetry,
|
isRetry,
|
||||||
insufficientBalance,
|
insufficientBalance,
|
||||||
maxModeOn,
|
|
||||||
customGasPrice,
|
customGasPrice,
|
||||||
customGasTotal,
|
|
||||||
balance,
|
|
||||||
sendToken,
|
|
||||||
tokenBalance,
|
|
||||||
customGasLimit,
|
customGasLimit,
|
||||||
transaction,
|
transaction,
|
||||||
} = stateProps;
|
} = stateProps;
|
||||||
const {
|
const {
|
||||||
hideGasButtonGroup: dispatchHideGasButtonGroup,
|
useCustomGas: dispatchUseCustomGas,
|
||||||
setGasData: dispatchSetGasData,
|
setGasData: dispatchSetGasData,
|
||||||
updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate,
|
updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate,
|
||||||
createSpeedUpTransaction: dispatchCreateSpeedUpTransaction,
|
createSpeedUpTransaction: dispatchCreateSpeedUpTransaction,
|
||||||
@ -269,7 +254,6 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
|||||||
hideSidebar: dispatchHideSidebar,
|
hideSidebar: dispatchHideSidebar,
|
||||||
cancelAndClose: dispatchCancelAndClose,
|
cancelAndClose: dispatchCancelAndClose,
|
||||||
hideModal: dispatchHideModal,
|
hideModal: dispatchHideModal,
|
||||||
setAmountToMax: dispatchSetAmountToMax,
|
|
||||||
...otherDispatchProps
|
...otherDispatchProps
|
||||||
} = dispatchProps;
|
} = dispatchProps;
|
||||||
|
|
||||||
@ -305,17 +289,9 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
|||||||
dispatchCancelAndClose();
|
dispatchCancelAndClose();
|
||||||
} else {
|
} else {
|
||||||
dispatchSetGasData(gasLimit, gasPrice);
|
dispatchSetGasData(gasLimit, gasPrice);
|
||||||
dispatchHideGasButtonGroup();
|
dispatchUseCustomGas();
|
||||||
dispatchCancelAndClose();
|
dispatchCancelAndClose();
|
||||||
}
|
}
|
||||||
if (maxModeOn) {
|
|
||||||
dispatchSetAmountToMax({
|
|
||||||
balance,
|
|
||||||
gasTotal: customGasTotal,
|
|
||||||
sendToken,
|
|
||||||
tokenBalance,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
gasPriceButtonGroupProps: {
|
gasPriceButtonGroupProps: {
|
||||||
...gasPriceButtonGroupProps,
|
...gasPriceButtonGroupProps,
|
||||||
|
@ -15,6 +15,7 @@ export default function TokenCell({
|
|||||||
string,
|
string,
|
||||||
image,
|
image,
|
||||||
onClick,
|
onClick,
|
||||||
|
isERC721,
|
||||||
}) {
|
}) {
|
||||||
const userAddress = useSelector(getSelectedAddress);
|
const userAddress = useSelector(getSelectedAddress);
|
||||||
const t = useI18nContext();
|
const t = useI18nContext();
|
||||||
@ -50,6 +51,7 @@ export default function TokenCell({
|
|||||||
warning={warning}
|
warning={warning}
|
||||||
primary={`${string || 0}`}
|
primary={`${string || 0}`}
|
||||||
secondary={formattedFiat}
|
secondary={formattedFiat}
|
||||||
|
isERC721={isERC721}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -62,6 +64,7 @@ TokenCell.propTypes = {
|
|||||||
string: PropTypes.string,
|
string: PropTypes.string,
|
||||||
image: PropTypes.string,
|
image: PropTypes.string,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
|
isERC721: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
TokenCell.defaultProps = {
|
TokenCell.defaultProps = {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { getIsMainnet, getPreferences } from '../../../selectors';
|
import { getShouldShowFiat } from '../../../selectors';
|
||||||
import { getNativeCurrency } from '../../../ducks/metamask/metamask';
|
import { getNativeCurrency } from '../../../ducks/metamask/metamask';
|
||||||
import { getHexGasTotal } from '../../../helpers/utils/confirm-tx.util';
|
import { getHexGasTotal } from '../../../helpers/utils/confirm-tx.util';
|
||||||
import { sumHexes } from '../../../helpers/utils/transactions.util';
|
import { sumHexes } from '../../../helpers/utils/transactions.util';
|
||||||
@ -11,8 +11,6 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
txParams: { gas, gasPrice, value } = {},
|
txParams: { gas, gasPrice, value } = {},
|
||||||
txReceipt: { gasUsed } = {},
|
txReceipt: { gasUsed } = {},
|
||||||
} = transaction;
|
} = transaction;
|
||||||
const { showFiatInTestnets } = getPreferences(state);
|
|
||||||
const isMainnet = getIsMainnet(state);
|
|
||||||
|
|
||||||
const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas;
|
const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas;
|
||||||
|
|
||||||
@ -22,7 +20,7 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
nativeCurrency: getNativeCurrency(state),
|
nativeCurrency: getNativeCurrency(state),
|
||||||
showFiat: isMainnet || Boolean(showFiatInTestnets),
|
showFiat: getShouldShowFiat(state),
|
||||||
totalInHex,
|
totalInHex,
|
||||||
gas,
|
gas,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
} from '../../../hooks/useMetricEvent';
|
} from '../../../hooks/useMetricEvent';
|
||||||
import { useTokenTracker } from '../../../hooks/useTokenTracker';
|
import { useTokenTracker } from '../../../hooks/useTokenTracker';
|
||||||
import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
|
import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
|
||||||
import { updateSendToken } from '../../../ducks/send/send.duck';
|
import { ASSET_TYPES, updateSendAsset } from '../../../ducks/send';
|
||||||
import { setSwapsFromToken } from '../../../ducks/swaps/swaps';
|
import { setSwapsFromToken } from '../../../ducks/swaps/swaps';
|
||||||
import {
|
import {
|
||||||
getAssetImages,
|
getAssetImages,
|
||||||
@ -85,12 +85,19 @@ const TokenOverview = ({ className, token }) => {
|
|||||||
className="token-overview__button"
|
className="token-overview__button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
sendTokenEvent();
|
sendTokenEvent();
|
||||||
dispatch(updateSendToken(token));
|
dispatch(
|
||||||
|
updateSendAsset({
|
||||||
|
type: ASSET_TYPES.TOKEN,
|
||||||
|
details: token,
|
||||||
|
}),
|
||||||
|
).then(() => {
|
||||||
history.push(SEND_ROUTE);
|
history.push(SEND_ROUTE);
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
Icon={SendIcon}
|
Icon={SendIcon}
|
||||||
label={t('send')}
|
label={t('send')}
|
||||||
data-testid="eth-overview-send"
|
data-testid="eth-overview-send"
|
||||||
|
disabled={token.isERC721}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
className="token-overview__button"
|
className="token-overview__button"
|
||||||
@ -145,6 +152,7 @@ TokenOverview.propTypes = {
|
|||||||
address: PropTypes.string.isRequired,
|
address: PropTypes.string.isRequired,
|
||||||
decimals: PropTypes.number,
|
decimals: PropTypes.number,
|
||||||
symbol: PropTypes.string,
|
symbol: PropTypes.string,
|
||||||
|
isERC721: PropTypes.bool,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { ETH } from '../../../helpers/constants/common';
|
import { ETH } from '../../../helpers/constants/common';
|
||||||
import { getIsMainnet, getPreferences } from '../../../selectors';
|
import { getShouldShowFiat } from '../../../selectors';
|
||||||
import CurrencyInput from './currency-input.component';
|
import CurrencyInput from './currency-input.component';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const {
|
const {
|
||||||
metamask: { nativeCurrency, currentCurrency, conversionRate },
|
metamask: { nativeCurrency, currentCurrency, conversionRate },
|
||||||
} = state;
|
} = state;
|
||||||
const { showFiatInTestnets } = getPreferences(state);
|
const showFiat = getShouldShowFiat(state);
|
||||||
const isMainnet = getIsMainnet(state);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
conversionRate,
|
conversionRate,
|
||||||
hideFiat: !isMainnet && !showFiatInTestnets,
|
hideFiat: !showFiat,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,3 +110,12 @@
|
|||||||
'. actions actions actions actions mid mid mid mid right right right';
|
'. actions actions actions actions mid mid mid mid right right right';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-item--single-content-row {
|
||||||
|
grid-template-areas: 'icon head head head head head head head right right right right';
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
grid-template-areas: 'icon head head head head mid mid mid mid right right right';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,7 +14,11 @@ export default function ListItem({
|
|||||||
className,
|
className,
|
||||||
'data-testid': dataTestId,
|
'data-testid': dataTestId,
|
||||||
}) {
|
}) {
|
||||||
const primaryClassName = classnames('list-item', className);
|
const primaryClassName = classnames(
|
||||||
|
'list-item',
|
||||||
|
className,
|
||||||
|
subtitle || children ? '' : 'list-item--single-content-row',
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -141,6 +141,7 @@ describe('TokenInput Component', () => {
|
|||||||
}}
|
}}
|
||||||
tokenExchangeRates={{ '0x1': 2 }}
|
tokenExchangeRates={{ '0x1': 2 }}
|
||||||
showFiat
|
showFiat
|
||||||
|
currentCurrency="usd"
|
||||||
/>
|
/>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
);
|
);
|
||||||
@ -278,6 +279,7 @@ describe('TokenInput Component', () => {
|
|||||||
}}
|
}}
|
||||||
tokenExchangeRates={{ '0x1': 2 }}
|
tokenExchangeRates={{ '0x1': 2 }}
|
||||||
showFiat
|
showFiat
|
||||||
|
currentCurrency="usd"
|
||||||
/>
|
/>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
);
|
);
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import { getTokenExchangeRates, getShouldShowFiat } from '../../../selectors';
|
||||||
getIsMainnet,
|
|
||||||
getTokenExchangeRates,
|
|
||||||
getPreferences,
|
|
||||||
} from '../../../selectors';
|
|
||||||
import TokenInput from './token-input.component';
|
import TokenInput from './token-input.component';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
const {
|
const {
|
||||||
metamask: { currentCurrency },
|
metamask: { currentCurrency },
|
||||||
} = state;
|
} = state;
|
||||||
const { showFiatInTestnets } = getPreferences(state);
|
|
||||||
const isMainnet = getIsMainnet(state);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentCurrency,
|
currentCurrency,
|
||||||
tokenExchangeRates: getTokenExchangeRates(state),
|
tokenExchangeRates: getTokenExchangeRates(state),
|
||||||
hideConversion: !isMainnet && !showFiatInTestnets,
|
hideConversion: !getShouldShowFiat(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { removeLeadingZeroes } from '../../../pages/send/send.utils';
|
|
||||||
|
function removeLeadingZeroes(str) {
|
||||||
|
return str.replace(/^0*(?=\d)/u, '');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that attaches a suffix or unit of measurement trailing user input, ex. 'ETH'. Also
|
* Component that attaches a suffix or unit of measurement trailing user input, ex. 'ETH'. Also
|
||||||
|
@ -15,10 +15,11 @@ import {
|
|||||||
getNumberOfAccounts,
|
getNumberOfAccounts,
|
||||||
getNumberOfTokens,
|
getNumberOfTokens,
|
||||||
} from '../selectors/selectors';
|
} from '../selectors/selectors';
|
||||||
import { getSendToken } from '../selectors/send';
|
import { getSendAsset, ASSET_TYPES } from '../ducks/send';
|
||||||
import { txDataSelector } from '../selectors/confirm-transaction';
|
import { txDataSelector } from '../selectors/confirm-transaction';
|
||||||
import { getEnvironmentType } from '../../app/scripts/lib/util';
|
import { getEnvironmentType } from '../../app/scripts/lib/util';
|
||||||
import { trackMetaMetricsEvent } from '../store/actions';
|
import { trackMetaMetricsEvent } from '../store/actions';
|
||||||
|
import { getNativeCurrency } from '../ducks/metamask/metamask';
|
||||||
|
|
||||||
export const MetaMetricsContext = createContext(() => {
|
export const MetaMetricsContext = createContext(() => {
|
||||||
captureException(
|
captureException(
|
||||||
@ -31,7 +32,8 @@ export const MetaMetricsContext = createContext(() => {
|
|||||||
export function MetaMetricsProvider({ children }) {
|
export function MetaMetricsProvider({ children }) {
|
||||||
const txData = useSelector(txDataSelector) || {};
|
const txData = useSelector(txDataSelector) || {};
|
||||||
const environmentType = getEnvironmentType();
|
const environmentType = getEnvironmentType();
|
||||||
const activeCurrency = useSelector(getSendToken)?.symbol;
|
const activeAsset = useSelector(getSendAsset);
|
||||||
|
const nativeAssetSymbol = useSelector(getNativeCurrency);
|
||||||
const accountType = useSelector(getAccountType);
|
const accountType = useSelector(getAccountType);
|
||||||
const confirmTransactionOrigin = txData.origin;
|
const confirmTransactionOrigin = txData.origin;
|
||||||
const numberOfTokens = useSelector(getNumberOfTokens);
|
const numberOfTokens = useSelector(getNumberOfTokens);
|
||||||
@ -72,7 +74,10 @@ export function MetaMetricsProvider({ children }) {
|
|||||||
action: eventOpts.action,
|
action: eventOpts.action,
|
||||||
number_of_tokens: numberOfTokens,
|
number_of_tokens: numberOfTokens,
|
||||||
number_of_accounts: numberOfAccounts,
|
number_of_accounts: numberOfAccounts,
|
||||||
active_currency: activeCurrency,
|
active_currency:
|
||||||
|
activeAsset.type === ASSET_TYPES.NATIVE
|
||||||
|
? nativeAssetSymbol
|
||||||
|
: activeAsset?.details?.symbol,
|
||||||
account_type: accountType,
|
account_type: accountType,
|
||||||
is_new_visit: config.is_new_visit,
|
is_new_visit: config.is_new_visit,
|
||||||
// the properties coming from this key will not match our standards for
|
// the properties coming from this key will not match our standards for
|
||||||
@ -102,7 +107,8 @@ export function MetaMetricsProvider({ children }) {
|
|||||||
accountType,
|
accountType,
|
||||||
currentPath,
|
currentPath,
|
||||||
confirmTransactionOrigin,
|
confirmTransactionOrigin,
|
||||||
activeCurrency,
|
activeAsset,
|
||||||
|
nativeAssetSymbol,
|
||||||
numberOfTokens,
|
numberOfTokens,
|
||||||
numberOfAccounts,
|
numberOfAccounts,
|
||||||
environmentType,
|
environmentType,
|
||||||
|
200
ui/ducks/ens.js
Normal file
200
ui/ducks/ens.js
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
|
import ENS from 'ethjs-ens';
|
||||||
|
import log from 'loglevel';
|
||||||
|
import networkMap from 'ethereum-ens-network-map';
|
||||||
|
import { isConfusing } from 'unicode-confusables';
|
||||||
|
import { isHexString } from 'ethereumjs-util';
|
||||||
|
|
||||||
|
import { getCurrentChainId } from '../selectors';
|
||||||
|
import {
|
||||||
|
CHAIN_ID_TO_NETWORK_ID_MAP,
|
||||||
|
MAINNET_NETWORK_ID,
|
||||||
|
} from '../../shared/constants/network';
|
||||||
|
import {
|
||||||
|
CONFUSING_ENS_ERROR,
|
||||||
|
ENS_ILLEGAL_CHARACTER,
|
||||||
|
ENS_NOT_FOUND_ON_NETWORK,
|
||||||
|
ENS_NOT_SUPPORTED_ON_NETWORK,
|
||||||
|
ENS_NO_ADDRESS_FOR_NAME,
|
||||||
|
ENS_REGISTRATION_ERROR,
|
||||||
|
ENS_UNKNOWN_ERROR,
|
||||||
|
} from '../pages/send/send.constants';
|
||||||
|
import { isValidDomainName } from '../helpers/utils/util';
|
||||||
|
import { CHAIN_CHANGED } from '../store/actionConstants';
|
||||||
|
import {
|
||||||
|
BURN_ADDRESS,
|
||||||
|
isBurnAddress,
|
||||||
|
isValidHexAddress,
|
||||||
|
} from '../../shared/modules/hexstring-utils';
|
||||||
|
|
||||||
|
// Local Constants
|
||||||
|
const ZERO_X_ERROR_ADDRESS = '0x';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
stage: 'UNINITIALIZED',
|
||||||
|
resolution: null,
|
||||||
|
error: null,
|
||||||
|
warning: null,
|
||||||
|
network: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ensInitialState = initialState;
|
||||||
|
|
||||||
|
const name = 'ENS';
|
||||||
|
|
||||||
|
let ens = null;
|
||||||
|
|
||||||
|
const slice = createSlice({
|
||||||
|
name,
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
ensLookup: (state, action) => {
|
||||||
|
// first clear out the previous state
|
||||||
|
state.resolution = null;
|
||||||
|
state.error = null;
|
||||||
|
state.warning = null;
|
||||||
|
const { address, ensName, error, network } = action.payload;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
if (
|
||||||
|
isValidDomainName(ensName) &&
|
||||||
|
error.message === 'ENS name not defined.'
|
||||||
|
) {
|
||||||
|
state.error =
|
||||||
|
network === MAINNET_NETWORK_ID
|
||||||
|
? ENS_NO_ADDRESS_FOR_NAME
|
||||||
|
: ENS_NOT_FOUND_ON_NETWORK;
|
||||||
|
} else if (error.message === 'Illegal Character for ENS.') {
|
||||||
|
state.error = ENS_ILLEGAL_CHARACTER;
|
||||||
|
} else {
|
||||||
|
log.error(error);
|
||||||
|
state.error = ENS_UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
} else if (address) {
|
||||||
|
if (address === BURN_ADDRESS) {
|
||||||
|
state.error = ENS_NO_ADDRESS_FOR_NAME;
|
||||||
|
} else if (address === ZERO_X_ERROR_ADDRESS) {
|
||||||
|
state.error = ENS_REGISTRATION_ERROR;
|
||||||
|
} else {
|
||||||
|
state.resolution = address;
|
||||||
|
}
|
||||||
|
if (isValidDomainName(address) && isConfusing(address)) {
|
||||||
|
state.warning = CONFUSING_ENS_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableEnsLookup: (state, action) => {
|
||||||
|
state.stage = 'INITIALIZED';
|
||||||
|
state.error = null;
|
||||||
|
state.resolution = null;
|
||||||
|
state.warning = null;
|
||||||
|
state.network = action.payload;
|
||||||
|
},
|
||||||
|
disableEnsLookup: (state) => {
|
||||||
|
state.stage = 'NO_NETWORK_SUPPORT';
|
||||||
|
state.error = null;
|
||||||
|
state.warning = null;
|
||||||
|
state.resolution = null;
|
||||||
|
state.network = null;
|
||||||
|
},
|
||||||
|
ensNotSupported: (state) => {
|
||||||
|
state.resolution = null;
|
||||||
|
state.warning = null;
|
||||||
|
state.error = ENS_NOT_SUPPORTED_ON_NETWORK;
|
||||||
|
},
|
||||||
|
resetEnsResolution: (state) => {
|
||||||
|
state.resolution = null;
|
||||||
|
state.warning = null;
|
||||||
|
state.error = null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extraReducers: (builder) => {
|
||||||
|
builder.addCase(CHAIN_CHANGED, (state, action) => {
|
||||||
|
if (action.payload !== state.currentChainId) {
|
||||||
|
state.stage = 'UNINITIALIZED';
|
||||||
|
ens = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { reducer, actions } = slice;
|
||||||
|
export default reducer;
|
||||||
|
|
||||||
|
const {
|
||||||
|
disableEnsLookup,
|
||||||
|
ensLookup,
|
||||||
|
enableEnsLookup,
|
||||||
|
ensNotSupported,
|
||||||
|
resetEnsResolution,
|
||||||
|
} = actions;
|
||||||
|
export { resetEnsResolution };
|
||||||
|
|
||||||
|
export function initializeEnsSlice() {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
|
const network = CHAIN_ID_TO_NETWORK_ID_MAP[chainId];
|
||||||
|
const networkIsSupported = Boolean(networkMap[network]);
|
||||||
|
if (networkIsSupported) {
|
||||||
|
ens = new ENS({ provider: global.ethereumProvider, network });
|
||||||
|
dispatch(enableEnsLookup(network));
|
||||||
|
} else {
|
||||||
|
ens = null;
|
||||||
|
dispatch(disableEnsLookup());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function lookupEnsName(ensName) {
|
||||||
|
return async (dispatch, getState) => {
|
||||||
|
const trimmedEnsName = ensName.trim();
|
||||||
|
let state = getState();
|
||||||
|
if (state[name].stage === 'UNINITIALIZED') {
|
||||||
|
await dispatch(initializeEnsSlice());
|
||||||
|
}
|
||||||
|
state = getState();
|
||||||
|
if (
|
||||||
|
state[name].stage === 'NO_NETWORK_SUPPORT' &&
|
||||||
|
!(
|
||||||
|
isBurnAddress(trimmedEnsName) === false &&
|
||||||
|
isValidHexAddress(trimmedEnsName, { mixedCaseUseChecksum: true })
|
||||||
|
) &&
|
||||||
|
!isHexString(trimmedEnsName)
|
||||||
|
) {
|
||||||
|
await dispatch(ensNotSupported());
|
||||||
|
} else {
|
||||||
|
log.info(`ENS attempting to resolve name: ${trimmedEnsName}`);
|
||||||
|
let address;
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
address = await ens.lookup(trimmedEnsName);
|
||||||
|
} catch (err) {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
|
const network = CHAIN_ID_TO_NETWORK_ID_MAP[chainId];
|
||||||
|
await dispatch(
|
||||||
|
ensLookup({
|
||||||
|
ensName: trimmedEnsName,
|
||||||
|
address,
|
||||||
|
error,
|
||||||
|
chainId,
|
||||||
|
network,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEnsResolution(state) {
|
||||||
|
return state[name].resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEnsError(state) {
|
||||||
|
return state[name].error;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEnsWarning(state) {
|
||||||
|
return state[name].warning;
|
||||||
|
}
|
14
ui/ducks/gas/gas-action-constants.js
Normal file
14
ui/ducks/gas/gas-action-constants.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// This file has been separated because it is required in both the gas and send
|
||||||
|
// slices. This created a circular dependency problem as both slices also
|
||||||
|
// import from the actions and selectors files. This easiest path for
|
||||||
|
// untangling is having the constants separate.
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
export const BASIC_GAS_ESTIMATE_STATUS =
|
||||||
|
'metamask/gas/BASIC_GAS_ESTIMATE_STATUS';
|
||||||
|
export const RESET_CUSTOM_DATA = 'metamask/gas/RESET_CUSTOM_DATA';
|
||||||
|
export const SET_BASIC_GAS_ESTIMATE_DATA =
|
||||||
|
'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA';
|
||||||
|
export const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT';
|
||||||
|
export const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE';
|
||||||
|
export const SET_ESTIMATE_SOURCE = 'metamask/gas/SET_ESTIMATE_SOURCE';
|
@ -10,6 +10,14 @@ import GasReducer, {
|
|||||||
fetchBasicGasEstimates,
|
fetchBasicGasEstimates,
|
||||||
} from './gas.duck';
|
} from './gas.duck';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BASIC_GAS_ESTIMATE_STATUS,
|
||||||
|
SET_BASIC_GAS_ESTIMATE_DATA,
|
||||||
|
SET_CUSTOM_GAS_PRICE,
|
||||||
|
SET_CUSTOM_GAS_LIMIT,
|
||||||
|
SET_ESTIMATE_SOURCE,
|
||||||
|
} from './gas-action-constants';
|
||||||
|
|
||||||
jest.mock('../../helpers/utils/storage-helpers.js', () => ({
|
jest.mock('../../helpers/utils/storage-helpers.js', () => ({
|
||||||
getStorageItem: jest.fn(),
|
getStorageItem: jest.fn(),
|
||||||
setStorageItem: jest.fn(),
|
setStorageItem: jest.fn(),
|
||||||
@ -61,13 +69,6 @@ describe('Gas Duck', () => {
|
|||||||
type: 'mainnet',
|
type: 'mainnet',
|
||||||
};
|
};
|
||||||
|
|
||||||
const BASIC_GAS_ESTIMATE_STATUS = 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS';
|
|
||||||
const SET_BASIC_GAS_ESTIMATE_DATA =
|
|
||||||
'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA';
|
|
||||||
const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT';
|
|
||||||
const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE';
|
|
||||||
const SET_ESTIMATE_SOURCE = 'metamask/gas/SET_ESTIMATE_SOURCE';
|
|
||||||
|
|
||||||
describe('GasReducer()', () => {
|
describe('GasReducer()', () => {
|
||||||
it('should initialize state', () => {
|
it('should initialize state', () => {
|
||||||
expect(GasReducer(undefined, {})).toStrictEqual(initState);
|
expect(GasReducer(undefined, {})).toStrictEqual(initState);
|
||||||
|
@ -10,6 +10,14 @@ import {
|
|||||||
} from '../../helpers/utils/conversions.util';
|
} from '../../helpers/utils/conversions.util';
|
||||||
import { getIsMainnet, getCurrentChainId } from '../../selectors';
|
import { getIsMainnet, getCurrentChainId } from '../../selectors';
|
||||||
import fetchWithCache from '../../helpers/utils/fetch-with-cache';
|
import fetchWithCache from '../../helpers/utils/fetch-with-cache';
|
||||||
|
import {
|
||||||
|
BASIC_GAS_ESTIMATE_STATUS,
|
||||||
|
RESET_CUSTOM_DATA,
|
||||||
|
SET_BASIC_GAS_ESTIMATE_DATA,
|
||||||
|
SET_CUSTOM_GAS_LIMIT,
|
||||||
|
SET_CUSTOM_GAS_PRICE,
|
||||||
|
SET_ESTIMATE_SOURCE,
|
||||||
|
} from './gas-action-constants';
|
||||||
|
|
||||||
export const BASIC_ESTIMATE_STATES = {
|
export const BASIC_ESTIMATE_STATES = {
|
||||||
LOADING: 'LOADING',
|
LOADING: 'LOADING',
|
||||||
@ -22,14 +30,6 @@ export const GAS_SOURCE = {
|
|||||||
ETHGASPRICE: 'eth_gasprice',
|
ETHGASPRICE: 'eth_gasprice',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actions
|
|
||||||
const BASIC_GAS_ESTIMATE_STATUS = 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS';
|
|
||||||
const RESET_CUSTOM_DATA = 'metamask/gas/RESET_CUSTOM_DATA';
|
|
||||||
const SET_BASIC_GAS_ESTIMATE_DATA = 'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA';
|
|
||||||
const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT';
|
|
||||||
const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE';
|
|
||||||
const SET_ESTIMATE_SOURCE = 'metamask/gas/SET_ESTIMATE_SOURCE';
|
|
||||||
|
|
||||||
const initState = {
|
const initState = {
|
||||||
customData: {
|
customData: {
|
||||||
price: null,
|
price: null,
|
||||||
|
@ -2,7 +2,8 @@ import { combineReducers } from 'redux';
|
|||||||
import { ALERT_TYPES } from '../../shared/constants/alerts';
|
import { ALERT_TYPES } from '../../shared/constants/alerts';
|
||||||
import metamaskReducer from './metamask/metamask';
|
import metamaskReducer from './metamask/metamask';
|
||||||
import localeMessagesReducer from './locale/locale';
|
import localeMessagesReducer from './locale/locale';
|
||||||
import sendReducer from './send/send.duck';
|
import sendReducer from './send/send';
|
||||||
|
import ensReducer from './ens';
|
||||||
import appStateReducer from './app/app';
|
import appStateReducer from './app/app';
|
||||||
import confirmTransactionReducer from './confirm-transaction/confirm-transaction.duck';
|
import confirmTransactionReducer from './confirm-transaction/confirm-transaction.duck';
|
||||||
import gasReducer from './gas/gas.duck';
|
import gasReducer from './gas/gas.duck';
|
||||||
@ -16,6 +17,7 @@ export default combineReducers({
|
|||||||
activeTab: (s) => (s === undefined ? null : s),
|
activeTab: (s) => (s === undefined ? null : s),
|
||||||
metamask: metamaskReducer,
|
metamask: metamaskReducer,
|
||||||
appState: appStateReducer,
|
appState: appStateReducer,
|
||||||
|
ENS: ensReducer,
|
||||||
history: historyReducer,
|
history: historyReducer,
|
||||||
send: sendReducer,
|
send: sendReducer,
|
||||||
confirmTransaction: confirmTransactionReducer,
|
confirmTransaction: confirmTransactionReducer,
|
||||||
|
1
ui/ducks/send/index.js
Normal file
1
ui/ducks/send/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './send';
|
@ -1,142 +0,0 @@
|
|||||||
import SendReducer, {
|
|
||||||
openToDropdown,
|
|
||||||
closeToDropdown,
|
|
||||||
updateSendErrors,
|
|
||||||
showGasButtonGroup,
|
|
||||||
hideGasButtonGroup,
|
|
||||||
} from './send.duck';
|
|
||||||
|
|
||||||
describe('Send Duck', () => {
|
|
||||||
const mockState = {
|
|
||||||
mockProp: 123,
|
|
||||||
};
|
|
||||||
const initState = {
|
|
||||||
toDropdownOpen: false,
|
|
||||||
gasButtonGroupShown: true,
|
|
||||||
errors: {},
|
|
||||||
gasLimit: null,
|
|
||||||
gasPrice: null,
|
|
||||||
gasTotal: null,
|
|
||||||
tokenBalance: '0x0',
|
|
||||||
from: '',
|
|
||||||
to: '',
|
|
||||||
amount: '0',
|
|
||||||
memo: '',
|
|
||||||
maxModeOn: false,
|
|
||||||
editingTransactionId: null,
|
|
||||||
toNickname: '',
|
|
||||||
ensResolution: null,
|
|
||||||
ensResolutionError: '',
|
|
||||||
gasIsLoading: false,
|
|
||||||
};
|
|
||||||
const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN';
|
|
||||||
const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN';
|
|
||||||
const UPDATE_SEND_ERRORS = 'metamask/send/UPDATE_SEND_ERRORS';
|
|
||||||
const RESET_SEND_STATE = 'metamask/send/RESET_SEND_STATE';
|
|
||||||
const SHOW_GAS_BUTTON_GROUP = 'metamask/send/SHOW_GAS_BUTTON_GROUP';
|
|
||||||
const HIDE_GAS_BUTTON_GROUP = 'metamask/send/HIDE_GAS_BUTTON_GROUP';
|
|
||||||
|
|
||||||
describe('SendReducer()', () => {
|
|
||||||
it('should initialize state', () => {
|
|
||||||
expect(SendReducer(undefined, {})).toStrictEqual(initState);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return state unchanged if it does not match a dispatched actions type', () => {
|
|
||||||
expect(
|
|
||||||
SendReducer(mockState, {
|
|
||||||
type: 'someOtherAction',
|
|
||||||
value: 'someValue',
|
|
||||||
}),
|
|
||||||
).toStrictEqual(mockState);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set toDropdownOpen to true when receiving a OPEN_TO_DROPDOWN action', () => {
|
|
||||||
expect(
|
|
||||||
SendReducer(mockState, {
|
|
||||||
type: OPEN_TO_DROPDOWN,
|
|
||||||
}),
|
|
||||||
).toStrictEqual({ toDropdownOpen: true, ...mockState });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set toDropdownOpen to false when receiving a CLOSE_TO_DROPDOWN action', () => {
|
|
||||||
expect(
|
|
||||||
SendReducer(mockState, {
|
|
||||||
type: CLOSE_TO_DROPDOWN,
|
|
||||||
}),
|
|
||||||
).toStrictEqual({ toDropdownOpen: false, ...mockState });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set gasButtonGroupShown to true when receiving a SHOW_GAS_BUTTON_GROUP action', () => {
|
|
||||||
expect(
|
|
||||||
SendReducer(
|
|
||||||
{ ...mockState, gasButtonGroupShown: false },
|
|
||||||
{ type: SHOW_GAS_BUTTON_GROUP },
|
|
||||||
),
|
|
||||||
).toStrictEqual({ gasButtonGroupShown: true, ...mockState });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set gasButtonGroupShown to false when receiving a HIDE_GAS_BUTTON_GROUP action', () => {
|
|
||||||
expect(
|
|
||||||
SendReducer(mockState, { type: HIDE_GAS_BUTTON_GROUP }),
|
|
||||||
).toStrictEqual({ gasButtonGroupShown: false, ...mockState });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should extend send.errors with the value of a UPDATE_SEND_ERRORS action', () => {
|
|
||||||
const modifiedMockState = {
|
|
||||||
...mockState,
|
|
||||||
errors: {
|
|
||||||
someError: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
expect(
|
|
||||||
SendReducer(modifiedMockState, {
|
|
||||||
type: UPDATE_SEND_ERRORS,
|
|
||||||
value: { someOtherError: true },
|
|
||||||
}),
|
|
||||||
).toStrictEqual({
|
|
||||||
...modifiedMockState,
|
|
||||||
errors: {
|
|
||||||
someError: false,
|
|
||||||
someOtherError: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return the initial state in response to a RESET_SEND_STATE action', () => {
|
|
||||||
expect(
|
|
||||||
SendReducer(mockState, {
|
|
||||||
type: RESET_SEND_STATE,
|
|
||||||
}),
|
|
||||||
).toStrictEqual(initState);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Send Duck Actions', () => {
|
|
||||||
it('calls openToDropdown action', () => {
|
|
||||||
expect(openToDropdown()).toStrictEqual({ type: OPEN_TO_DROPDOWN });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls closeToDropdown action', () => {
|
|
||||||
expect(closeToDropdown()).toStrictEqual({ type: CLOSE_TO_DROPDOWN });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls showGasButtonGroup action', () => {
|
|
||||||
expect(showGasButtonGroup()).toStrictEqual({
|
|
||||||
type: SHOW_GAS_BUTTON_GROUP,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls hideGasButtonGroup action', () => {
|
|
||||||
expect(hideGasButtonGroup()).toStrictEqual({
|
|
||||||
type: HIDE_GAS_BUTTON_GROUP,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls updateSendErrors action', () => {
|
|
||||||
expect(updateSendErrors('mockErrorObject')).toStrictEqual({
|
|
||||||
type: UPDATE_SEND_ERRORS,
|
|
||||||
value: 'mockErrorObject',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,382 +0,0 @@
|
|||||||
import log from 'loglevel';
|
|
||||||
import { estimateGas } from '../../store/actions';
|
|
||||||
import { setCustomGasLimit } from '../gas/gas.duck';
|
|
||||||
import {
|
|
||||||
estimateGasForSend,
|
|
||||||
calcTokenBalance,
|
|
||||||
} from '../../pages/send/send.utils';
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN';
|
|
||||||
const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN';
|
|
||||||
const UPDATE_SEND_ERRORS = 'metamask/send/UPDATE_SEND_ERRORS';
|
|
||||||
const RESET_SEND_STATE = 'metamask/send/RESET_SEND_STATE';
|
|
||||||
const SHOW_GAS_BUTTON_GROUP = 'metamask/send/SHOW_GAS_BUTTON_GROUP';
|
|
||||||
const HIDE_GAS_BUTTON_GROUP = 'metamask/send/HIDE_GAS_BUTTON_GROUP';
|
|
||||||
const UPDATE_GAS_LIMIT = 'UPDATE_GAS_LIMIT';
|
|
||||||
const UPDATE_GAS_PRICE = 'UPDATE_GAS_PRICE';
|
|
||||||
const UPDATE_GAS_TOTAL = 'UPDATE_GAS_TOTAL';
|
|
||||||
const UPDATE_SEND_HEX_DATA = 'UPDATE_SEND_HEX_DATA';
|
|
||||||
const UPDATE_SEND_TOKEN_BALANCE = 'UPDATE_SEND_TOKEN_BALANCE';
|
|
||||||
const UPDATE_SEND_TO = 'UPDATE_SEND_TO';
|
|
||||||
const UPDATE_SEND_AMOUNT = 'UPDATE_SEND_AMOUNT';
|
|
||||||
const UPDATE_MAX_MODE = 'UPDATE_MAX_MODE';
|
|
||||||
const UPDATE_SEND = 'UPDATE_SEND';
|
|
||||||
const UPDATE_SEND_TOKEN = 'UPDATE_SEND_TOKEN';
|
|
||||||
const CLEAR_SEND = 'CLEAR_SEND';
|
|
||||||
const GAS_LOADING_STARTED = 'GAS_LOADING_STARTED';
|
|
||||||
const GAS_LOADING_FINISHED = 'GAS_LOADING_FINISHED';
|
|
||||||
const UPDATE_SEND_ENS_RESOLUTION = 'UPDATE_SEND_ENS_RESOLUTION';
|
|
||||||
const UPDATE_SEND_ENS_RESOLUTION_ERROR = 'UPDATE_SEND_ENS_RESOLUTION_ERROR';
|
|
||||||
|
|
||||||
const initState = {
|
|
||||||
toDropdownOpen: false,
|
|
||||||
gasButtonGroupShown: true,
|
|
||||||
errors: {},
|
|
||||||
gasLimit: null,
|
|
||||||
gasPrice: null,
|
|
||||||
gasTotal: null,
|
|
||||||
tokenBalance: '0x0',
|
|
||||||
from: '',
|
|
||||||
to: '',
|
|
||||||
amount: '0',
|
|
||||||
memo: '',
|
|
||||||
maxModeOn: false,
|
|
||||||
editingTransactionId: null,
|
|
||||||
toNickname: '',
|
|
||||||
ensResolution: null,
|
|
||||||
ensResolutionError: '',
|
|
||||||
gasIsLoading: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reducer
|
|
||||||
export default function reducer(state = initState, action) {
|
|
||||||
switch (action.type) {
|
|
||||||
case OPEN_TO_DROPDOWN:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
toDropdownOpen: true,
|
|
||||||
};
|
|
||||||
case CLOSE_TO_DROPDOWN:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
toDropdownOpen: false,
|
|
||||||
};
|
|
||||||
case UPDATE_SEND_ERRORS:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
errors: {
|
|
||||||
...state.errors,
|
|
||||||
...action.value,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
case SHOW_GAS_BUTTON_GROUP:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasButtonGroupShown: true,
|
|
||||||
};
|
|
||||||
case HIDE_GAS_BUTTON_GROUP:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasButtonGroupShown: false,
|
|
||||||
};
|
|
||||||
case UPDATE_GAS_LIMIT:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasLimit: action.value,
|
|
||||||
};
|
|
||||||
case UPDATE_GAS_PRICE:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasPrice: action.value,
|
|
||||||
};
|
|
||||||
case RESET_SEND_STATE:
|
|
||||||
return { ...initState };
|
|
||||||
case UPDATE_GAS_TOTAL:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasTotal: action.value,
|
|
||||||
};
|
|
||||||
case UPDATE_SEND_TOKEN_BALANCE:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
tokenBalance: action.value,
|
|
||||||
};
|
|
||||||
case UPDATE_SEND_HEX_DATA:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data: action.value,
|
|
||||||
};
|
|
||||||
case UPDATE_SEND_TO:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
to: action.value.to,
|
|
||||||
toNickname: action.value.nickname,
|
|
||||||
};
|
|
||||||
case UPDATE_SEND_AMOUNT:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
amount: action.value,
|
|
||||||
};
|
|
||||||
case UPDATE_MAX_MODE:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
maxModeOn: action.value,
|
|
||||||
};
|
|
||||||
case UPDATE_SEND:
|
|
||||||
return Object.assign(state, action.value);
|
|
||||||
case UPDATE_SEND_TOKEN: {
|
|
||||||
const newSend = {
|
|
||||||
...state,
|
|
||||||
token: action.value,
|
|
||||||
};
|
|
||||||
// erase token-related state when switching back to native currency
|
|
||||||
if (newSend.editingTransactionId && !newSend.token) {
|
|
||||||
const unapprovedTx =
|
|
||||||
newSend?.unapprovedTxs?.[newSend.editingTransactionId] || {};
|
|
||||||
const txParams = unapprovedTx.txParams || {};
|
|
||||||
Object.assign(newSend, {
|
|
||||||
tokenBalance: null,
|
|
||||||
balance: '0',
|
|
||||||
from: unapprovedTx.from || '',
|
|
||||||
unapprovedTxs: {
|
|
||||||
...newSend.unapprovedTxs,
|
|
||||||
[newSend.editingTransactionId]: {
|
|
||||||
...unapprovedTx,
|
|
||||||
txParams: {
|
|
||||||
...txParams,
|
|
||||||
data: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Object.assign(state, newSend);
|
|
||||||
}
|
|
||||||
case UPDATE_SEND_ENS_RESOLUTION:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
ensResolution: action.payload,
|
|
||||||
ensResolutionError: '',
|
|
||||||
};
|
|
||||||
case UPDATE_SEND_ENS_RESOLUTION_ERROR:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
ensResolution: null,
|
|
||||||
ensResolutionError: action.payload,
|
|
||||||
};
|
|
||||||
case CLEAR_SEND:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasLimit: null,
|
|
||||||
gasPrice: null,
|
|
||||||
gasTotal: null,
|
|
||||||
tokenBalance: null,
|
|
||||||
from: '',
|
|
||||||
to: '',
|
|
||||||
amount: '0x0',
|
|
||||||
memo: '',
|
|
||||||
errors: {},
|
|
||||||
maxModeOn: false,
|
|
||||||
editingTransactionId: null,
|
|
||||||
toNickname: '',
|
|
||||||
};
|
|
||||||
case GAS_LOADING_STARTED:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasIsLoading: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
case GAS_LOADING_FINISHED:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
gasIsLoading: false,
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Action Creators
|
|
||||||
export function openToDropdown() {
|
|
||||||
return { type: OPEN_TO_DROPDOWN };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function closeToDropdown() {
|
|
||||||
return { type: CLOSE_TO_DROPDOWN };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showGasButtonGroup() {
|
|
||||||
return { type: SHOW_GAS_BUTTON_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hideGasButtonGroup() {
|
|
||||||
return { type: HIDE_GAS_BUTTON_GROUP };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendErrors(errorObject) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_ERRORS,
|
|
||||||
value: errorObject,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetSendState() {
|
|
||||||
return { type: RESET_SEND_STATE };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setGasLimit(gasLimit) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_GAS_LIMIT,
|
|
||||||
value: gasLimit,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setGasPrice(gasPrice) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_GAS_PRICE,
|
|
||||||
value: gasPrice,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setGasTotal(gasTotal) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_GAS_TOTAL,
|
|
||||||
value: gasTotal,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateGasData({
|
|
||||||
gasPrice,
|
|
||||||
blockGasLimit,
|
|
||||||
selectedAddress,
|
|
||||||
sendToken,
|
|
||||||
to,
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
}) {
|
|
||||||
return (dispatch) => {
|
|
||||||
dispatch(gasLoadingStarted());
|
|
||||||
return estimateGasForSend({
|
|
||||||
estimateGasMethod: estimateGas,
|
|
||||||
blockGasLimit,
|
|
||||||
selectedAddress,
|
|
||||||
sendToken,
|
|
||||||
to,
|
|
||||||
value,
|
|
||||||
estimateGasPrice: gasPrice,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
.then((gas) => {
|
|
||||||
dispatch(setGasLimit(gas));
|
|
||||||
dispatch(setCustomGasLimit(gas));
|
|
||||||
dispatch(updateSendErrors({ gasLoadingError: null }));
|
|
||||||
dispatch(gasLoadingFinished());
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
log.error(err);
|
|
||||||
dispatch(updateSendErrors({ gasLoadingError: 'gasLoadingError' }));
|
|
||||||
dispatch(gasLoadingFinished());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function gasLoadingStarted() {
|
|
||||||
return {
|
|
||||||
type: GAS_LOADING_STARTED,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function gasLoadingFinished() {
|
|
||||||
return {
|
|
||||||
type: GAS_LOADING_FINISHED,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendTokenBalance({ sendToken, tokenContract, address }) {
|
|
||||||
return (dispatch) => {
|
|
||||||
const tokenBalancePromise = tokenContract
|
|
||||||
? tokenContract.balanceOf(address)
|
|
||||||
: Promise.resolve();
|
|
||||||
return tokenBalancePromise
|
|
||||||
.then((usersToken) => {
|
|
||||||
if (usersToken) {
|
|
||||||
const newTokenBalance = calcTokenBalance({ sendToken, usersToken });
|
|
||||||
dispatch(setSendTokenBalance(newTokenBalance));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
log.error(err);
|
|
||||||
updateSendErrors({ tokenBalance: 'tokenBalanceError' });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setSendTokenBalance(tokenBalance) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_TOKEN_BALANCE,
|
|
||||||
value: tokenBalance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendHexData(value) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_HEX_DATA,
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendTo(to, nickname = '') {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_TO,
|
|
||||||
value: { to, nickname },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendAmount(amount) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_AMOUNT,
|
|
||||||
value: amount,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setMaxModeTo(bool) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_MAX_MODE,
|
|
||||||
value: bool,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSend(newSend) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND,
|
|
||||||
value: newSend,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendToken(token) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_TOKEN,
|
|
||||||
value: token,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearSend() {
|
|
||||||
return {
|
|
||||||
type: CLEAR_SEND,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendEnsResolution(ensResolution) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_ENS_RESOLUTION,
|
|
||||||
payload: ensResolution,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSendEnsResolutionError(errorMessage) {
|
|
||||||
return {
|
|
||||||
type: UPDATE_SEND_ENS_RESOLUTION_ERROR,
|
|
||||||
payload: errorMessage,
|
|
||||||
};
|
|
||||||
}
|
|
1508
ui/ducks/send/send.js
Normal file
1508
ui/ducks/send/send.js
Normal file
File diff suppressed because it is too large
Load Diff
1859
ui/ducks/send/send.test.js
Normal file
1859
ui/ducks/send/send.test.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -34,9 +34,10 @@ import {
|
|||||||
SWAPS_MAINTENANCE_ROUTE,
|
SWAPS_MAINTENANCE_ROUTE,
|
||||||
} from '../../helpers/constants/routes';
|
} from '../../helpers/constants/routes';
|
||||||
import {
|
import {
|
||||||
fetchSwapsFeatureLiveness,
|
fetchSwapsFeatureFlags,
|
||||||
fetchSwapsGasPrices,
|
fetchSwapsGasPrices,
|
||||||
isContractAddressValid,
|
isContractAddressValid,
|
||||||
|
getSwapsLivenessForNetwork,
|
||||||
} from '../../pages/swaps/swaps.util';
|
} from '../../pages/swaps/swaps.util';
|
||||||
import { calcGasTotal } from '../../pages/send/send.utils';
|
import { calcGasTotal } from '../../pages/send/send.utils';
|
||||||
import {
|
import {
|
||||||
@ -223,9 +224,12 @@ export function shouldShowCustomPriceTooLowWarning(state) {
|
|||||||
|
|
||||||
const getSwapsState = (state) => state.metamask.swapsState;
|
const getSwapsState = (state) => state.metamask.swapsState;
|
||||||
|
|
||||||
export const getSwapsFeatureLiveness = (state) =>
|
export const getSwapsFeatureIsLive = (state) =>
|
||||||
state.metamask.swapsState.swapsFeatureIsLive;
|
state.metamask.swapsState.swapsFeatureIsLive;
|
||||||
|
|
||||||
|
export const getUseNewSwapsApi = (state) =>
|
||||||
|
state.metamask.swapsState.useNewSwapsApi;
|
||||||
|
|
||||||
export const getSwapsQuoteRefreshTime = (state) =>
|
export const getSwapsQuoteRefreshTime = (state) =>
|
||||||
state.metamask.swapsState.swapsQuoteRefreshTime;
|
state.metamask.swapsState.swapsQuoteRefreshTime;
|
||||||
|
|
||||||
@ -373,16 +377,21 @@ export const fetchAndSetSwapsGasPriceInfo = () => {
|
|||||||
|
|
||||||
export const fetchSwapsLiveness = () => {
|
export const fetchSwapsLiveness = () => {
|
||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
let swapsFeatureIsLive = false;
|
let swapsLivenessForNetwork = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(
|
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
|
||||||
|
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
|
||||||
|
swapsFeatureFlags,
|
||||||
getCurrentChainId(getState()),
|
getCurrentChainId(getState()),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
||||||
}
|
}
|
||||||
await dispatch(setSwapsLiveness(swapsFeatureIsLive));
|
await dispatch(setSwapsLiveness(swapsLivenessForNetwork));
|
||||||
return swapsFeatureIsLive;
|
return swapsLivenessForNetwork;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -395,15 +404,22 @@ export const fetchQuotesAndSetQuoteState = (
|
|||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
let swapsFeatureIsLive = false;
|
let swapsLivenessForNetwork = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId);
|
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
|
||||||
|
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
|
||||||
|
swapsFeatureFlags,
|
||||||
|
chainId,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
||||||
}
|
}
|
||||||
await dispatch(setSwapsLiveness(swapsFeatureIsLive));
|
await dispatch(setSwapsLiveness(swapsLivenessForNetwork));
|
||||||
|
|
||||||
if (!swapsFeatureIsLive) {
|
if (!swapsLivenessForNetwork.swapsFeatureIsLive) {
|
||||||
await history.push(SWAPS_MAINTENANCE_ROUTE);
|
await history.push(SWAPS_MAINTENANCE_ROUTE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -600,15 +616,22 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
|||||||
const state = getState();
|
const state = getState();
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
const hardwareWalletUsed = isHardwareWallet(state);
|
const hardwareWalletUsed = isHardwareWallet(state);
|
||||||
let swapsFeatureIsLive = false;
|
let swapsLivenessForNetwork = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId);
|
const swapsFeatureFlags = await fetchSwapsFeatureFlags();
|
||||||
|
swapsLivenessForNetwork = getSwapsLivenessForNetwork(
|
||||||
|
swapsFeatureFlags,
|
||||||
|
chainId,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
log.error('Failed to fetch Swaps liveness, defaulting to false.', error);
|
||||||
}
|
}
|
||||||
await dispatch(setSwapsLiveness(swapsFeatureIsLive));
|
await dispatch(setSwapsLiveness(swapsLivenessForNetwork));
|
||||||
|
|
||||||
if (!swapsFeatureIsLive) {
|
if (!swapsLivenessForNetwork.swapsFeatureIsLive) {
|
||||||
await history.push(SWAPS_MAINTENANCE_ROUTE);
|
await history.push(SWAPS_MAINTENANCE_ROUTE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -808,12 +831,13 @@ export function fetchMetaSwapsGasPriceEstimates() {
|
|||||||
return async (dispatch, getState) => {
|
return async (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const chainId = getCurrentChainId(state);
|
const chainId = getCurrentChainId(state);
|
||||||
|
const useNewSwapsApi = getUseNewSwapsApi(state);
|
||||||
|
|
||||||
dispatch(swapGasPriceEstimatesFetchStarted());
|
dispatch(swapGasPriceEstimatesFetchStarted());
|
||||||
|
|
||||||
let priceEstimates;
|
let priceEstimates;
|
||||||
try {
|
try {
|
||||||
priceEstimates = await fetchSwapsGasPrices(chainId);
|
priceEstimates = await fetchSwapsGasPrices(chainId, useNewSwapsApi);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warn('Fetching swaps gas prices failed:', e);
|
log.warn('Fetching swaps gas prices failed:', e);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import nock from 'nock';
|
import nock from 'nock';
|
||||||
|
|
||||||
|
import { MOCKS } from '../../../test/jest';
|
||||||
import { setSwapsLiveness } from '../../store/actions';
|
import { setSwapsLiveness } from '../../store/actions';
|
||||||
import { setStorageItem } from '../../helpers/utils/storage-helpers';
|
import { setStorageItem } from '../../helpers/utils/storage-helpers';
|
||||||
import * as swaps from './swaps';
|
import * as swaps from './swaps';
|
||||||
@ -25,7 +26,7 @@ describe('Ducks - Swaps', () => {
|
|||||||
describe('fetchSwapsLiveness', () => {
|
describe('fetchSwapsLiveness', () => {
|
||||||
const cleanFeatureFlagApiCache = () => {
|
const cleanFeatureFlagApiCache = () => {
|
||||||
setStorageItem(
|
setStorageItem(
|
||||||
'cachedFetch:https://api.metaswap.codefi.network/featureFlag',
|
'cachedFetch:https://api2.metaswap.codefi.network/featureFlags',
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -34,12 +35,12 @@ describe('Ducks - Swaps', () => {
|
|||||||
cleanFeatureFlagApiCache();
|
cleanFeatureFlagApiCache();
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockFeatureFlagApiResponse = ({
|
const mockFeatureFlagsApiResponse = ({
|
||||||
active = false,
|
featureFlagsResponse,
|
||||||
replyWithError = false,
|
replyWithError = false,
|
||||||
} = {}) => {
|
} = {}) => {
|
||||||
const apiNock = nock('https://api.metaswap.codefi.network').get(
|
const apiNock = nock('https://api2.metaswap.codefi.network').get(
|
||||||
'/featureFlag',
|
'/featureFlags',
|
||||||
);
|
);
|
||||||
if (replyWithError) {
|
if (replyWithError) {
|
||||||
return apiNock.replyWithError({
|
return apiNock.replyWithError({
|
||||||
@ -47,9 +48,7 @@ describe('Ducks - Swaps', () => {
|
|||||||
code: 'serverSideError',
|
code: 'serverSideError',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return apiNock.reply(200, {
|
return apiNock.reply(200, featureFlagsResponse);
|
||||||
active,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createGetState = () => {
|
const createGetState = () => {
|
||||||
@ -58,61 +57,111 @@ describe('Ducks - Swaps', () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
it('returns true if the Swaps feature is enabled', async () => {
|
it('checks that Swaps for ETH are enabled and can use new API', async () => {
|
||||||
const mockDispatch = jest.fn();
|
const mockDispatch = jest.fn();
|
||||||
const featureFlagApiNock = mockFeatureFlagApiResponse({ active: true });
|
const expectedSwapsLiveness = {
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: true,
|
||||||
|
};
|
||||||
|
const featureFlagsResponse = MOCKS.createFeatureFlagsResponse();
|
||||||
|
const featureFlagApiNock = mockFeatureFlagsApiResponse({
|
||||||
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(true);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(true);
|
expect(swapsLiveness).toMatchObject(expectedSwapsLiveness);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns false if the Swaps feature is disabled', async () => {
|
it('checks that Swaps for ETH are disabled for API v2 and enabled for API v1', async () => {
|
||||||
const mockDispatch = jest.fn();
|
const mockDispatch = jest.fn();
|
||||||
const featureFlagApiNock = mockFeatureFlagApiResponse({ active: false });
|
const expectedSwapsLiveness = {
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
|
const featureFlagsResponse = MOCKS.createFeatureFlagsResponse();
|
||||||
|
featureFlagsResponse.ethereum.extension_active = false;
|
||||||
|
const featureFlagApiNock = mockFeatureFlagsApiResponse({
|
||||||
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(false);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(false);
|
expect(swapsLiveness).toMatchObject(expectedSwapsLiveness);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns false if the /featureFlag API call throws an error', async () => {
|
it('checks that Swaps for ETH are disabled for API v1 and v2', async () => {
|
||||||
const mockDispatch = jest.fn();
|
const mockDispatch = jest.fn();
|
||||||
const featureFlagApiNock = mockFeatureFlagApiResponse({
|
const expectedSwapsLiveness = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
|
const featureFlagsResponse = MOCKS.createFeatureFlagsResponse();
|
||||||
|
featureFlagsResponse.ethereum.extension_active = false;
|
||||||
|
featureFlagsResponse.ethereum.fallback_to_v1 = false;
|
||||||
|
const featureFlagApiNock = mockFeatureFlagsApiResponse({
|
||||||
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
|
mockDispatch,
|
||||||
|
createGetState(),
|
||||||
|
);
|
||||||
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
|
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||||
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
|
expect(swapsLiveness).toMatchObject(expectedSwapsLiveness);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks that Swaps for ETH are disabled if the /featureFlags API call throws an error', async () => {
|
||||||
|
const mockDispatch = jest.fn();
|
||||||
|
const expectedSwapsLiveness = {
|
||||||
|
swapsFeatureIsLive: false,
|
||||||
|
useNewSwapsApi: false,
|
||||||
|
};
|
||||||
|
const featureFlagApiNock = mockFeatureFlagsApiResponse({
|
||||||
replyWithError: true,
|
replyWithError: true,
|
||||||
});
|
});
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
expect(mockDispatch).toHaveBeenCalledTimes(1);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(false);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(false);
|
expect(swapsLiveness).toMatchObject(expectedSwapsLiveness);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('only calls the API once and returns true from cache for the second call', async () => {
|
it('only calls the API once and returns response from cache for the second call', async () => {
|
||||||
const mockDispatch = jest.fn();
|
const mockDispatch = jest.fn();
|
||||||
const featureFlagApiNock = mockFeatureFlagApiResponse({ active: true });
|
const expectedSwapsLiveness = {
|
||||||
|
swapsFeatureIsLive: true,
|
||||||
|
useNewSwapsApi: true,
|
||||||
|
};
|
||||||
|
const featureFlagsResponse = MOCKS.createFeatureFlagsResponse();
|
||||||
|
const featureFlagApiNock = mockFeatureFlagsApiResponse({
|
||||||
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
await swaps.fetchSwapsLiveness()(mockDispatch, createGetState());
|
await swaps.fetchSwapsLiveness()(mockDispatch, createGetState());
|
||||||
expect(featureFlagApiNock.isDone()).toBe(true);
|
expect(featureFlagApiNock.isDone()).toBe(true);
|
||||||
const featureFlagApiNock2 = mockFeatureFlagApiResponse({ active: true });
|
const featureFlagApiNock2 = mockFeatureFlagsApiResponse({
|
||||||
const isSwapsFeatureEnabled = await swaps.fetchSwapsLiveness()(
|
featureFlagsResponse,
|
||||||
|
});
|
||||||
|
const swapsLiveness = await swaps.fetchSwapsLiveness()(
|
||||||
mockDispatch,
|
mockDispatch,
|
||||||
createGetState(),
|
createGetState(),
|
||||||
);
|
);
|
||||||
expect(featureFlagApiNock2.isDone()).toBe(false); // Second API call wasn't made, cache was used instead.
|
expect(featureFlagApiNock2.isDone()).toBe(false); // Second API call wasn't made, cache was used instead.
|
||||||
expect(mockDispatch).toHaveBeenCalledTimes(2);
|
expect(mockDispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(setSwapsLiveness).toHaveBeenCalledWith(true);
|
expect(setSwapsLiveness).toHaveBeenCalledWith(expectedSwapsLiveness);
|
||||||
expect(isSwapsFeatureEnabled).toBe(true);
|
expect(swapsLiveness).toMatchObject(expectedSwapsLiveness);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -5,3 +5,4 @@ export const TRANSACTION_NO_CONTRACT_ERROR_KEY = 'transactionErrorNoContract';
|
|||||||
export const ETH_GAS_PRICE_FETCH_WARNING_KEY = 'ethGasPriceFetchWarning';
|
export const ETH_GAS_PRICE_FETCH_WARNING_KEY = 'ethGasPriceFetchWarning';
|
||||||
export const GAS_PRICE_FETCH_FAILURE_ERROR_KEY = 'gasPriceFetchFailed';
|
export const GAS_PRICE_FETCH_FAILURE_ERROR_KEY = 'gasPriceFetchFailed';
|
||||||
export const GAS_PRICE_EXCESSIVE_ERROR_KEY = 'gasPriceExcessive';
|
export const GAS_PRICE_EXCESSIVE_ERROR_KEY = 'gasPriceExcessive';
|
||||||
|
export const UNSENDABLE_ASSET_ERROR_KEY = 'unsendableAsset';
|
||||||
|
@ -150,8 +150,11 @@ const conversionUtil = (
|
|||||||
conversionRate,
|
conversionRate,
|
||||||
invertConversionRate,
|
invertConversionRate,
|
||||||
},
|
},
|
||||||
) =>
|
) => {
|
||||||
converter({
|
if (fromCurrency !== toCurrency && !conversionRate) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return converter({
|
||||||
fromCurrency,
|
fromCurrency,
|
||||||
toCurrency,
|
toCurrency,
|
||||||
fromNumericBase,
|
fromNumericBase,
|
||||||
@ -163,6 +166,7 @@ const conversionUtil = (
|
|||||||
invertConversionRate,
|
invertConversionRate,
|
||||||
value: value || '0',
|
value: value || '0',
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getBigNumber = (value, base) => {
|
const getBigNumber = (value, base) => {
|
||||||
if (!isValidBase(base)) {
|
if (!isValidBase(base)) {
|
||||||
|
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