diff options
128 files changed, 2808 insertions, 1951 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 00ba9ffa2..60867e181 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -324,7 +324,7 @@ jobs: command: > git config user.name metamaskbot && git config user.email admin@metamask.io && - gh-pages -d docs/jsdocs + npm run publish-docs test-unit: docker: diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a9d7703c..312260c65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Current Develop Branch +- [#5563](https://github.com/MetaMask/metamask-extension/pull/5563#pullrequestreview-166769174) Feature: improve Hatian Creole translations +- [#5559](https://github.com/MetaMask/metamask-extension/pull/5559) Localize language names in translation select list + ## 4.16.0 Wednesday October 17 2018 - Feature: Add toggle for primary currency (eth/fiat) diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index bf5854a31..5423c76dc 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -692,9 +692,27 @@ "newRecipient": { "message": "New Recipient" }, - "newRPC": { + "newNetwork": { + "message": "New Network" + }, + "rpcURL": { "message": "New RPC URL" }, + "showAdvancedOptions": { + "message": "Show Advanced Options" + }, + "hideAdvancedOptions": { + "message": "Hide Advanced Options" + }, + "optionalChainId": { + "message": "ChainID (optional)" + }, + "optionalSymbol": { + "message": "Symbol (optional)" + }, + "optionalNickname": { + "message": "Nickname (optional)" + }, "next": { "message": "Next" }, @@ -803,7 +821,7 @@ "message": "Primary Currency" }, "primaryCurrencySettingDescription": { - "message": "Select ETH to prioritize displaying values in ETH. Select Fiat to prioritize displaying values in your selected currency." + "message": "Select native to prioritize displaying values in the native currency of the chain (e.g. ETH). Select Fiat to prioritize displaying values in your selected fiat currency." }, "privacyMsg": { "message": "Privacy Policy" @@ -1169,6 +1187,9 @@ "transactionUpdatedGas": { "message": "Transaction updated with a gas price of $1 on $2." }, + "transactionErrored": { + "message": "Transaction encountered an error." + }, "transactions": { "message": "transactions" }, diff --git a/app/_locales/ht/messages.json b/app/_locales/ht/messages.json index 50a0ec2bb..81ce18018 100644 --- a/app/_locales/ht/messages.json +++ b/app/_locales/ht/messages.json @@ -1,1237 +1,1312 @@ { - "accept": { - "message": "Aksepte" - }, - "accessingYourCamera": { - "message": "Aksè a Kamera" - }, - "account": { - "message": "Kont" - }, - "accountDetails": { - "message": "Detay Kont" - }, - "accountName": { - "message": "Non Kont" - }, - "accountSelectionRequired": { - "message": "Ou bezwen chwazi yon kont!" - }, - "activityLog": { - "message": "aktivite ki fèt" - }, - "address": { - "message": "Adrès" - }, - "addCustomToken": { - "message": "Ajoute token" - }, - "addToken": { - "message": "Ajoute Token" - }, - "addTokens": { - "message": "Ajoute Token" - }, - "addSuggestedTokens": { - "message": "Ajoute Token Yo Sikjere W" - }, - "addAcquiredTokens": { - "message": "Ajoute tokens yo ou te achte lè l sèvi avèk MetaMask" - }, - "amount": { - "message": "Kantite lajan" - }, - "amountPlusGas": { - "message": "Kantite lajan + Gaz" - }, - "appDescription": { - "message": "Ethereum Ekstansyon Navigatè", - "description": "The description of the application" - }, - "appName": { - "message": "MetaMask", - "description": "The name of the application" - }, - "approve": { - "message": "Apwouve" - }, - "approved": { - "message": "Apwouve" - }, - "attemptingConnect": { - "message": "Eseye konekte nan blockchain." - }, - "attributions": { - "message": "Atribisyon" - }, - "available": { - "message": "Disponib" - }, - "back": { - "message": "Retounen" - }, - "balance": { - "message": "Balans" - }, - "balances": { - "message": "Balans Token" - }, - "balanceIsInsufficientGas": { - "message": "Ensifizan balans pou total gaz aktyèl la" - }, - "beta": { - "message": "BETA" - }, - "betweenMinAndMax": { - "message": "dwe plis pase oswa egal a $ 1 mwens ke oswa egal a $ 2.", - "description": "helper for inputting hex as decimal input" - }, - "blockiesIdenticon": { - "message": "Itilize Blockies Identicon" - }, - "borrowDharma": { - "message": "Prete Avèk Dharma (Beta)" - }, - "browserNotSupported": { - "message": "Navigatè ou a pa sipòte..." - }, - "builtInCalifornia": { - "message": "MetaMask fèt e bati nan California." - }, - "buy": { - "message": "Achte" - }, - "buyCoinbase": { - "message": "Achte sou Coinbase" - }, - "buyCoinbaseExplainer": { - "message": "Coinbase se fason ki pi popilè nan mond lan yo achte ak vann Bitcoin, Ethereum, ak Litecoin." - }, - "bytes": { - "message": "Bytes" - }, - "ok": { - "message": "Oke" - }, - "cancel": { - "message": "Anile" - }, - "classicInterface": { - "message": "Sèvi ak fas klasik la" - }, - "clickCopy": { - "message": "Klike sou kopi" - }, - "close": { - "message": "Fèmen" - }, - "chromeRequiredForHardwareWallets": { - "message": "Ou bezwen sèvi ak MetaMask sou Google Chrome yo nan lòd yo konekte sou Hardware Wallet." - }, - "confirm": { - "message": "Konfime" - }, - "confirmed": { - "message": "Konfime" - }, - "confirmContract": { - "message": "Konfime Kontra" - }, - "confirmPassword": { - "message": "Konfime Modpas" - }, - "confirmTransaction": { - "message": "Konfime Tranzaksyon" - }, - "connectHardwareWallet": { - "message": "Konekte Hardware Wallet" - }, - "connect": { - "message": "Konekte" - }, - "connecting": { - "message": "Koneksyon..." - }, - "connectToLedger": { - "message": "Konekte ak Ledger" - }, - "connectToTrezor": { - "message": "Konekte ak Trezor" - }, - "continue": { - "message": "Kontinye" - }, - "continueToCoinbase": { - "message": "Kontinye Coinbase" - }, - "contractDeployment": { - "message": "Kontra Deplwaman" - }, - "conversionProgress": { - "message": "Konvèsyon nan Pwogrè" - }, - "copiedButton": { - "message": "Kopye" - }, - "copiedClipboard": { - "message": "Kopi nan Clipboard" - }, - "copiedExclamation": { - "message": "Kopye!" - }, - "copiedSafe": { - "message": "Mwen te kopye li yon kote ki san danje" - }, - "copy": { - "message": "Kopye" - }, - "copyAddress": { - "message": "Kopi adrès clipboard" - }, - "copyToClipboard": { - "message": "Kopi clipboard" - }, - "copyButton": { - "message": " Kopi " - }, - "copyPrivateKey": { - "message": "Sa a se kle prive ou (klike pou ou kopye)" - }, - "create": { - "message": "Kreye" - }, - "createAccount": { - "message": "Kreye Kont" - }, - "createDen": { - "message": "Kreye" - }, - "crypto": { - "message": "Crypto", - "description": "Change tip (cryptocurrencies)" - }, - "currentConversion": { - "message": "Konvèsyon aktyèl" - }, - "currentNetwork": { - "message": "Rezo aktyèl" - }, - "customGas": { - "message": "Koutim Gaz" - }, - "customToken": { - "message": "Koutim Token" - }, - "customize": { - "message": "Koutim" - }, - "customRPC": { - "message": "Koutim RPC" - }, - "decimalsMustZerotoTen": { - "message": "Desimal yo dwe omwen 0, epi pa dwe plis pase 36." - }, - "decimal": { - "message": "Presizyon desimal la" - }, - "defaultNetwork": { - "message": "Dfo rezo a pou tranzaksyon Ether se Mainnet." - }, - "denExplainer": { - "message": "DEN ou se depo modpas avèk chif ou nan MetaMask." - }, - "deposit": { - "message": "Depo" - }, - "depositBTC": { - "message": "Depoze BTC ou nan adrès ki anba a:" - }, - "depositCoin": { - "message": "Depoze $1 ou nan adrès ki anba a", - "description": "Tells the user what coin they have selected to deposit with shapeshift" - }, - "depositEth": { - "message": "Depo Eth" - }, - "depositEther": { - "message": "Depo Ether" - }, - "depositFiat": { - "message": "Depo ak Fiat" - }, - "depositFromAccount": { - "message": "Depo nan yon lòt kont" - }, - "depositShapeShift": { - "message": "Depo avèk ShapeShift" - }, - "depositShapeShiftExplainer": { - "message": "Si ou posede lòt cryptocurrencies, ou ka chanje ak depo Ether dirèkteman nan Wallet MetaMask ou. Pa gen kont ki nesesè." - }, - "details": { - "message": "Detay yo" - }, - "directDeposit": { - "message": "Depo Dirèk" - }, - "directDepositEther": { - "message": "Dirèkteman Depo Ether" - }, - "directDepositEtherExplainer": { - "message": "Si ou deja gen kèk Ether, fason ki pi rapid yo ka resevwa Ether nan nouvo Wallet ou pa depo dirèk." - }, - "done": { - "message": "Fini" - }, - "downloadGoogleChrome": { - "message": "Telechaje Google Chrome" - }, - "downloadStateLogs": { - "message": "Telechaje State Logs" - }, - "dontHaveAHardwareWallet": { - "message": "Pa gen yon materyèl bous?" - }, - "dropped": { - "message": "Tonbe" - }, - "edit": { - "message": "Korije" - }, - "editAccountName": { - "message": "Korije Non Kont" - }, - "editingTransaction": { - "message": "Fè chanjman nan tranzaksyon ou" - }, - "emailUs": { - "message": "Imèl nou!" - }, - "encryptNewDen": { - "message": "Ankripte nouvo DEN ou" - }, - "ensNameNotFound": { - "message": "Nou pa jwenn non ENS ou a" - }, - "enterPassword": { - "message": "Mete modpas" - }, - "enterPasswordConfirm": { - "message": "Antre nan modpas ou a konfime" - }, - "enterPasswordContinue": { - "message": "Mete modpas pou kontinye" - }, - "parameters": { - "message": "Paramèt" - }, - "passwordNotLongEnough": { - "message": "Modpas la pa ase" - }, - "passwordsDontMatch": { - "message": "Modpas Pa Koresponn ak" - }, - "etherscanView": { - "message": "Gade kont sou Etherscan" - }, - "exchangeRate": { - "message": "Chanje to" - }, - "exportPrivateKey": { - "message": "Voye Kòd Prive" - }, - "exportPrivateKeyWarning": { - "message": "Voye kle prive ak pwòp risk ou." - }, - "failed": { - "message": "Tonbe" - }, - "fiat": { - "message": "FIAT", - "description": "Exchange type" - }, - "fileImportFail": { - "message": "Enpòte dosye ki pa travay? Klike la a!", - "description": "Helps user import their account from a JSON file" - }, - "followTwitter": { - "message": "Swiv nou sou Twitter" - }, - "forgetDevice": { - "message": "Bliye aparèy sa a" - }, - "from": { - "message": "Soti nan" - }, - "fromToSame": { - "message": "Adrès orijinal le ak sa ou resevwa pake menm" - }, - "fromShapeShift": { - "message": "Soti nan ShapeShift" - }, - "functionType": { - "message": "Kalite Fonksyon" - }, - "gas": { - "message": "Gaz", - "description": "Short indication of gas cost" - }, - "gasFee": { - "message": "Frè gaz" - }, - "gasLimit": { - "message": "Limit gaz" - }, - "gasLimitCalculation": { - "message": "Nou kalkile gaz limit sijere a ki baze sou pousantaj siksè rezo a." - }, - "gasLimitRequired": { - "message": "Limit gaz nesesè" - }, - "gasLimitTooLow": { - "message": "Limit gaz dwe omwen 21000" - }, - "generatingSeed": { - "message": "Génération Seed..." - }, - "gasPrice": { - "message": "Pri gaz (GWEI)" - }, - "gasPriceCalculation": { - "message": "Nou kalkile pri yo gaz ki sijere ki baze sou pousantaj siksè rezo." - }, - "gasPriceRequired": { - "message": "Pri Gaz la Egzije" - }, - "generatingTransaction": { - "message": "Tranzaksyon kap fè" - }, - "getEther": { - "message": "Jwenn Ether" - }, - "getEtherFromFaucet": { - "message": "Jwenn Ether nan yon tiyo pou $1 la", - "description": "Displays network name for Ether faucet" - }, - "getHelp": { - "message": "Jwenn èd." - }, - "greaterThanMin": { - "message": "dwe pi gran pase oswa egal a $ 1.", - "description": "helper for inputting hex as decimal input" - }, - "hardware": { - "message": "hardware" - }, - "hardwareWalletConnected": { - "message": "Hardware Wallet konekte" - }, - "hardwareWallets": { - "message": "Hardware Wallet konekte" - }, - "hardwareWalletsMsg": { - "message": "Chwazi yon Hardware Wallet ou ta renmen itilize ak MetaMask" - }, - "havingTroubleConnecting": { - "message": "Èske w gen pwoblèm pou konekte?" - }, - "here": { - "message": "isit la", - "description": "as in -click here- for more information (goes with troubleTokenBalances)" - }, - "hereList": { - "message": "Isit la nan yon lis !!!!" - }, - "hexData": { - "message": "Hex Data" - }, - "hide": { - "message": "Kache" - }, - "hideToken": { - "message": "Kache Token" - }, - "hideTokenPrompt": { - "message": "Kache Token?" - }, - "history": { - "message": "Istwa" - }, - "howToDeposit": { - "message": "Ki jan ou ta renmen depo Ether?" - }, - "holdEther": { - "message": "Li pèmèt ou kenbe ether & tokens, epi sèvi kòm on pon pou desantralize aplikasyon." - }, - "import": { - "message": "Pòte", - "description": "Button to import an account from a selected file" - }, - "importAccount": { - "message": "Pòte Kont" - }, - "importAccountMsg": { - "message": " Kont pòte pa pral asosye avèk orijinal ou te kreye nan kont MetaMask seed fraz. Aprann plis sou kont enpòte " - }, - "importAnAccount": { - "message": "Pòte yon kont" - }, - "importDen": { - "message": "Pòte ki deja egziste DEN" - }, - "imported": { - "message": "Pòte", - "description": "status showing that an account has been fully loaded into the keyring" - }, - "importUsingSeed": { - "message": "Pòte lè sèvi avèk seed fraz" - }, - "infoHelp": { - "message": "Enfo & Èd" - }, - "initialTransactionConfirmed": { - "message": "Premye tranzaksyon ou konfime sou rezo a. Klike sou OK pou tounen." - }, - "insufficientFunds": { - "message": "Lajan ensifizan." - }, - "insufficientTokens": { - "message": "Tokens pa valab." - }, - "invalidAddress": { - "message": "Adrès pa valab" - }, - "invalidAddressRecipient": { - "message": "Moun ki resevwa adrès la pa valab" - }, - "invalidGasParams": { - "message": "Gaz Paramèt la pa valab" - }, - "invalidInput": { - "message": "Sa ou rantre a pa valab" - }, - "invalidRequest": { - "message": "Demann pa valab" - }, - "invalidRPC": { - "message": "RPC URI pa valab" - }, - "invalidSeedPhrase": { - "message": "Seed fraz pa valab" - }, - "jsonFail": { - "message": "Yon bagay ale mal. Tanpri, asire w ke dosye JSON ou an byen fòmate." - }, - "jsonFile": { - "message": "JSON Dosye", - "description": "format for importing an account" - }, - "keepTrackTokens": { - "message": "Gade tokens yo ou te achte ak kont MetaMask ou." - }, - "kovan": { - "message": "Kovan Tès Rezo" - }, - "knowledgeDataBase": { - "message": "Vizite baz nou an" - }, - "max": { - "message": "Maksimòm" - }, - "learnMore": { - "message": "Aprann plis" - }, - "ledgerAccountRestriction": { - "message": "Ou bezwen sèvi ak dènye kont ou anvan ou ka ajoute yon nouvo." - }, - "lessThanMax": { - "message": "dwe mwens pase oswa egal a $ 1.", - "description": "helper for inputting hex as decimal input" - }, - "likeToAddTokens": { - "message": "Èske ou ta renmen ajoute sa nan tokens?" - }, - "links": { - "message": "Lyen" - }, - "limit": { - "message": "Limitasyon" - }, - "loading": { - "message": "Telechaje..." - }, - "loadingTokens": { - "message": "Telechaje Tokens..." - }, - "localhost": { - "message": "Localhost 8545" - }, - "login": { - "message": "Ouvri" - }, - "logout": { - "message": "Dekonekte" - }, - "loose": { - "message": "Pèdi" - }, - "loweCaseWords": { - "message": "seed mo sèlman gen karaktè miniskil" - }, - "mainnet": { - "message": "Main Ethereum Network" - }, - "menu": { - "message": "Opsyon" - }, - "message": { - "message": "Mesaje" - }, - "metamaskDescription": { - "message": "MetaMask sekirize idantite pou Ethereum." - }, - "metamaskSeedWords": { - "message": "MetaMask Seed Mo" - }, - "min": { - "message": "Minimòm" - }, - "myAccounts": { - "message": "Kont mwen" - }, - "mustSelectOne": { - "message": "Ou dwe chwazi omwen 1 token." - }, - "needEtherInWallet": { - "message": "Pou kominike avèk aplikasyon desantralize ou dwe itilize MetaMask, ou pral bezwen Ether nan Wallet ou." - }, - "needImportFile": { - "message": "Ou dwe chwazi yon dosye pou enpòte.", - "description": "User is important an account and needs to add a file to continue" - }, - "needImportPassword": { - "message": "Ou dwe antre nan yon modpas pou dosye ou te chwazi a.", - "description": "Password and file needed to import an account" - }, - "negativeETH": { - "message": "Pa ka voye kantite lajan negatif ETH." - }, - "networks": { - "message": "Rezo" - }, - "nevermind": { - "message": "Pa pwoblèm" - }, - "newAccount": { - "message": "Nouvo Kont" - }, - "newAccountNumberName": { - "message": "Kont $1", - "description": "Default name of next account to be created on create account screen" - }, - "newContract": { - "message": "Nouvo Kontra" - }, - "newPassword": { - "message": "Nouvo modpas (minit 8)" - }, - "newRecipient": { - "message": "Nouvo Benefisyè" - }, - "newRPC": { - "message": "Nouvo RPC URL" - }, - "next": { - "message": "Aprè sa" - }, - "noAddressForName": { - "message": "Pa gen adrès ki etabli pou non sa a." - }, - "noDeposits": { - "message": "Pa gen depo ou te resevwa" - }, - "noConversionRateAvailable": { - "message": "Pa gen okenn Konvèsyon Disponib" - }, - "noTransactionHistory": { - "message": "Pa gen istwa tranzaksyon." - }, - "noTransactions": { - "message": "Pa gen tranzaksyon" - }, - "notFound": { - "message": "Pa jwenn" - }, - "notStarted": { - "message": "Pa kòmanse" - }, - "noWebcamFoundTitle": { - "message": "Pa jwenn webcam" - }, - "noWebcamFound": { - "message": "Nou pakay jwenn webcam òdinatè ou. Tanpri eseye ankò." - }, - "oldUI": { - "message": "Ansyen Itilizatè kouòdone" - }, - "oldUIMessage": { - "message": "Ou te retounen nan Ansyen Itilizatè kouòdone. Ou ka chanje tounen nan nouvo Ansyen Itilizatè nan opsyon a nan meni an tèt la." - }, - "openInTab": { - "message": "Louvri nan etikèt" - }, - "or": { - "message": "oubyen", - "description": "choice between creating or importing a new account" - }, - "origin": { - "message": "Orijin" - }, - "password": { - "message": "Modpas" - }, - "passwordCorrect": { - "message": "Tanpri asire ke modpas ou kòrèk." - }, - "passwordMismatch": { - "message": "modpas sa pa menm", - "description": "in password creation process, the two new password fields did not match" - }, - "passwordShort": { - "message": "modpas pa sifi", - "description": "in password creation process, the password is not long enough to be secure" - }, - "pastePrivateKey": { - "message": "Kole fraz prive ou a la:", - "description": "For importing an account from a private key" - }, - "pasteSeed": { - "message": "Kole seed fraz ou a la!" - }, - "pending": { - "message": "l ap mache" - }, - "personalAddressDetected": { - "message": "Adrès pèsonèl detekte. Antre adrès kontra token la." - }, - "pleaseReviewTransaction": { - "message": "Tanpri revize tranzaksyon ou." - }, - "popularTokens": { - "message": "Popilè Tokens" - }, - "prev": { - "message": "Avan" - }, - "privacyMsg": { - "message": "Règleman sou enfòmasyon prive" - }, - "privateKey": { - "message": "Prive kle", - "description": "select this type of file to use to import an account" - }, - "privateKeyWarning": { - "message": "Atansyon: pa janm divilge kle sa. Nenpòt moun kapab avèk kle prive ou a vòlè sa ou gen ou sou kont ou a." - }, - "privateNetwork": { - "message": "Rezo Prive" - }, - "qrCode": { - "message": "Montre QR Kòd" - }, - "queue": { - "message": "Queue" - }, - "readdToken": { - "message": "Ou ka ajoute token sa aprè sa ankò ou prale nan \"Ajoute token\" nan opsyon meni kont ou an." - }, - "readMore": { - "message": "Li plis isit la." - }, - "readMore2": { - "message": "Li plis isit la." - }, - "receive": { - "message": "Resevwa" - }, - "recipientAddress": { - "message": "Adrès pou resevwa" - }, - "refundAddress": { - "message": "Adrès pou resevwa" - }, - "rejected": { - "message": "Rejte" - }, - "reset": { - "message": "Repwograme" - }, - "resetAccount": { - "message": "Repwograme Kont" - }, - "resetAccountDescription": { - "message": "Repwograme kont a netwaye tranzaksyon ou yo." - }, - "restoreFromSeed": { - "message": "Restore kont?" - }, - "restoreVault": { - "message": "Retabli kazye" - }, - "restoreAccountWithSeed": { - "message": "Retabli kont ou avèk yo Seed Fraz" - }, - "required": { - "message": "Egzije" - }, - "retryWithMoreGas": { - "message": "Reseye ak yon pri gaz pi wo isit la" - }, - "walletSeed": { - "message": "Wallet Seed" - }, - "restore": { - "message": "Retabli" - }, - "revealSeedWords": { - "message": "Revele Seed Mo Yo" - }, - "revealSeedWordsTitle": { - "message": "Seed Fraz" - }, - "revealSeedWordsDescription": { - "message": "Si ou pa janm chanje navigatè ou deplase òdinatè, ou pral bezwen fraz seed la pou ka gen aksè a kont ou. Sere yo on kote an sekirite e an sekrè." - }, - "revealSeedWordsWarningTitle": { - "message": "PA pataje fraz sa a avèk nenpòt moun!" - }, - "revealSeedWordsWarning": { - "message": "Yo ka itilize mo sa pou vòlè kont ou." - }, - "revert": { - "message": "Retounen" - }, - "remove": { - "message": "retire" - }, - "removeAccount": { - "message": "Retire kont" - }, - "removeAccountDescription": { - "message": "Kont sa a pral retire nan Wallet ou. Tanpri, asire ou ke ou gen orijinal fraz seed la oubyen kle prive pou rantre kont lan avan ou kontinye. Oubyen ou ka rantre kont ou ankò apati kont \"drop-down\" ou an." - }, - "readyToConnect": { - "message": "Pare pou konekte?" - }, - "rinkeby": { - "message": "Rinkeby Tès Rezo" - }, - "ropsten": { - "message": "Ropsten Tès Rezo" - }, - "rpc": { - "message": "Koutim RPC" - }, - "currentRpc": { - "message": "Kounya RPC" - }, - "connectingToMainnet": { - "message": "Konekte ak Main (Prensipal) Ethereum Rezo a" - }, - "connectingToRopsten": { - "message": "Konekte ak Ropsten Tès Rezo a" - }, - "connectingToKovan": { - "message": "Konekte nan Kovan Tès Rezo a" - }, - "connectingToRinkeby": { - "message": "Konekte nan Rinkeby Tès Rezo a" - }, - "connectingToUnknown": { - "message": "Konekte nan rezo enkoni" - }, - "sampleAccountName": { - "message": "Pa egzanp, Nouvo kont mwen an", - "description": "Help user understand concept of adding a human-readable name to their account" - }, - "save": { - "message": "Sove" - }, - "speedUp": { - "message": "pi vit" - }, - "speedUpTitle": { - "message": "Monte vitès tranzaksyon" - }, - "speedUpSubtitle": { - "message": "Ogmante pri gaz ou pou eseye efase tranzaksyon ou pi vit" - }, - "saveAsCsvFile": { - "message": "Sove kòm dosye CSV" - }, - "saveAsFile": { - "message": "Sove kòm dosye", - "description": "Account export process" - }, - "saveSeedAsFile": { - "message": "Sove pawòl seed kòm dosye" - }, - "search": { - "message": "Rechèch" - }, - "searchResults": { - "message": "Rezilta rechèch" - }, - "secretPhrase": { - "message": "Antre fraz sekrè douz mo ou a pou w restore kòf ou a." - }, - "showHexData": { - "message": "Montre Hex Data" - }, - "showHexDataDescription": { - "message": "Pran sa pouw ka montre chan entèfas hex data a" - }, - "newPassword8Chars": { - "message": "Nouvo modpas (pou pi pit 8)" - }, - "seedPhraseReq": { - "message": "Seed fraz yo se 12 long mo" - }, - "select": { - "message": "Chwazi" - }, - "selectCurrency": { - "message": "Chwazi Lajan" - }, - "selectService": { - "message": "Chwazi Sèvis" - }, - "selectType": { - "message": "Chwazi Kalite" - }, - "send": { - "message": "Voye" - }, - "sendETH": { - "message": "Voye ETH" - }, - "sendTokens": { - "message": "Voye Tokens" - }, - "sentEther": { - "message": "Voye ether" - }, - "sentTokens": { - "message": "tokens deja voye" - }, - "separateEachWord": { - "message": "Separe chak mo ak yon sèl espas" - }, - "onlySendToEtherAddress": { - "message": "Sèlman voye ETH nan yon adrès Ethereum." - }, - "onlySendTokensToAccountAddress": { - "message": "Sèlman voye $ 1 nan yon adrès kont Ethereum.", - "description": "displays token symbol" - }, - "orderOneHere": { - "message": "Mete nan lòd on Trezor oswa Ledger epi kenbe lajan ou nan yon stòk frèt." - }, - "outgoing": { - "message": "Ap kite" - }, - "searchTokens": { - "message": "Rechèch Tokens" - }, - "selectAnAddress": { - "message": "Chwazi yon adrès" - }, - "selectAnAccount": { - "message": "Chwazi yon kont" - }, - "selectAnAccountHelp": { - "message": "Chwazi kont pou wè nan MetaMask" - }, - "selectHdPath": { - "message": "Chwazi chemen HD" - }, - "selectPathHelp": { - "message": "Si ou pa wè kont Ledger ou te genyen an anba a, eseye chanje chemen an \"Eritaj (MEW / MyCrypto)\"" - }, - "sendTokensAnywhere": { - "message": "Voye Tokens pou nenpòt moun ki gen yon kont Ethereum" - }, - "settings": { - "message": "Paramèt" - }, - "step1HardwareWallet": { - "message": "1. Konekte Materyèl bous" - }, - "step1HardwareWalletMsg": { - "message": "Konekte materyèl bous ou dirèkteman nan òdinatè ou." - }, - "step2HardwareWallet": { - "message": "2. Chwazi yon kont" - }, - "step2HardwareWalletMsg": { - "message": "Chwazi kont ou vle wè a. Ou ka chwazi youn sèlman nan yon moman." - }, - "step3HardwareWallet": { - "message": "3. Kòmanse itilize dApps ak plis ankò!" - }, - "step3HardwareWalletMsg": { - "message": "Sèvi ak kont materyèl ou menm jan ou t ap fè pou kont Etherum. Ouvri sesyon an nan dApps, voye Eth, achte ak stòke ERC20 tokens ak e ki pake chanje tokens tankou CryptoKitties." - }, - "info": { - "message": "Enfòmasyon" - }, - "scanInstructions": { - "message": "Mete kòd QR la devan kamera ou" - }, - "scanQrCode": { - "message": "Enspeksyon QR Kòd" - }, - "shapeshiftBuy": { - "message": "Achte avèk Shapeshift" - }, - "showPrivateKeys": { - "message": "Montre Kle Prive" - }, - "showQRCode": { - "message": "Montre Kòd QR" - }, - "sign": { - "message": "Siyen" - }, - "signatureRequest": { - "message": "Siyati Mande" - }, - "signed": { - "message": "Te Siyen" - }, - "signMessage": { - "message": "Siyen mesaj" - }, - "signNotice": { - "message": "Lè w siyen mesaj sa a ka gen efè segondè ki \ndanjere. Sèlman \nsit mesaj ki soti nan sit ou konplètman fè konfyans ak tout kont ou. \n Metòd danjere sa yo pral retire nan yon vèsyon fiti. " - }, - "sigRequest": { - "message": "Demann Siyati" - }, - "sigRequested": { - "message": "Demann Siyati" - }, - "spaceBetween": { - "message": "ka gen sèlman yon espas ant mo yo" - }, - "status": { - "message": "Kondisyon" - }, - "stateLogs": { - "message": "State Logs" - }, - "stateLogsDescription": { - "message": "State logs gen adrès kont piblik ou yo epi tranzaksyon ou te voye yo." - }, - "stateLogError": { - "message": "Erè nan retwouve State Logs yo." - }, - "submit": { - "message": "Soumèt" - }, - "submitted": { - "message": "Te Soumèt" - }, - "supportCenter": { - "message": "Vizite Sant Sipò Nou" - }, - "symbolBetweenZeroTen": { - "message": "Senbòl yo dwe ant 0 ak 10 karaktè." - }, - "takesTooLong": { - "message": "Pran twò lontan?" - }, - "terms": { - "message": "Tèm pou itilize" - }, - "testFaucet": { - "message": "Tès Tiyo" - }, - "to": { - "message": "Pou" - }, - "toETHviaShapeShift": { - "message": "$1 pou ETH pa ShapeShift", - "description": "system will fill in deposit type in start of message" - }, - "token": { - "message": "Token" - }, - "tokenAddress": { - "message": "Adrès Token" - }, - "tokenAlreadyAdded": { - "message": "Ou te deja ajoute token." - }, - "tokenBalance": { - "message": "Balans Token ou se:" - }, - "tokenSelection": { - "message": "Chache Tokens oswa chwazi nan lis Tokens popilè nou an." - }, - "tokenSymbol": { - "message": "Token Senbòl" - }, - "tokenWarning1": { - "message": "Kenbe tras token yo ou te achte ak kont MetaMask ou. Si ou te achte tokens pandan wap itilize yon kont diferan tokens sa yo pa pral parèt la." - }, - "total": { - "message": "Total" - }, - "transactions": { - "message": "tranzaksyon yo" - }, - "transactionConfirmed": { - "message": "Tranzaksyon ou te konfime pou $2." - }, - "transactionCreated": { - "message": "Tranzaksyon ou te kreye avèk on valè de $1 pou $2." - }, - "transactionDropped": { - "message": "Tranzaksyon ou te tonbe a $2." - }, - "transactionSubmitted": { - "message": "Tranzaksyon ou te soumèt a $2." - }, - "transactionUpdated": { - "message": "Tranzaksyon ou te aktyalize a $2." - }, - "transactionUpdatedGas": { - "message": "Tranzaksyon ou te aktyalize avèk on pri gaz de $1 a $2." - }, - "transactionError": { - "message": "Erè tranzaksyon. Eksepsyon jete nan kòd kontra." - }, - "transactionMemo": { - "message": "Memo tranzaksyon (opsyonèl)" - }, - "transactionNumber": { - "message": "Nimewo Tranzaksyon" - }, - "transfer": { - "message": "Transfè" - }, - "transfers": { - "message": "Transfè yo" - }, - "trezorHardwareWallet": { - "message": "TREZOR Materyèl Bous" - }, - "troubleTokenBalances": { - "message": "Nou te gen pwoblèm chaje balans token ou. Ou ka wè yo ", - "description": "Followed by a link (here) to view token balances" - }, - "tryAgain": { - "message": "Eseye anko" - }, - "twelveWords": { - "message": "12 mo sa yo se sèl fason pou retabli kont MetaMask ou yo. \nKenbe yo yon kote ki an sekirite ak sekrè." - }, - "typePassword": { - "message": "Tape modpas ou" - }, - "uiWelcome": { - "message": "Byenveni nan New itilizatè koòdone (Beta)" - }, - "uiWelcomeMessage": { - "message": "Kounya w ap itilize nouvo MetaMask UI (itilizatè koòdone) a." - }, - "unapproved": { - "message": "Pa apwouve" - }, - "unavailable": { - "message": "Pa disponib" - }, - "units": { - "message": "inite yo" - }, - "unknown": { - "message": "Enkoni" - }, - "unknownFunction": { - "message": "Fonksyon enkoni" - }, - "unknownNetwork": { - "message": "Rezo Prive Enkoni" - }, - "unknownNetworkId": { - "message": "Rezo ID Enkoni" - }, - "unknownQrCode": { - "message": "Erè: Nou pa t kapab idantifye QR kòd sa" - }, - "unknownCameraErrorTitle": { - "message": "Ooops! Yon bagay te ale mal...." - }, - "unknownCameraError": { - "message": "Te gen yon erè pandan y ap eseye jwenn aksè nan kamera ou. Tanpri eseye ankò..." - }, - "unlock": { - "message": "Debloke" - }, - "unlockMessage": { - "message": "Entènèt desantralize a ap tann" - }, - "uriErrorMsg": { - "message": "URIs mande pou apwopriye prefiks HTTP / HTTPS a." - }, - "usaOnly": { - "message": "USA sèlman", - "description": "Using this exchange is limited to people inside the USA" - }, - "usedByClients": { - "message": "Itilize pa yon varyete de kliyan diferan" - }, - "useOldUI": { - "message": "Itilizasyon ansyen UI (itilizatè koòdone)" - }, - "validFileImport": { - "message": "Ou dwe chwazi yon dosye ki valab pou enpòte." - }, - "vaultCreated": { - "message": "Kòf Kreye" - }, - "viewAccount": { - "message": "Wè Kont" - }, - "viewOnEtherscan": { - "message": "Wè sou Etherscan" - }, - "visitWebSite": { - "message": "Vizite sit entènèt nou an" - }, - "warning": { - "message": "Avètisman" - }, - "welcomeBack": { - "message": "Bon retou!" - }, - "welcomeBeta": { - "message": "Byenveni nan MetaMask Beta" - }, - "whatsThis": { - "message": "Kisa sa ye?" - }, - "youNeedToAllowCameraAccess": { - "message": "Ou bezwen bay kamera aksè pou sèvi ak fonksyon sa." - }, - "yourSigRequested": { - "message": "Yo mande siyati ou" - }, - "youSign": { - "message": "Ou ap siyen kounya" - }, - "yourPrivateSeedPhrase": { - "message": "Seed fraz prive ou a" - } + "accept": { + "message": "Aksepte" + }, + "accessingYourCamera": { + "message": "Aksè a Kamera" + }, + "account": { + "message": "Kont" + }, + "accountDetails": { + "message": "Detay Kont" + }, + "accountName": { + "message": "Non Kont" + }, + "accountOptions": { + "message": "Opsyon kont" + }, + "accountSelectionRequired": { + "message": "Ou bezwen chwazi yon kont!" + }, + "activityLog": { + "message": "aktivite ki fèt" + }, + "address": { + "message": "Adrès" + }, + "addCustomToken": { + "message": "Ajoute token" + }, + "addToken": { + "message": "Ajoute Token" + }, + "addTokens": { + "message": "Ajoute Token" + }, + "addSuggestedTokens": { + "message": "Ajoute Token Yo Sikjere W" + }, + "addAcquiredTokens": { + "message": "Ajoute tokens yo ou te achte lè l sèvi avèk MetaMask" + }, + "amount": { + "message": "Kantite lajan" + }, + "amountPlusGas": { + "message": "Kantite lajan + Gaz" + }, + "appDescription": { + "message": "Ekstansyon Navigatè Ethereum", + "description": "The description of the application" + }, + "appName": { + "message": "MetaMask", + "description": "The name of the application" + }, + "approve": { + "message": "Apwouve" + }, + "approved": { + "message": "Apwouve" + }, + "attemptingConnect": { + "message": "Eseye konekte nan blockchain." + }, + "attemptToCancel": { + "message": "Eseye anile?" + }, + "attemptToCancelDescription": { + "message": "Soumèt tantativ sa a pa garanti ke yo pral anile tranzaksyon ou anile. Si tantativ anile an gen siksè, ou pral chaje frè yo tranzaksyon pi wo a." + }, + "attributions": { + "message": "Atribisyon" + }, + "available": { + "message": "Disponib" + }, + "back": { + "message": "Retounen" + }, + "balance": { + "message": "Balans" + }, + "balances": { + "message": "Balans Token" + }, + "balanceIsInsufficientGas": { + "message": "Ensifizan balans pou total gaz aktyèl la" + }, + "beta": { + "message": "BETA" + }, + "betweenMinAndMax": { + "message": "dwe plis pase oswa egal a $ 1 mwens ke oswa egal a $ 2.", + "description": "helper for inputting hex as decimal input" + }, + "blockiesIdenticon": { + "message": "Itilize Blockies Identicon" + }, + "borrowDharma": { + "message": "Prete Avèk Dharma (Beta)" + }, + "browserNotSupported": { + "message": "Navigatè ou a pa sipòte..." + }, + "builtInCalifornia": { + "message": "MetaMask fèt e bati nan California." + }, + "buy": { + "message": "Achte" + }, + "buyCoinbase": { + "message": "Achte sou Coinbase" + }, + "buyCoinbaseExplainer": { + "message": "Coinbase se fason ki pi popilè nan mond lan yo achte ak vann Bitcoin, Ethereum, ak Litecoin." + }, + "bytes": { + "message": "Bytes" + }, + "ok": { + "message": "Oke" + }, + "cancel": { + "message": "Anile" + }, + "cancelAttempt": { + "message": "Teste Anile" + }, + "cancellationGasFee": { + "message": "Anilasyon Gaz Chaj" + }, + "cancelN": { + "message": "Anile tout $ 1 tranzaksyon" + }, + "classicInterface": { + "message": "Sèvi ak fas klasik la" + }, + "clickCopy": { + "message": "Klike sou kopi" + }, + "clickToAdd": { + "message": "Klike sou $ 1 pou ajoute yo nan kont ou" + }, + "close": { + "message": "Fèmen" + }, + "chromeRequiredForHardwareWallets": { + "message": "Ou bezwen sèvi ak MetaMask sou Google Chrome yo nan lòd yo konekte sou Hardware Wallet." + }, + "confirm": { + "message": "Konfime" + }, + "confirmed": { + "message": "Konfime" + }, + "confirmContract": { + "message": "Konfime Kontra" + }, + "confirmPassword": { + "message": "Konfime Modpas" + }, + "confirmTransaction": { + "message": "Konfime Tranzaksyon" + }, + "connectHardwareWallet": { + "message": "Konekte Materyèl Wallet" + }, + "connect": { + "message": "Konekte" + }, + "connecting": { + "message": "Koneksyon..." + }, + "connectingToKovan": { + "message": "Konekte nan Kovan Tès Rezo a" + }, + "connectingToMainnet": { + "message": "Konekte ak Prensipal Ethereum Rezo a" + }, + "connectingToRopsten": { + "message": "Konekte ak Ropsten Tès Rezo a" + }, + "connectingToRinkeby": { + "message": "Konekte nan Rinkeby Tès Rezo a" + }, + "connectingToUnknown": { + "message": "Konekte nan rezo enkoni" + }, + "connectToLedger": { + "message": "Konekte ak Ledger" + }, + "connectToTrezor": { + "message": "Konekte ak Trezor" + }, + "continue": { + "message": "Kontinye" + }, + "continueToCoinbase": { + "message": "Kontinye Coinbase" + }, + "contractDeployment": { + "message": "Kontra Deplwaman" + }, + "conversionProgress": { + "message": "Konvèsyon nan Pwogrè" + }, + "copiedButton": { + "message": "Kopye" + }, + "copiedClipboard": { + "message": "Kopi nan Clipboard" + }, + "copiedExclamation": { + "message": "Kopye!" + }, + "copiedSafe": { + "message": "Mwen te kopye li yon kote ki san danje" + }, + "copy": { + "message": "Kopye" + }, + "copyAddress": { + "message": "Kopi adrès clipboard" + }, + "copyToClipboard": { + "message": "Kopi clipboard" + }, + "copyButton": { + "message": " Kopi " + }, + "copyPrivateKey": { + "message": "Sa a se kle prive ou (klike pou ou kopye)" + }, + "create": { + "message": "Kreye" + }, + "createAccount": { + "message": "Kreye Kont" + }, + "createDen": { + "message": "Kreye" + }, + "crypto": { + "message": "Crypto", + "description": "Exchange type (cryptocurrencies)" + }, + "currentConversion": { + "message": "Konvèsyon aktyèl" + }, + "currentLanguage": { + "message": "Lang Aktyèl" + }, + "currentNetwork": { + "message": "Rezo aktyèl" + }, + "currentRpc": { + "message": "Aktyèl RPC" + }, + "customGas": { + "message": "Koutim Gaz" + }, + "customToken": { + "message": "Koutim Token" + }, + "customize": { + "message": "Koutim" + }, + "customRPC": { + "message": "Koutim RPC" + }, + "decimalsMustZerotoTen": { + "message": "Desimal yo dwe omwen 0, epi pa dwe plis pase 36." + }, + "decimal": { + "message": "Presizyon desimal la" + }, + "defaultNetwork": { + "message": "Dfo rezo a pou tranzaksyon Ether se Mainnet." + }, + "denExplainer": { + "message": "DEN ou se depo modpas avèk chif ou nan MetaMask." + }, + "deposit": { + "message": "Depo" + }, + "depositBTC": { + "message": "Depoze BTC ou nan adrès ki anba a:" + }, + "depositCoin": { + "message": "Depoze $1 ou nan adrès ki anba a", + "description": "Tells the user what coin they have selected to deposit with shapeshift" + }, + "depositEth": { + "message": "Depo Eth" + }, + "depositEther": { + "message": "Depo Ether" + }, + "depositFiat": { + "message": "Depo ak Fiat" + }, + "depositFromAccount": { + "message": "Depo nan yon lòt kont" + }, + "depositShapeShift": { + "message": "Depo avèk ShapeShift" + }, + "depositShapeShiftExplainer": { + "message": "Si ou posede lòt cryptocurrencies, ou ka chanje ak depo Ether dirèkteman nan Wallet MetaMask ou. Pa gen kont ki nesesè." + }, + "details": { + "message": "Detay yo" + }, + "directDeposit": { + "message": "Depo Dirèk" + }, + "directDepositEther": { + "message": "Dirèkteman Depo Ether" + }, + "directDepositEtherExplainer": { + "message": "Si ou deja gen kèk Ether, fason ki pi rapid yo ka resevwa Ether nan nouvo Wallet ou pa depo dirèk." + }, + "done": { + "message": "Fini" + }, + "downloadGoogleChrome": { + "message": "Telechaje Google Chrome" + }, + "downloadStateLogs": { + "message": "Telechaje State Logs" + }, + "dontHaveAHardwareWallet": { + "message": "Pa gen yon materyèl bous?" + }, + "dropped": { + "message": "Tonbe" + }, + "edit": { + "message": "Korije" + }, + "editAccountName": { + "message": "Korije Non Kont" + }, + "editingTransaction": { + "message": "Fè chanjman nan tranzaksyon ou" + }, + "emailUs": { + "message": "Imèl nou!" + }, + "encryptNewDen": { + "message": "Ankripte nouvo DEN ou" + }, + "ensNameNotFound": { + "message": "Nou pa jwenn non ENS ou a" + }, + "enterPassword": { + "message": "Mete modpas" + }, + "enterPasswordConfirm": { + "message": "Antre nan modpas ou a konfime" + }, + "enterPasswordContinue": { + "message": "Mete modpas pou kontinye" + }, + "eth": { + "message": "ETH" + }, + "etherscanView": { + "message": "Gade kont sou Etherscan" + }, + "exchangeRate": { + "message": "Chanje to" + }, + "expandView": { + "message": "Elaji Wè" + }, + "exportPrivateKey": { + "message": "Voye Kòd Prive" + }, + "exportPrivateKeyWarning": { + "message": "Voye kle prive ak pwòp risk ou." + }, + "failed": { + "message": "Tonbe" + }, + "fiat": { + "message": "FIAT", + "description": "Exchange type" + }, + "fileImportFail": { + "message": "Enpòte dosye ki pa travay? Klike la a!", + "description": "Helps user import their account from a JSON file" + }, + "followTwitter": { + "message": "Swiv nou sou Twitter" + }, + "forgetDevice": { + "message": "Bliye aparèy sa a" + }, + "from": { + "message": "Soti nan" + }, + "fromToSame": { + "message": "Adrès orijinal le ak sa ou resevwa pake menm" + }, + "fromShapeShift": { + "message": "Soti nan ShapeShift" + }, + "functionType": { + "message": "Kalite Fonksyon" + }, + "gas": { + "message": "Gaz", + "description": "Short indication of gas cost" + }, + "gasFee": { + "message": "Frè gaz" + }, + "gasLimit": { + "message": "Limit gaz" + }, + "gasLimitCalculation": { + "message": "Nou kalkile gaz limit sijere a ki baze sou pousantaj siksè rezo a." + }, + "gasLimitRequired": { + "message": "Limit gaz nesesè" + }, + "gasLimitTooLow": { + "message": "Limit gaz dwe omwen 21000" + }, + "gasUsed": { + "message": "Gaz yo Itilize" + }, + "generatingSeed": { + "message": "Grenn jenerasyon..." + }, + "gasPrice": { + "message": "Pri gaz (GWEI)" + }, + "gasPriceCalculation": { + "message": "Nou kalkile pri yo gaz ki sijere ki baze sou pousantaj siksè rezo." + }, + "gasPriceRequired": { + "message": "Pri Gaz la Egzije" + }, + "generatingTransaction": { + "message": "Tranzaksyon kap fè" + }, + "getEther": { + "message": "Jwenn Ether" + }, + "getEtherFromFaucet": { + "message": "Jwenn Ether nan yon tiyo pou $1 la", + "description": "Displays network name for Ether faucet" + }, + "getHelp": { + "message": "Jwenn èd." + }, + "greaterThanMin": { + "message": "dwe pi gran pase oswa egal a $ 1.", + "description": "helper for inputting hex as decimal input" + }, + "hardware": { + "message": "materyèl" + }, + "hardwareWalletConnected": { + "message": "Materyèl Wallet konekte" + }, + "hardwareWallets": { + "message": "Materyèl Wallet konekte" + }, + "hardwareWalletsMsg": { + "message": "Chwazi yon Materyèl Wallet ou ta renmen itilize ak MetaMask" + }, + "havingTroubleConnecting": { + "message": "Èske w gen pwoblèm pou konekte?" + }, + "here": { + "message": "isit la", + "description": "as in -click here- for more information (goes with troubleTokenBalances)" + }, + "hereList": { + "message": "Isit la nan yon lis !!!!" + }, + "hexData": { + "message": "Hex Data" + }, + "hide": { + "message": "Kache" + }, + "hideToken": { + "message": "Kache Token" + }, + "hideTokenPrompt": { + "message": "Kache Token?" + }, + "history": { + "message": "Istwa" + }, + "howToDeposit": { + "message": "Ki jan ou ta renmen depo Ether?" + }, + "holdEther": { + "message": "Li pèmèt ou kenbe ether & tokens, epi sèvi kòm on pon pou desantralize aplikasyon." + }, + "import": { + "message": "Pòte", + "description": "Button to import an account from a selected file" + }, + "importAccount": { + "message": "Pòte Kont" + }, + "importAccountMsg": { + "message": " Kont pòte pa pral asosye avèk orijinal ou te kreye nan kont MetaMask seed fraz. Aprann plis sou kont enpòte " + }, + "importAnAccount": { + "message": "Pòte yon kont" + }, + "importDen": { + "message": "Pòte ki deja egziste DEN" + }, + "imported": { + "message": "Pòte", + "description": "status showing that an account has been fully loaded into the keyring" + }, + "importUsingSeed": { + "message": "Pòte lè sèvi avèk seed fraz" + }, + "info": { + "message": "Enfo" + }, + "infoHelp": { + "message": "Enfo & Èd" + }, + "initialTransactionConfirmed": { + "message": "Premye tranzaksyon ou konfime sou rezo a. Klike sou OK pou tounen." + }, + "insufficientFunds": { + "message": "Lajan ensifizan." + }, + "insufficientTokens": { + "message": "Tokens pa valab." + }, + "invalidAddress": { + "message": "Adrès pa valab" + }, + "invalidAddressRecipient": { + "message": "Moun ki resevwa adrès la pa valab" + }, + "invalidGasParams": { + "message": "Gaz Paramèt la pa valab" + }, + "invalidInput": { + "message": "Sa ou rantre a pa valab" + }, + "invalidRequest": { + "message": "Demann pa valab" + }, + "invalidRPC": { + "message": "RPC URI pa valab" + }, + "invalidSeedPhrase": { + "message": "Seed fraz pa valab" + }, + "jsonFail": { + "message": "Yon bagay ale mal. Tanpri, asire w ke dosye JSON ou an byen fòmate." + }, + "jsonFile": { + "message": "JSON Dosye", + "description": "format for importing an account" + }, + "keepTrackTokens": { + "message": "Gade tokens yo ou te achte ak kont MetaMask ou." + }, + "kovan": { + "message": "Kovan Tès Rezo" + }, + "knowledgeDataBase": { + "message": "Vizite baz nou an" + }, + "max": { + "message": "Maksimòm" + }, + "learnMore": { + "message": "Aprann plis" + }, + "ledgerAccountRestriction": { + "message": "Ou bezwen sèvi ak dènye kont ou anvan ou ka ajoute yon nouvo." + }, + "lessThanMax": { + "message": "dwe mwens pase oswa egal a $ 1.", + "description": "helper for inputting hex as decimal input" + }, + "likeToAddTokens": { + "message": "Èske ou ta renmen ajoute sa nan tokens?" + }, + "links": { + "message": "Lyen" + }, + "limit": { + "message": "Limitasyon" + }, + "loading": { + "message": "Telechaje..." + }, + "loadingTokens": { + "message": "Telechaje Tokens..." + }, + "localhost": { + "message": "Localhost 8545" + }, + "login": { + "message": "Ouvri" + }, + "logout": { + "message": "Dekonekte" + }, + "loose": { + "message": "Pèdi" + }, + "loweCaseWords": { + "message": "seed mo sèlman gen karaktè miniskil" + }, + "mainnet": { + "message": "Prensipal Ethereum Rezo a" + }, + "menu": { + "message": "Opsyon" + }, + "message": { + "message": "Mesaje" + }, + "metamaskDescription": { + "message": "MetaMask sekirize idantite pou Ethereum." + }, + "metamaskSeedWords": { + "message": "MetaMask Seed Mo" + }, + "metamaskVersion": { + "message": "MetaMask Vèsyon" + }, + "min": { + "message": "Minimòm" + }, + "missingYourTokens": { + "message": "Ou pa wè token ou a?" + }, + "myAccounts": { + "message": "Kont mwen" + }, + "mustSelectOne": { + "message": "Ou dwe chwazi omwen 1 token." + }, + "needEtherInWallet": { + "message": "Pou kominike avèk aplikasyon desantralize ou dwe itilize MetaMask, ou pral bezwen Ether nan Wallet ou." + }, + "needImportFile": { + "message": "Ou dwe chwazi yon dosye pou enpòte.", + "description": "User is important an account and needs to add a file to continue" + }, + "needImportPassword": { + "message": "Ou dwe antre nan yon modpas pou dosye ou te chwazi a.", + "description": "Password and file needed to import an account" + }, + "negativeETH": { + "message": "Pa ka voye kantite lajan negatif ETH." + }, + "networks": { + "message": "Rezo" + }, + "nevermind": { + "message": "Pa pwoblèm" + }, + "newAccount": { + "message": "Nouvo Kont" + }, + "newAccountNumberName": { + "message": "Kont $1", + "description": "Default name of next account to be created on create account screen" + }, + "newContract": { + "message": "Nouvo Kontra" + }, + "newPassword": { + "message": "Nouvo modpas (minit 8)" + }, + "newPassword8Chars": { + "message": "Nouvo modpas (minit 8)" + }, + "newRecipient": { + "message": "Nouvo Benefisyè" + }, + "newRPC": { + "message": "Nouvo RPC URL" + }, + "next": { + "message": "Aprè sa" + }, + "noAddressForName": { + "message": "Pa gen adrès ki etabli pou non sa a." + }, + "noDeposits": { + "message": "Pa gen depo ou te resevwa" + }, + "noConversionRateAvailable": { + "message": "Pa gen okenn Konvèsyon Disponib" + }, + "noTransactionHistory": { + "message": "Pa gen istwa tranzaksyon." + }, + "noTransactions": { + "message": "Pa gen tranzaksyon" + }, + "notFound": { + "message": "Pa jwenn" + }, + "notStarted": { + "message": "Pa kòmanse" + }, + "noWebcamFoundTitle": { + "message": "Pa jwenn webcam" + }, + "noWebcamFound": { + "message": "Nou pakay jwenn webcam òdinatè ou. Tanpri eseye ankò." + }, + "oldUI": { + "message": "Ansyen Itilizatè kouòdone" + }, + "oldUIMessage": { + "message": "Ou te retounen nan Ansyen Itilizatè kouòdone. Ou ka chanje tounen nan nouvo Ansyen Itilizatè nan opsyon a nan meni an tèt la." + }, + "onlySendToEtherAddress": { + "message": "Sèlman voye ETH nan yon adrès Ethereum." + }, + "onlySendTokensToAccountAddress": { + "message": "Sèlman voye $ 1 nan yon adrès kont Ethereum.", + "description": "displays token symbol" + }, + "openInTab": { + "message": "Louvri nan etikèt" + }, + "or": { + "message": "oubyen", + "description": "choice between creating or importing a new account" + }, + "orderOneHere": { + "message": "Mete nan lòd on Trezor oswa Ledger epi kenbe lajan ou nan yon stòk frèt." + }, + "origin": { + "message": "Orijin" + }, + "outgoing": { + "message": "Ap kite" + }, + "parameters": { + "message": "Paramèt" + }, + "password": { + "message": "Modpas" + }, + "passwordCorrect": { + "message": "Tanpri asire ke modpas ou kòrèk." + }, + "passwordsDontMatch": { + "message": "Modpas pa matche" + }, + "passwordMismatch": { + "message": "modpas sa pa menm", + "description": "in password creation process, the two new password fields did not match" + }, + "passwordNotLongEnough": { + "message": "Modpas pa lontan ase" + }, + "passwordShort": { + "message": "modpas pa sifi", + "description": "in password creation process, the password is not long enough to be secure" + }, + "pastePrivateKey": { + "message": "Kole fraz prive ou a la:", + "description": "For importing an account from a private key" + }, + "pasteSeed": { + "message": "Kole seed fraz ou a la!" + }, + "pending": { + "message": "l ap mache" + }, + "personalAddressDetected": { + "message": "Adrès pèsonèl detekte. Antre adrès kontra token la." + }, + "pleaseReviewTransaction": { + "message": "Tanpri revize tranzaksyon ou." + }, + "popularTokens": { + "message": "Popilè Tokens" + }, + "prev": { + "message": "Avan" + }, + "primaryCurrencySetting": { + "message": "Lajan ou itilize pi plis la" + }, + "primaryCurrencySettingDescription": { + "message": "Chwazi ETH pou bay priyorite montre valè nan ETH. Chwazi Fiat priyorite montre valè nan lajan ou chwazi a." + }, + "privacyMsg": { + "message": "Règleman sou enfòmasyon prive" + }, + "privateKey": { + "message": "Prive kle", + "description": "select this type of file to use to import an account" + }, + "privateKeyWarning": { + "message": "Atansyon: pa janm divilge kle sa. Nenpòt moun kapab avèk kle prive ou a vòlè sa ou gen ou sou kont ou a." + }, + "privateNetwork": { + "message": "Rezo Prive" + }, + "qrCode": { + "message": "Montre QR Kòd" + }, + "queue": { + "message": "Queue" + }, + "readdToken": { + "message": "Ou ka ajoute token sa aprè sa ankò ou prale nan \"Ajoute token\" nan opsyon meni kont ou an." + }, + "readMore": { + "message": "Li plis isit la." + }, + "readMore2": { + "message": "Li plis isit la." + }, + "receive": { + "message": "Resevwa" + }, + "recipientAddress": { + "message": "Adrès pou resevwa" + }, + "refundAddress": { + "message": "Adrès pou resevwa" + }, + "reject": { + "message": "Rejte" + }, + "rejectAll": { + "message": "Rejte Tout" + }, + "rejectTxsN": { + "message": "Rejete $ 1 tranzaksyon" + }, + "rejectTxsDescription": { + "message": "Ou se sou rejte $ 1 yon anpil nan tranzaksyon yo." + }, + "rejected": { + "message": "Rejete" + }, + "reset": { + "message": "Repwograme" + }, + "resetAccount": { + "message": "Repwograme Kont" + }, + "resetAccountDescription": { + "message": "Repwograme kont a netwaye tranzaksyon ou yo." + }, + "restoreFromSeed": { + "message": "Restore kont?" + }, + "restoreVault": { + "message": "Retabli kazye" + }, + "restoreAccountWithSeed": { + "message": "Retabli kont ou avèk yo Seed Fraz" + }, + "required": { + "message": "Egzije" + }, + "retryWithMoreGas": { + "message": "Reseye ak yon pri gaz pi wo isit la" + }, + "restore": { + "message": "Retabli" + }, + "revealSeedWords": { + "message": "Revele Seed Mo Yo" + }, + "revealSeedWordsTitle": { + "message": "Seed Fraz" + }, + "revealSeedWordsDescription": { + "message": "Si ou pa janm chanje navigatè ou deplase òdinatè, ou pral bezwen fraz seed la pou ka gen aksè a kont ou. Sere yo on kote an sekirite e an sekrè." + }, + "revealSeedWordsWarningTitle": { + "message": "PA pataje fraz sa a avèk nenpòt moun!" + }, + "revealSeedWordsWarning": { + "message": "Yo ka itilize mo sa pou vòlè kont ou." + }, + "revert": { + "message": "Retounen" + }, + "remove": { + "message": "retire" + }, + "removeAccount": { + "message": "Retire kont" + }, + "removeAccountDescription": { + "message": "Kont sa a pral retire nan Wallet ou. Tanpri, asire ou ke ou gen orijinal fraz seed la oubyen kle prive pou rantre kont lan avan ou kontinye. Oubyen ou ka rantre kont ou ankò apati kont \"drop-down\" ou an." + }, + "readyToConnect": { + "message": "Pare pou konekte?" + }, + "rinkeby": { + "message": "Rinkeby Tès Rezo" + }, + "ropsten": { + "message": "Ropsten Tès Rezo" + }, + "rpc": { + "message": "Koutim RPC" + }, + "sampleAccountName": { + "message": "Pa egzanp, Nouvo kont mwen an", + "description": "Help user understand concept of adding a human-readable name to their account" + }, + "save": { + "message": "Sove" + }, + "saveAsCsvFile": { + "message": "Sove kòm dosye CSV" + }, + "saveAsFile": { + "message": "Sove kòm dosye", + "description": "Account export process" + }, + "saveSeedAsFile": { + "message": "Sove pawòl seed kòm dosye" + }, + "scanInstructions": { + "message": "Mete kòd QR la devan kamera ou" + }, + "scanQrCode": { + "message": "Enspeksyon QR Kòd" + }, + "search": { + "message": "Rechèch" + }, + "searchResults": { + "message": "Rezilta rechèch" + }, + "secretPhrase": { + "message": "Antre fraz sekrè douz mo ou a pou w restore kòf ou a." + }, + "seedPhraseReq": { + "message": "Seed fraz yo se 12 long mo" + }, + "select": { + "message": "Chwazi" + }, + "selectCurrency": { + "message": "Chwazi Lajan" + }, + "selectLocale": { + "message": "Chwazi Lokasyon" + }, + "selectService": { + "message": "Chwazi Sèvis" + }, + "selectType": { + "message": "Chwazi Kalite" + }, + "send": { + "message": "Voye" + }, + "sendETH": { + "message": "Voye ETH" + }, + "sendTokens": { + "message": "Voye Tokens" + }, + "sentEther": { + "message": "Voye ether" + }, + "sentTokens": { + "message": "tokens deja voye" + }, + "separateEachWord": { + "message": "Separe chak mo ak yon sèl espas" + }, + "searchTokens": { + "message": "Rechèch Tokens" + }, + "selectAnAddress": { + "message": "Chwazi yon adrès" + }, + "selectAnAccount": { + "message": "Chwazi yon kont" + }, + "selectAnAccountHelp": { + "message": "Chwazi kont pou wè nan MetaMask" + }, + "selectHdPath": { + "message": "Chwazi chemen HD" + }, + "selectPathHelp": { + "message": "Si ou pa wè kont Ledger ou te genyen an anba a, eseye chanje chemen an \"Eritaj (MEW / MyCrypto)\"" + }, + "sendTokensAnywhere": { + "message": "Voye Tokens pou nenpòt moun ki gen yon kont Ethereum" + }, + "settings": { + "message": "Paramèt" + }, + "shapeshiftBuy": { + "message": "Achte avèk Shapeshift" + }, + "showPrivateKeys": { + "message": "Montre Kle Prive" + }, + "showQRCode": { + "message": "Montre Kòd QR" + }, + "showHexData": { + "message": "Montre Hex Data" + }, + "showHexDataDescription": { + "message": "Pran sa pouw ka montre chan entèfas hex data a" + }, + "sign": { + "message": "Siyen" + }, + "signatureRequest": { + "message": "Siyati Mande" + }, + "signed": { + "message": "Te Siyen" + }, + "signMessage": { + "message": "Siyen mesaj" + }, + "signNotice": { + "message": "Lè w siyen mesaj sa a ka gen efè segondè ki \ndanjere. Sèlman \nsit mesaj ki soti nan sit ou konplètman fè konfyans ak tout kont ou. \n Metòd danjere sa yo pral retire nan yon vèsyon fiti. " + }, + "sigRequest": { + "message": "Demann Siyati" + }, + "sigRequested": { + "message": "Demann Siyati" + }, + "spaceBetween": { + "message": "ka gen sèlman yon espas ant mo yo" + }, + "speedUp": { + "message": "pi vit" + }, + "speedUpTitle": { + "message": "Monte vitès tranzaksyon" + }, + "speedUpSubtitle": { + "message": "Ogmante pri gaz ou pou eseye efase tranzaksyon ou pi vit" + }, + "status": { + "message": "Kondisyon" + }, + "stateLogs": { + "message": "State Logs" + }, + "stateLogsDescription": { + "message": "State logs gen adrès kont piblik ou yo epi tranzaksyon ou te voye yo." + }, + "stateLogError": { + "message": "Erè nan retwouve State Logs yo." + }, + "step1HardwareWallet": { + "message": "1. Konekte Materyèl bous" + }, + "step1HardwareWalletMsg": { + "message": "Konekte materyèl bous ou dirèkteman nan òdinatè ou." + }, + "step2HardwareWallet": { + "message": "2. Chwazi yon kont" + }, + "step2HardwareWalletMsg": { + "message": "Chwazi kont ou vle wè a. Ou ka chwazi youn sèlman nan yon moman." + }, + "step3HardwareWallet": { + "message": "3. Kòmanse itilize dApps ak plis ankò!" + }, + "step3HardwareWalletMsg": { + "message": "Sèvi ak kont materyèl ou menm jan ou t ap fè pou kont Etherum. Ouvri sesyon an nan dApps, voye Eth, achte ak stòke ERC20 tokens ak e ki pake chanje tokens tankou CryptoKitties." + }, + "submit": { + "message": "Soumèt" + }, + "submitted": { + "message": "Te Soumèt" + }, + "supportCenter": { + "message": "Vizite Sant Sipò Nou" + }, + "symbolBetweenZeroTen": { + "message": "Senbòl yo dwe ant 0 ak 10 karaktè." + }, + "takesTooLong": { + "message": "Pran twò lontan?" + }, + "terms": { + "message": "Tèm pou itilize" + }, + "testFaucet": { + "message": "Tès Tiyo" + }, + "to": { + "message": "Pou" + }, + "toETHviaShapeShift": { + "message": "$1 pou ETH pa ShapeShift", + "description": "system will fill in deposit type in start of message" + }, + "token": { + "message": "Token" + }, + "tokenAddress": { + "message": "Adrès Token" + }, + "tokenAlreadyAdded": { + "message": "Ou te deja ajoute token." + }, + "tokenBalance": { + "message": "Balans Token ou se:" + }, + "tokenSelection": { + "message": "Chache Tokens oswa chwazi nan lis Tokens popilè nou an." + }, + "tokenSymbol": { + "message": "Token Senbòl" + }, + "tokenWarning1": { + "message": "Kenbe tras token yo ou te achte ak kont MetaMask ou. Si ou te achte tokens pandan wap itilize yon kont diferan tokens sa yo pa pral parèt la." + }, + "total": { + "message": "Total" + }, + "transaction": { + "message": "tranzaksyon yo" + }, + "transactionConfirmed": { + "message": "Tranzaksyon ou te konfime pou $2." + }, + "transactionCreated": { + "message": "Tranzaksyon ou te kreye avèk on valè de $1 pou $2." + }, + "transactionWithNonce": { + "message": "Tranzaksyon $1" + }, + "transactionDropped": { + "message": "Tranzaksyon ou te tonbe a $2." + }, + "transactionSubmitted": { + "message": "Tranzaksyon ou te soumèt a $2." + }, + "transactionUpdated": { + "message": "Tranzaksyon ou te aktyalize a $2." + }, + "transactionUpdatedGas": { + "message": "Tranzaksyon ou te aktyalize avèk on pri gaz de $1 a $2." + }, + "transactions": { + "message": "transactions" + }, + "transactionError": { + "message": "Erè tranzaksyon. Eksepsyon jete nan kòd kontra." + }, + "transactionMemo": { + "message": "Memo tranzaksyon (opsyonèl)" + }, + "transactionNumber": { + "message": "Nimewo Tranzaksyon" + }, + "transfer": { + "message": "Transfè" + }, + "transferFrom": { + "message": "Transfer From" + }, + "transfers": { + "message": "Transfè yo" + }, + "trezorHardwareWallet": { + "message": "TREZOR Materyèl Bous" + }, + "troubleTokenBalances": { + "message": "Nou te gen pwoblèm chaje balans token ou. Ou ka wè yo ", + "description": "Followed by a link (here) to view token balances" + }, + "tryAgain": { + "message": "Eseye anko" + }, + "twelveWords": { + "message": "12 mo sa yo se sèl fason pou retabli kont MetaMask ou yo. \nKenbe yo yon kote ki an sekirite ak sekrè." + }, + "typePassword": { + "message": "Tape modpas ou" + }, + "uiWelcome": { + "message": "Byenveni nan New itilizatè koòdone (Beta)" + }, + "uiWelcomeMessage": { + "message": "Kounya w ap itilize nouvo MetaMask UI (itilizatè koòdone) a." + }, + "unapproved": { + "message": "Pa apwouve" + }, + "unavailable": { + "message": "Pa disponib" + }, + "units": { + "message": "inite yo" + }, + "unknown": { + "message": "Enkoni" + }, + "unknownFunction": { + "message": "Fonksyon enkoni" + }, + "unknownNetwork": { + "message": "Rezo Prive Enkoni" + }, + "unknownNetworkId": { + "message": "Rezo ID Enkoni" + }, + "unknownQrCode": { + "message": "Erè: Nou pa t kapab idantifye QR kòd sa" + }, + "unknownCameraErrorTitle": { + "message": "Ooops! Yon bagay te ale mal...." + }, + "unknownCameraError": { + "message": "Te gen yon erè pandan y ap eseye jwenn aksè nan kamera ou. Tanpri eseye ankò..." + }, + "unlock": { + "message": "Debloke" + }, + "unlockMessage": { + "message": "Entènèt desantralize a ap tann" + }, + "updatedWithDate": { + "message": "Mete ajou $1" + }, + "uriErrorMsg": { + "message": "URIs mande pou apwopriye prefiks HTTP / HTTPS a." + }, + "usaOnly": { + "message": "USA sèlman", + "description": "Using this exchange is limited to people inside the USA" + }, + "usedByClients": { + "message": "Itilize pa yon varyete de kliyan diferan" + }, + "useOldUI": { + "message": "Itilizasyon ansyen UI (itilizatè koòdone)" + }, + "validFileImport": { + "message": "Ou dwe chwazi yon dosye ki valab pou enpòte." + }, + "vaultCreated": { + "message": "Kòf Kreye" + }, + "viewAccount": { + "message": "Wè Kont" + }, + "viewOnEtherscan": { + "message": "Wè sou Etherscan" + }, + "visitWebSite": { + "message": "Vizite sit entènèt nou an" + }, + "walletSeed": { + "message": "Bous Seed" + }, + "warning": { + "message": "Avètisman" + }, + "welcomeBack": { + "message": "Bon Retou!" + }, + "welcomeBeta": { + "message": "Byenveni nan MetaMask Beta" + }, + "whatsThis": { + "message": "Kisa sa ye?" + }, + "yesLetsTry": { + "message": "Wi, ann eseye" + }, + "youNeedToAllowCameraAccess": { + "message": "Ou bezwen bay kamera aksè pou sèvi ak fonksyon sa." + }, + "yourSigRequested": { + "message": "Yo mande siyati ou" + }, + "youSign": { + "message": "Ou ap siyen kounya" + }, + "yourPrivateSeedPhrase": { + "message": "Seed fraz prive ou a" + } } diff --git a/app/_locales/index.json b/app/_locales/index.json index 46933dc3f..234215e39 100644 --- a/app/_locales/index.json +++ b/app/_locales/index.json @@ -1,24 +1,24 @@ [ - { "code": "cs", "name": "Czech" }, - { "code": "de", "name": "German" }, + { "code": "cs", "name": "Čeština" }, + { "code": "de", "name": "Deutsche" }, { "code": "en", "name": "English" }, - { "code": "es", "name": "Spanish" }, - { "code": "fr", "name": "French" }, - { "code": "ht", "name": "Haitian Creole" }, - { "code": "hn", "name": "Hindi" }, - { "code": "it", "name": "Italian" }, - { "code": "ja", "name": "Japanese" }, - { "code": "ko", "name": "Korean" }, - { "code": "nl", "name": "Dutch" }, - { "code": "ph", "name": "Tagalog" }, - { "code": "pl", "name": "Polish" }, - { "code": "pt", "name": "Portuguese" }, - { "code": "ru", "name": "Russian" }, - { "code": "sl", "name": "Slovenian" }, - { "code": "th", "name": "Thai" }, - { "code": "tml", "name": "Tamil" }, - { "code": "tr", "name": "Turkish" }, - { "code": "vi", "name": "Vietnamese" }, - { "code": "zh_CN", "name": "Chinese (Simplified)" }, - { "code": "zh_TW", "name": "Chinese (Traditional)" } + { "code": "es", "name": "Español" }, + { "code": "fr", "name": "Français" }, + { "code": "ht", "name": "Kreyòl ayisyen" }, + { "code": "hn", "name": "हिन्दी" }, + { "code": "it", "name": "Italiano" }, + { "code": "ja", "name": "日本語" }, + { "code": "ko", "name": "한국어" }, + { "code": "nl", "name": "Nederlands" }, + { "code": "ph", "name": "Pilipino" }, + { "code": "pl", "name": "Polskie" }, + { "code": "pt", "name": "Português" }, + { "code": "ru", "name": "Русский" }, + { "code": "sl", "name": "Slovenščina" }, + { "code": "th", "name": "ไทย" }, + { "code": "tml", "name": "தமிழ்" }, + { "code": "tr", "name": "Türkçe" }, + { "code": "vi", "name": "Tiếng Việt" }, + { "code": "zh_CN", "name": "中文(简体)" }, + { "code": "zh_TW", "name": "中文(繁體)" } ] diff --git a/app/_locales/nl/messages.json b/app/_locales/nl/messages.json index e6d09c123..fc3450290 100644 --- a/app/_locales/nl/messages.json +++ b/app/_locales/nl/messages.json @@ -435,7 +435,7 @@ "message": "back-up woorden hebben alleen kleine letters" }, "mainnet": { - "message": "belangrijkste Ethereum-netwerk" + "message": "Main Netwerk" }, "message": { "message": "Bericht" diff --git a/app/loading.html b/app/loading.html index aef5d9607..71403a5ac 100644 --- a/app/loading.html +++ b/app/loading.html @@ -1,5 +1,9 @@ -<html> +<!DOCTYPE html> +<html lang="en"> <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>MetaMask Loading</title> <style> #div-logo { @@ -31,5 +35,11 @@ <div id="div-logo"> <img id="logo" src="./images/loginglogo.svg"> </div> + <script type="text/javascript"> + // redirect to 404 after one minute + setTimeout(() => { + location.href = './404.html' + }, 60000) + </script> </body> </html> diff --git a/app/scripts/background.js b/app/scripts/background.js index 509a0001d..6b3ac2664 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -29,7 +29,7 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics') const EdgeEncryptor = require('./edge-encryptor') const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code') const getObjStructure = require('./lib/getObjStructure') -const ipfsContent = require('./lib/ipfsContent.js') +const setupEnsIpfsResolver = require('./lib/ens-ipfs/setup') const { ENVIRONMENT_TYPE_POPUP, @@ -58,7 +58,6 @@ const isIE = !!document.documentMode // Edge 20+ const isEdge = !isIE && !!window.StyleMedia -let ipfsHandle let popupIsOpen = false let notificationIsOpen = false const openMetamaskTabsIDs = {} @@ -164,7 +163,6 @@ async function initialize () { const initLangCode = await getFirstPreferredLangCode() await setupController(initState, initLangCode) log.debug('MetaMask initialization complete.') - ipfsHandle = ipfsContent(initState.NetworkController.provider) } // @@ -269,10 +267,8 @@ function setupController (initState, initLangCode) { }) global.metamaskController = controller - controller.networkController.on('networkDidChange', () => { - ipfsHandle && ipfsHandle.remove() - ipfsHandle = ipfsContent(controller.networkController.providerStore.getState()) - }) + const provider = controller.provider + setupEnsIpfsResolver({ provider }) // report failed transactions to Sentry controller.txController.on(`tx:status-update`, (txId, status) => { diff --git a/app/scripts/controllers/currency.js b/app/scripts/controllers/currency.js index d5bc5fe2b..1e866d2c9 100644 --- a/app/scripts/controllers/currency.js +++ b/app/scripts/controllers/currency.js @@ -21,6 +21,7 @@ class CurrencyController { * since midnight of January 1, 1970 * @property {number} conversionInterval The id of the interval created by the scheduleConversionInterval method. * Used to clear an existing interval on subsequent calls of that method. + * @property {string} nativeCurrency The ticker/symbol of the native chain currency * */ constructor (opts = {}) { @@ -28,6 +29,7 @@ class CurrencyController { currentCurrency: 'usd', conversionRate: 0, conversionDate: 'N/A', + nativeCurrency: 'ETH', }, opts.initState) this.store = new ObservableStore(initState) } @@ -37,6 +39,29 @@ class CurrencyController { // /** + * A getter for the nativeCurrency property + * + * @returns {string} A 2-4 character shorthand that describes the specific currency + * + */ + getNativeCurrency () { + return this.store.getState().nativeCurrency + } + + /** + * A setter for the nativeCurrency property + * + * @param {string} nativeCurrency The new currency to set as the nativeCurrency in the store + * + */ + setNativeCurrency (nativeCurrency) { + this.store.updateState({ + nativeCurrency, + ticker: nativeCurrency, + }) + } + + /** * A getter for the currentCurrency property * * @returns {string} A 2-4 character shorthand that describes a specific currency, currently selected by the user @@ -104,15 +129,32 @@ class CurrencyController { * */ async updateConversionRate () { - let currentCurrency + let currentCurrency, nativeCurrency try { currentCurrency = this.getCurrentCurrency() - const response = await fetch(`https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`) + nativeCurrency = this.getNativeCurrency() + let apiUrl + if (nativeCurrency === 'ETH') { + apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}` + } else { + apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}` + } + const response = await fetch(apiUrl) const parsedResponse = await response.json() - this.setConversionRate(Number(parsedResponse.bid)) - this.setConversionDate(Number(parsedResponse.timestamp)) + if (nativeCurrency === 'ETH') { + this.setConversionRate(Number(parsedResponse.bid)) + this.setConversionDate(Number(parsedResponse.timestamp)) + } else { + if (parsedResponse[currentCurrency.toUpperCase()]) { + this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()])) + this.setConversionDate(parseInt((new Date()).getTime() / 1000)) + } else { + this.setConversionRate(0) + this.setConversionDate('N/A') + } + } } catch (err) { - log.warn(`MetaMask - Failed to query currency conversion:`, currentCurrency, err) + log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err) this.setConversionRate(0) this.setConversionDate('N/A') } diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js index 6a5369f06..c21e9c764 100644 --- a/app/scripts/controllers/network/network.js +++ b/app/scripts/controllers/network/network.js @@ -11,6 +11,8 @@ const createInfuraClient = require('./createInfuraClient') const createJsonRpcClient = require('./createJsonRpcClient') const createLocalhostClient = require('./createLocalhostClient') const { createSwappableProxy, createEventEmitterProxy } = require('swappable-obj-proxy') +const extend = require('extend') +const networks = { networkList: {} } const { ROPSTEN, @@ -29,6 +31,10 @@ const defaultProviderConfig = { type: testMode ? RINKEBY : MAINNET, } +const defaultNetworkConfig = { + ticker: 'ETH', +} + module.exports = class NetworkController extends EventEmitter { constructor (opts = {}) { @@ -39,7 +45,8 @@ module.exports = class NetworkController extends EventEmitter { // create stores this.providerStore = new ObservableStore(providerConfig) this.networkStore = new ObservableStore('loading') - this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore }) + this.networkConfig = new ObservableStore(defaultNetworkConfig) + this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore, settings: this.networkConfig }) this.on('networkDidChange', this.lookupNetwork) // provider and block tracker this._provider = null @@ -51,8 +58,8 @@ module.exports = class NetworkController extends EventEmitter { initializeProvider (providerParams) { this._baseProviderParams = providerParams - const { type, rpcTarget } = this.providerStore.getState() - this._configureProvider({ type, rpcTarget }) + const { type, rpcTarget, chainId, ticker, nickname } = this.providerStore.getState() + this._configureProvider({ type, rpcTarget, chainId, ticker, nickname }) this.lookupNetwork() } @@ -72,7 +79,20 @@ module.exports = class NetworkController extends EventEmitter { return this.networkStore.getState() } - setNetworkState (network) { + getNetworkConfig () { + return this.networkConfig.getState() + } + + setNetworkState (network, type) { + if (network === 'loading') { + return this.networkStore.putState(network) + } + + // type must be defined + if (!type) { + return + } + network = networks.networkList[type] && networks.networkList[type].chainId ? networks.networkList[type].chainId : network return this.networkStore.putState(network) } @@ -85,6 +105,7 @@ module.exports = class NetworkController extends EventEmitter { if (!this._provider) { return log.warn('NetworkController - lookupNetwork aborted due to missing provider') } + var { type } = this.providerStore.getState() const ethQuery = new EthQuery(this._provider) // first attempt to perform lookup via eth_chainId ethQuery.sendAsync({ method: 'eth_chainId' }, (err, chainIdHex) => { @@ -93,20 +114,23 @@ module.exports = class NetworkController extends EventEmitter { ethQuery.sendAsync({ method: 'net_version' }, (err, network) => { if (err) return this.setNetworkState('loading') log.info(`net_version returned ${network}`) - this.setNetworkState(network) + this.setNetworkState(network, type) }) return } const chainId = Number.parseInt(chainIdHex, 16) log.info(`net_version returned ${chainId}`) - this.setNetworkState(chainId) + this.setNetworkState(chainId, type) }) } - setRpcTarget (rpcTarget) { + setRpcTarget (rpcTarget, chainId, ticker = 'ETH', nickname = '') { const providerConfig = { type: 'rpc', rpcTarget, + chainId, + ticker, + nickname, } this.providerConfig = providerConfig } @@ -142,7 +166,7 @@ module.exports = class NetworkController extends EventEmitter { } _configureProvider (opts) { - const { type, rpcTarget } = opts + const { type, rpcTarget, chainId, ticker, nickname } = opts // infura type-based endpoints const isInfura = INFURA_PROVIDER_TYPES.includes(type) if (isInfura) { @@ -152,7 +176,7 @@ module.exports = class NetworkController extends EventEmitter { this._configureLocalhostProvider() // url-based rpc endpoints } else if (type === 'rpc') { - this._configureStandardProvider({ rpcUrl: rpcTarget }) + this._configureStandardProvider({ rpcUrl: rpcTarget, chainId, ticker, nickname }) } else { throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`) } @@ -162,6 +186,11 @@ module.exports = class NetworkController extends EventEmitter { log.info('NetworkController - configureInfuraProvider', type) const networkClient = createInfuraClient({ network: type }) this._setNetworkClient(networkClient) + // setup networkConfig + var settings = { + ticker: 'ETH', + } + this.networkConfig.putState(settings) } _configureLocalhostProvider () { @@ -170,9 +199,22 @@ module.exports = class NetworkController extends EventEmitter { this._setNetworkClient(networkClient) } - _configureStandardProvider ({ rpcUrl }) { + _configureStandardProvider ({ rpcUrl, chainId, ticker, nickname }) { log.info('NetworkController - configureStandardProvider', rpcUrl) const networkClient = createJsonRpcClient({ rpcUrl }) + // hack to add a 'rpc' network with chainId + networks.networkList['rpc'] = { + chainId: chainId, + rpcUrl, + ticker: ticker || 'ETH', + nickname, + } + // setup networkConfig + var settings = { + network: chainId, + } + settings = extend(settings, networks.networkList['rpc']) + this.networkConfig.putState(settings) this._setNetworkClient(networkClient) } diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 8eb2bce0c..120801f06 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -25,7 +25,7 @@ class PreferencesController { */ constructor (opts = {}) { const initState = extend({ - frequentRpcList: [], + frequentRpcListDetail: [], currentAccountTab: 'history', accountTokens: {}, assetImages: {}, @@ -39,7 +39,7 @@ class PreferencesController { seedWords: null, forgottenPassword: false, preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, opts.initState) @@ -104,7 +104,7 @@ class PreferencesController { * @param {Function} - end */ async requestWatchAsset (req, res, next, end) { - if (req.method === 'metamask_watchAsset') { + if (req.method === 'metamask_watchAsset' || req.method === 'wallet_watchAsset') { const { type, options } = req.params switch (type) { case 'ERC20': @@ -375,22 +375,6 @@ class PreferencesController { } /** - * Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list. - * - * @param {string} _url The the new rpc url to add to the updated list - * @param {bool} remove Remove selected url - * @returns {Promise<void>} Promise resolves with undefined - * - */ - updateFrequentRpcList (_url, remove = false) { - return this.addToFrequentRpcList(_url, remove) - .then((rpcList) => { - this.store.updateState({ frequentRpcList: rpcList }) - return Promise.resolve() - }) - } - - /** * Setter for the `currentAccountTab` property * * @param {string} currentAccountTab Specifies the new tab to be marked as current @@ -405,35 +389,53 @@ class PreferencesController { } /** - * Returns an updated rpcList based on the passed url and the current list. - * The returned list will have a max length of 3. If the _url currently exists it the list, it will be moved to the - * end of the list. The current list is modified and returned as a promise. + * Adds custom RPC url to state. * - * @param {string} _url The rpc url to add to the frequentRpcList. - * @param {bool} remove Remove selected url - * @returns {Promise<array>} The updated frequentRpcList. + * @param {string} url The RPC url to add to frequentRpcList. + * @param {number} chainId Optional chainId of the selected network. + * @param {string} ticker Optional ticker symbol of the selected network. + * @param {string} nickname Optional nickname of the selected network. + * @returns {Promise<array>} Promise resolving to updated frequentRpcList. * */ - addToFrequentRpcList (_url, remove = false) { - const rpcList = this.getFrequentRpcList() - const index = rpcList.findIndex((element) => { return element === _url }) + addToFrequentRpcList (url, chainId, ticker = 'ETH', nickname = '') { + const rpcList = this.getFrequentRpcListDetail() + const index = rpcList.findIndex((element) => { return element.rpcUrl === url }) if (index !== -1) { rpcList.splice(index, 1) } - if (!remove && _url !== 'http://localhost:8545') { - rpcList.push(_url) + if (url !== 'http://localhost:8545') { + rpcList.push({ rpcUrl: url, chainId, ticker, nickname }) + } + this.store.updateState({ frequentRpcListiDetail: rpcList }) + return Promise.resolve(rpcList) + } + + /** + * Removes custom RPC url from state. + * + * @param {string} url The RPC url to remove from frequentRpcList. + * @returns {Promise<array>} Promise resolving to updated frequentRpcList. + * + */ + removeFromFrequentRpcList (url) { + const rpcList = this.getFrequentRpcListDetail() + const index = rpcList.findIndex((element) => { return element.rpcUrl === url }) + if (index !== -1) { + rpcList.splice(index, 1) } + this.store.updateState({ frequentRpcListDetail: rpcList }) return Promise.resolve(rpcList) } /** - * Getter for the `frequentRpcList` property. + * Getter for the `frequentRpcListDetail` property. * - * @returns {array<string>} An array of one or two rpc urls. + * @returns {array<array>} An array of rpc urls. * */ - getFrequentRpcList () { - return this.store.getState().frequentRpcList + getFrequentRpcListDetail () { + return this.store.getState().frequentRpcListDetail } /** diff --git a/app/scripts/lib/contracts/registrar.js b/app/scripts/lib/ens-ipfs/contracts/registrar.js index 99ca24458..99ca24458 100644 --- a/app/scripts/lib/contracts/registrar.js +++ b/app/scripts/lib/ens-ipfs/contracts/registrar.js diff --git a/app/scripts/lib/contracts/resolver.js b/app/scripts/lib/ens-ipfs/contracts/resolver.js index 1bf3f90ce..1bf3f90ce 100644 --- a/app/scripts/lib/contracts/resolver.js +++ b/app/scripts/lib/ens-ipfs/contracts/resolver.js diff --git a/app/scripts/lib/ens-ipfs/resolver.js b/app/scripts/lib/ens-ipfs/resolver.js new file mode 100644 index 000000000..fe2dc1134 --- /dev/null +++ b/app/scripts/lib/ens-ipfs/resolver.js @@ -0,0 +1,54 @@ +const namehash = require('eth-ens-namehash') +const multihash = require('multihashes') +const Eth = require('ethjs-query') +const EthContract = require('ethjs-contract') +const registrarAbi = require('./contracts/registrar') +const resolverAbi = require('./contracts/resolver') + +module.exports = resolveEnsToIpfsContentId + + +async function resolveEnsToIpfsContentId ({ provider, name }) { + const eth = new Eth(provider) + const hash = namehash.hash(name) + const contract = new EthContract(eth) + // lookup registrar + const chainId = Number.parseInt(await eth.net_version(), 10) + const registrarAddress = getRegistrarForChainId(chainId) + if (!registrarAddress) { + throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`) + } + const Registrar = contract(registrarAbi).at(registrarAddress) + // lookup resolver + const resolverLookupResult = await Registrar.resolver(hash) + const resolverAddress = resolverLookupResult[0] + if (hexValueIsEmpty(resolverAddress)) { + throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`) + } + const Resolver = contract(resolverAbi).at(resolverAddress) + // lookup content id + const contentLookupResult = await Resolver.content(hash) + const contentHash = contentLookupResult[0] + if (hexValueIsEmpty(contentHash)) { + throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`) + } + const nonPrefixedHex = contentHash.slice(2) + const buffer = multihash.fromHexString(nonPrefixedHex) + const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256')) + return contentId +} + +function hexValueIsEmpty(value) { + return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value) +} + +function getRegistrarForChainId (chainId) { + switch (chainId) { + // mainnet + case 1: + return '0x314159265dd8dbb310642f98f50c066173c1259b' + // ropsten + case 3: + return '0x112234455c3a32fd11230c42e7bccd4a84e02010' + } +} diff --git a/app/scripts/lib/ens-ipfs/setup.js b/app/scripts/lib/ens-ipfs/setup.js new file mode 100644 index 000000000..45eb1ce14 --- /dev/null +++ b/app/scripts/lib/ens-ipfs/setup.js @@ -0,0 +1,63 @@ +const urlUtil = require('url') +const extension = require('extensionizer') +const resolveEnsToIpfsContentId = require('./resolver.js') + +const supportedTopLevelDomains = ['eth'] + +module.exports = setupEnsIpfsResolver + +function setupEnsIpfsResolver({ provider }) { + + // install listener + const urlPatterns = supportedTopLevelDomains.map(tld => `*://*.${tld}/*`) + extension.webRequest.onErrorOccurred.addListener(webRequestDidFail, { urls: urlPatterns }) + + // return api object + return { + // uninstall listener + remove () { + extension.webRequest.onErrorOccurred.removeListener(webRequestDidFail) + }, + } + + async function webRequestDidFail (details) { + const { tabId, url } = details + // ignore requests that are not associated with tabs + if (tabId === -1) return + // parse ens name + const urlData = urlUtil.parse(url) + const { hostname: name, path, search } = urlData + const domainParts = name.split('.') + const topLevelDomain = domainParts[domainParts.length - 1] + // if unsupported TLD, abort + if (!supportedTopLevelDomains.includes(topLevelDomain)) return + // otherwise attempt resolve + attemptResolve({ tabId, name, path, search }) + } + + async function attemptResolve({ tabId, name, path, search }) { + extension.tabs.update(tabId, { url: `loading.html` }) + try { + const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name }) + let url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}` + try { + // check if ipfs gateway has result + const response = await fetch(url, { method: 'HEAD' }) + // if failure, redirect to 404 page + if (response.status !== 200) { + extension.tabs.update(tabId, { url: '404.html' }) + return + } + // otherwise redirect to the correct page + extension.tabs.update(tabId, { url }) + } catch (err) { + console.warn(err) + // if HEAD fetch failed, redirect so user can see relevant error page + extension.tabs.update(tabId, { url }) + } + } catch (err) { + console.warn(err) + extension.tabs.update(tabId, { url: `error.html?name=${name}` }) + } + } +} diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js deleted file mode 100644 index 8b08453c4..000000000 --- a/app/scripts/lib/ipfsContent.js +++ /dev/null @@ -1,46 +0,0 @@ -const extension = require('extensionizer') -const resolver = require('./resolver.js') - -module.exports = function (provider) { - function ipfsContent (details) { - const name = details.url.substring(7, details.url.length - 1) - let clearTime = null - if (/^.+\.eth$/.test(name) === false) return - - extension.tabs.query({active: true}, tab => { - extension.tabs.update(tab.id, { url: 'loading.html' }) - - clearTime = setTimeout(() => { - return extension.tabs.update(tab.id, { url: '404.html' }) - }, 60000) - - resolver.resolve(name, provider).then(ipfsHash => { - clearTimeout(clearTime) - let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash - return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { - if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' }) - extension.tabs.update(tab.id, { url: url }) - }) - .catch(err => { - url = 'https://ipfs.infura.io/ipfs/' + ipfsHash - extension.tabs.update(tab.id, {url: url}) - return err - }) - }) - .catch(err => { - clearTimeout(clearTime) - const url = err === 'unsupport' ? 'unsupport' : 'error' - extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) - }) - }) - return { cancel: true } - } - - extension.webRequest.onErrorOccurred.addListener(ipfsContent, {urls: ['*://*.eth/'], types: ['main_frame']}) - - return { - remove () { - extension.webRequest.onErrorOccurred.removeListener(ipfsContent) - }, - } -} diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js deleted file mode 100644 index ff0fed161..000000000 --- a/app/scripts/lib/resolver.js +++ /dev/null @@ -1,71 +0,0 @@ -const namehash = require('eth-ens-namehash') -const multihash = require('multihashes') -const HttpProvider = require('ethjs-provider-http') -const Eth = require('ethjs-query') -const EthContract = require('ethjs-contract') -const registrarAbi = require('./contracts/registrar') -const resolverAbi = require('./contracts/resolver') - -function ens (name, provider) { - const eth = new Eth(new HttpProvider(getProvider(provider.type))) - const hash = namehash.hash(name) - const contract = new EthContract(eth) - const Registrar = contract(registrarAbi).at(getRegistrar(provider.type)) - return new Promise((resolve, reject) => { - if (provider.type === 'mainnet' || provider.type === 'ropsten') { - Registrar.resolver(hash).then((address) => { - if (address === '0x0000000000000000000000000000000000000000') { - reject(null) - } else { - const Resolver = contract(resolverAbi).at(address['0']) - return Resolver.content(hash) - } - }).then((contentHash) => { - if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null) - if (contentHash.ret !== '0x') { - const hex = contentHash['0'].substring(2) - const buf = multihash.fromHexString(hex) - resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256'))) - } else { - reject(null) - } - }) - } else { - return reject('unsupport') - } - }) -} - -function getProvider (type) { - switch (type) { - case 'mainnet': - return 'https://mainnet.infura.io/' - case 'ropsten': - return 'https://ropsten.infura.io/' - default: - return 'http://localhost:8545/' - } -} - -function getRegistrar (type) { - switch (type) { - case 'mainnet': - return '0x314159265dd8dbb310642f98f50c066173c1259b' - case 'ropsten': - return '0x112234455c3a32fd11230c42e7bccd4a84e02010' - default: - return '0x0000000000000000000000000000000000000000' - } -} - -module.exports.resolve = function (name, provider) { - const path = name.split('.') - const topLevelDomain = path[path.length - 1] - if (topLevelDomain === 'eth' || topLevelDomain === 'test') { - return ens(name, provider) - } else { - return new Promise((resolve, reject) => { - reject(null) - }) - } -} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 32ceb6790..3778dbdb6 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -138,12 +138,12 @@ module.exports = class MetamaskController extends EventEmitter { this.accountTracker.stop() } }) - + // ensure accountTracker updates balances after network change this.networkController.on('networkDidChange', () => { this.accountTracker._updateAccounts() }) - + // key mgmt const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring] this.keyringController = new KeyringController({ @@ -197,6 +197,8 @@ module.exports = class MetamaskController extends EventEmitter { }) this.networkController.on('networkDidChange', () => { this.balancesController.updateAllBalances() + var currentCurrency = this.currencyController.getCurrentCurrency() + this.setCurrentCurrency(currentCurrency, function() {}) }) this.balancesController.updateAllBalances() @@ -1412,10 +1414,13 @@ module.exports = class MetamaskController extends EventEmitter { * @param {Function} cb - A callback function returning currency info. */ setCurrentCurrency (currencyCode, cb) { + const { ticker } = this.networkController.getNetworkConfig() try { + this.currencyController.setNativeCurrency(ticker) this.currencyController.setCurrentCurrency(currencyCode) this.currencyController.updateConversionRate() const data = { + nativeCurrency: ticker || 'ETH', conversionRate: this.currencyController.getConversionRate(), currentCurrency: this.currencyController.getCurrentCurrency(), conversionDate: this.currencyController.getConversionDate(), @@ -1454,11 +1459,14 @@ module.exports = class MetamaskController extends EventEmitter { /** * A method for selecting a custom URL for an ethereum RPC provider. * @param {string} rpcTarget - A URL for a valid Ethereum RPC API. + * @param {number} chainId - The chainId of the selected network. + * @param {string} ticker - The ticker symbol of the selected network. + * @param {string} nickname - Optional nickname of the selected network. * @returns {Promise<String>} - The RPC Target URL confirmed. */ - async setCustomRpc (rpcTarget) { - this.networkController.setRpcTarget(rpcTarget) - await this.preferencesController.updateFrequentRpcList(rpcTarget) + async setCustomRpc (rpcTarget, chainId, ticker = 'ETH', nickname = '') { + this.networkController.setRpcTarget(rpcTarget, chainId, ticker, nickname) + await this.preferencesController.addToFrequentRpcList(rpcTarget, chainId, ticker, nickname) return rpcTarget } @@ -1467,7 +1475,7 @@ module.exports = class MetamaskController extends EventEmitter { * @param {string} rpcTarget - A RPC URL to delete. */ async delCustomRpc (rpcTarget) { - await this.preferencesController.updateFrequentRpcList(rpcTarget, true) + await this.preferencesController.removeFromFrequentRpcList(rpcTarget) } /** diff --git a/development/states/add-token.json b/development/states/add-token.json index 6a525f2b3..b59e9b757 100644 --- a/development/states/add-token.json +++ b/development/states/add-token.json @@ -109,7 +109,7 @@ }, "currentLocale": "en", "preferences": { - "useETHAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true } }, "appState": { diff --git a/development/states/confirm-sig-requests.json b/development/states/confirm-sig-requests.json index c7103cd13..1ffde3938 100644 --- a/development/states/confirm-sig-requests.json +++ b/development/states/confirm-sig-requests.json @@ -152,7 +152,7 @@ }, "currentLocale": "en", "preferences": { - "useETHAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true } }, "appState": { diff --git a/development/states/currency-localization.json b/development/states/currency-localization.json index 7dea42ade..ef28891a3 100644 --- a/development/states/currency-localization.json +++ b/development/states/currency-localization.json @@ -110,7 +110,7 @@ }, "currentLocale": "en", "preferences": { - "useETHAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true } }, "appState": { diff --git a/development/states/first-time.json b/development/states/first-time.json index 3206b67a3..ff7078720 100644 --- a/development/states/first-time.json +++ b/development/states/first-time.json @@ -39,7 +39,7 @@ "tokens": [], "currentLocale": "en", "preferences": { - "useETHAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true } }, "appState": { diff --git a/development/states/send-new-ui.json b/development/states/send-new-ui.json index d9924dd74..0cd2f23f2 100644 --- a/development/states/send-new-ui.json +++ b/development/states/send-new-ui.json @@ -111,7 +111,7 @@ }, "currentLocale": "en", "preferences": { - "useETHAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true } }, "appState": { diff --git a/development/states/tx-list-items.json b/development/states/tx-list-items.json index cbffa98e5..e83179a17 100644 --- a/development/states/tx-list-items.json +++ b/development/states/tx-list-items.json @@ -104,7 +104,7 @@ "send": {}, "currentLocale": "en", "preferences": { - "useETHAsPrimaryCurrency": true + "useNativeCurrencyAsPrimaryCurrency": true } }, "appState": { diff --git a/old-ui/app/app.js b/old-ui/app/app.js index 9be21ebad..f5e03d4f6 100644 --- a/old-ui/app/app.js +++ b/old-ui/app/app.js @@ -73,7 +73,7 @@ function mapStateToProps (state) { forgottenPassword: state.appState.forgottenPassword, nextUnreadNotice: state.metamask.nextUnreadNotice, lostAccounts: state.metamask.lostAccounts, - frequentRpcList: state.metamask.frequentRpcList || [], + frequentRpcListDetail: state.metamask.frequentRpcListDetail || [], featureFlags, suggestedTokens: state.metamask.suggestedTokens, diff --git a/old-ui/app/components/app-bar.js b/old-ui/app/components/app-bar.js index 234c06a01..fa8e499ed 100644 --- a/old-ui/app/components/app-bar.js +++ b/old-ui/app/components/app-bar.js @@ -17,7 +17,7 @@ module.exports = class AppBar extends Component { static propTypes = { dispatch: PropTypes.func.isRequired, - frequentRpcList: PropTypes.array.isRequired, + frequentRpcListDetail: PropTypes.array.isRequired, isMascara: PropTypes.bool.isRequired, isOnboarding: PropTypes.bool.isRequired, identities: PropTypes.any.isRequired, @@ -196,7 +196,7 @@ module.exports = class AppBar extends Component { renderNetworkDropdown () { const { dispatch, - frequentRpcList: rpcList, + frequentRpcListDetail: rpcList, provider, } = this.props const { @@ -321,8 +321,8 @@ module.exports = class AppBar extends Component { ]) } - renderCustomOption ({ rpcTarget, type }) { - const {dispatch} = this.props + renderCustomOption ({ rpcTarget, type, ticker }) { + const {dispatch, network} = this.props if (type !== 'rpc') { return null @@ -340,7 +340,7 @@ module.exports = class AppBar extends Component { default: return h(DropdownMenuItem, { key: rpcTarget, - onClick: () => dispatch(actions.setRpcTarget(rpcTarget)), + onClick: () => dispatch(actions.setRpcTarget(rpcTarget, network, ticker)), closeMenu: () => this.setState({ isNetworkMenuOpen: false }), }, [ h('i.fa.fa-question-circle.fa-lg.menu-icon'), @@ -354,7 +354,8 @@ module.exports = class AppBar extends Component { const {dispatch} = this.props const reversedRpcList = rpcList.slice().reverse() - return reversedRpcList.map((rpc) => { + return reversedRpcList.map((entry) => { + const rpc = entry.rpcUrl const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget if ((rpc === LOCALHOST_RPC_URL) || currentRpcTarget) { @@ -363,7 +364,7 @@ module.exports = class AppBar extends Component { return h(DropdownMenuItem, { key: `common${rpc}`, closeMenu: () => this.setState({ isNetworkMenuOpen: false }), - onClick: () => dispatch(actions.setRpcTarget(rpc)), + onClick: () => dispatch(actions.setRpcTarget(rpc, entry.chainId, entry.ticker)), }, [ h('i.fa.fa-question-circle.fa-lg.menu-icon'), rpc, diff --git a/old-ui/app/components/balance.js b/old-ui/app/components/balance.js index 57ca84564..8995f961f 100644 --- a/old-ui/app/components/balance.js +++ b/old-ui/app/components/balance.js @@ -1,12 +1,18 @@ const Component = require('react').Component const h = require('react-hyperscript') +const connect = require('react-redux').connect const inherits = require('util').inherits const formatBalance = require('../util').formatBalance const generateBalanceObject = require('../util').generateBalanceObject const Tooltip = require('./tooltip.js') const FiatValue = require('./fiat-value.js') -module.exports = EthBalanceComponent +module.exports = connect(mapStateToProps)(EthBalanceComponent) +function mapStateToProps (state) { + return { + ticker: state.metamask.ticker, + } +} inherits(EthBalanceComponent, Component) function EthBalanceComponent () { @@ -16,9 +22,10 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { var props = this.props let { value } = props + const { ticker } = props var style = props.style var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true - value = value ? formatBalance(value, 6, needsParse) : '...' + value = value ? formatBalance(value, 6, needsParse, ticker) : '...' var width = props.width return ( diff --git a/old-ui/app/components/eth-balance.js b/old-ui/app/components/eth-balance.js index 4f538fd31..4458e6a9a 100644 --- a/old-ui/app/components/eth-balance.js +++ b/old-ui/app/components/eth-balance.js @@ -1,12 +1,18 @@ const Component = require('react').Component const h = require('react-hyperscript') +const connect = require('react-redux').connect const inherits = require('util').inherits const formatBalance = require('../util').formatBalance const generateBalanceObject = require('../util').generateBalanceObject const Tooltip = require('./tooltip.js') const FiatValue = require('./fiat-value.js') -module.exports = EthBalanceComponent +module.exports = connect(mapStateToProps)(EthBalanceComponent) +function mapStateToProps (state) { + return { + ticker: state.metamask.ticker, + } +} inherits(EthBalanceComponent, Component) function EthBalanceComponent () { @@ -16,9 +22,9 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { var props = this.props let { value } = props - const { style, width } = props + const { ticker, style, width } = props var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true - value = value ? formatBalance(value, 6, needsParse) : '...' + value = value ? formatBalance(value, 6, needsParse, ticker) : '...' return ( diff --git a/old-ui/app/config.js b/old-ui/app/config.js index 392a6dba7..7a93887a5 100644 --- a/old-ui/app/config.js +++ b/old-ui/app/config.js @@ -68,7 +68,7 @@ ConfigScreen.prototype.render = function () { currentProviderDisplay(metamaskState), - h('div', { style: {display: 'flex'} }, [ + h('div', { style: {display: 'block'} }, [ h('input#new_rpc', { placeholder: 'New RPC URL', style: { @@ -81,7 +81,70 @@ ConfigScreen.prototype.render = function () { if (event.key === 'Enter') { var element = event.target var newRpc = element.value - rpcValidation(newRpc, state) + var chainid = document.querySelector('input#chainid') + var ticker = document.querySelector('input#ticker') + var nickname = document.querySelector('input#nickname') + rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state) + } + }, + }), + h('br'), + h('input#chainid', { + placeholder: 'ChainId (optional)', + style: { + width: 'inherit', + flex: '1 0 auto', + height: '30px', + margin: '8px', + }, + onKeyPress (event) { + if (event.key === 'Enter') { + var element = document.querySelector('input#new_rpc') + var newRpc = element.value + var chainid = document.querySelector('input#chainid') + var ticker = document.querySelector('input#ticker') + var nickname = document.querySelector('input#nickname') + rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state) + } + }, + }), + h('br'), + h('input#ticker', { + placeholder: 'Symbol (optional)', + style: { + width: 'inherit', + flex: '1 0 auto', + height: '30px', + margin: '8px', + }, + onKeyPress (event) { + if (event.key === 'Enter') { + var element = document.querySelector('input#new_rpc') + var newRpc = element.value + var chainid = document.querySelector('input#chainid') + var ticker = document.querySelector('input#ticker') + var nickname = document.querySelector('input#nickname') + rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state) + } + }, + }), + h('br'), + h('input#nickname', { + placeholder: 'Nickname (optional)', + style: { + width: 'inherit', + flex: '1 0 auto', + height: '30px', + margin: '8px', + }, + onKeyPress (event) { + if (event.key === 'Enter') { + var element = document.querySelector('input#new_rpc') + var newRpc = element.value + var chainid = document.querySelector('input#chainid') + var ticker = document.querySelector('input#ticker') + var nickname = document.querySelector('input#nickname') + rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state) } }, }), @@ -93,7 +156,10 @@ ConfigScreen.prototype.render = function () { event.preventDefault() var element = document.querySelector('input#new_rpc') var newRpc = element.value - rpcValidation(newRpc, state) + var chainid = document.querySelector('input#chainid') + var ticker = document.querySelector('input#ticker') + var nickname = document.querySelector('input#nickname') + rpcValidation(newRpc, chainid.value, ticker.value, nickname.value, state) }, }, 'Save'), ]), @@ -189,9 +255,9 @@ ConfigScreen.prototype.render = function () { ) } -function rpcValidation (newRpc, state) { +function rpcValidation (newRpc, chainid, ticker = 'ETH', nickname = '', state) { if (validUrl.isWebUri(newRpc)) { - state.dispatch(actions.setRpcTarget(newRpc)) + state.dispatch(actions.setRpcTarget(newRpc, chainid, ticker, nickname)) } else { var appendedRpc = `http://${newRpc}` if (validUrl.isWebUri(appendedRpc)) { diff --git a/old-ui/app/util.js b/old-ui/app/util.js index 962832ce7..40e79b88c 100644 --- a/old-ui/app/util.js +++ b/old-ui/app/util.js @@ -102,7 +102,7 @@ function parseBalance (balance) { // Takes wei hex, returns an object with three properties. // Its "formatted" property is what we generally use to render values. -function formatBalance (balance, decimalsToKeep, needsParse = true) { +function formatBalance (balance, decimalsToKeep, needsParse = true, ticker = 'ETH') { var parsed = needsParse ? parseBalance(balance) : balance.split('.') var beforeDecimal = parsed[0] var afterDecimal = parsed[1] @@ -112,14 +112,14 @@ function formatBalance (balance, decimalsToKeep, needsParse = true) { if (afterDecimal !== '0') { var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits if (sigFigs) { afterDecimal = sigFigs[0] } - formatted = '0.' + afterDecimal + ' ETH' + formatted = '0.' + afterDecimal + ` ${ticker}` } } else { - formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ' ETH' + formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ` ${ticker}` } } else { afterDecimal += Array(decimalsToKeep).join('0') - formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ' ETH' + formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ` ${ticker}` } return formatted } diff --git a/package-lock.json b/package-lock.json index 09b66d261..a55e00aa6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9570,9 +9570,9 @@ } }, "eth-block-tracker": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.0.3.tgz", - "integrity": "sha512-Uy+5hEvOT1/C6N1Lw/uQ10v03ArnNEQEkM0yhJQWwpd8Ymy3sw4jk75SE58s1spfOBBtnr8JaSAFioAFSeg6HA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.1.0.tgz", + "integrity": "sha512-991xTy6CzYYbizkHmgRFFI9iGx1OCISve8sSLuOlt7/yD7VFH1Jd8mOmBqxaG5ywGkIXdwAR78nQ2WDReETzBg==", "requires": { "eth-json-rpc-infura": "^3.1.2", "eth-query": "^2.1.0", @@ -9604,7 +9604,7 @@ }, "eth-json-rpc-middleware": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz", + "resolved": "http://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz", "integrity": "sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q==", "requires": { "async": "^2.5.0", @@ -9643,12 +9643,12 @@ }, "node-fetch": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", + "resolved": "http://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" }, "whatwg-fetch": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "resolved": "http://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" } } @@ -9932,12 +9932,22 @@ "requires": { "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7", "ethereumjs-util": "^5.1.1" + }, + "dependencies": { + "ethereumjs-abi": { + "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7", + "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7", + "dev": true, + "requires": { + "bn.js": "^4.10.0", + "ethereumjs-util": "^5.0.0" + } + } } }, "ethereumjs-abi": { "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#00ba8463a7f7a67fcad737ff9c2ebd95643427f7", "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", - "dev": true, "requires": { "bn.js": "^4.10.0", "ethereumjs-util": "^5.0.0" @@ -9947,7 +9957,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", - "dev": true, "requires": { "bn.js": "^4.11.0", "create-hash": "^1.1.2", @@ -10390,7 +10399,7 @@ "dependencies": { "babelify": { "version": "7.3.0", - "resolved": "http://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz", "integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=", "requires": { "babel-core": "^6.0.14", @@ -14249,14 +14258,354 @@ "dev": true }, "glob-watcher": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-4.0.0.tgz", - "integrity": "sha1-nmOo/25h6TLebMLK7OUHGm1zcyk=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.1.tgz", + "integrity": "sha512-fK92r2COMC199WCyGUblrZKhjra3cyVMDiypDdqg1vsSDmexnbYivK1kNR4QItiNXLKmGlqan469ks67RtNa2g==", "requires": { "async-done": "^1.2.0", - "chokidar": "^1.4.3", + "chokidar": "^2.0.0", "just-debounce": "^1.0.0", "object.defaults": "^1.1.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "dependencies": { + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==" + } } }, "glob2base": { @@ -14381,10 +14730,10 @@ "dev": true }, "gulp": { - "version": "github:gulpjs/gulp#71c094a51c7972d26f557899ddecab0210ef3776", - "from": "github:gulpjs/gulp#4.0", + "version": "github:gulpjs/gulp#55eb23a268dcc7340bb40808600fd4802848c06f", + "from": "github:gulpjs/gulp#v4.0.0", "requires": { - "glob-watcher": "^4.0.0", + "glob-watcher": "^5.0.0", "gulp-cli": "^2.0.0", "undertaker": "^1.0.0", "vinyl-fs": "^3.0.0" @@ -21494,9 +21843,9 @@ "dev": true }, "mute-stdout": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.0.tgz", - "integrity": "sha1-WzLqB+tDyd7WEwQ0z5JvRrKn/U0=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==" }, "mute-stream": { "version": "0.0.7", @@ -33471,9 +33820,9 @@ "optional": true }, "v8flags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.0.tgz", - "integrity": "sha512-0m69VIK2dudEf2Ub0xwLQhZkDZu85OmiOpTw+UGDt56ibviYICHziM/3aE+oVg7IjGPp0c83w3eSVqa+lYZ9UQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.1.tgz", + "integrity": "sha512-iw/1ViSEaff8NJ3HLyEjawk/8hjJib3E7pvG4pddVXfUg1983s3VGsiClDjhK64MQVDGqc1Q8r18S4VKQZS9EQ==", "requires": { "homedir-polyfill": "^1.0.1" } @@ -33700,9 +34049,12 @@ }, "dependencies": { "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } } } }, diff --git a/package.json b/package.json index c994acc2f..bee3d088a 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "ensnare": "^1.0.0", "eslint-plugin-react": "^7.4.0", "eth-bin-to-ops": "^1.0.1", - "eth-block-tracker": "^4.0.3", + "eth-block-tracker": "^4.1.0", "eth-contract-metadata": "github:MetaMask/eth-contract-metadata#master", "eth-ens-namehash": "^2.0.8", "eth-hd-keyring": "^1.2.2", @@ -143,7 +143,7 @@ "fast-levenshtein": "^2.0.6", "file-loader": "^1.1.11", "fuse.js": "^3.2.0", - "gulp": "github:gulpjs/gulp#4.0", + "gulp": "github:gulpjs/gulp#v4.0.0", "gulp-autoprefixer": "^5.0.0", "gulp-debug": "^3.2.0", "gulp-eslint": "^4.0.0", diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 7e083c60e..8deff5531 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -111,7 +111,9 @@ "0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d": 0.00039345803819379796, "0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5": 0.00008189274407698049 }, + "ticker": "ETH", "currentCurrency": "usd", + "nativeCurrency": "ETH", "conversionRate": 556.12, "addressBook": [ { @@ -1248,4 +1250,4 @@ "context": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc" } } -}
\ No newline at end of file +} diff --git a/test/e2e/beta/from-import-beta-ui.spec.js b/test/e2e/beta/from-import-beta-ui.spec.js index b782a1c40..d2c3f8958 100644 --- a/test/e2e/beta/from-import-beta-ui.spec.js +++ b/test/e2e/beta/from-import-beta-ui.spec.js @@ -319,7 +319,7 @@ describe('Using MetaMask with an existing account', function () { const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) assert.equal(txValues.length, 1) - assert.equal(await txValues[0].getText(), '-1 ETH') + assert.ok(/-1\s*ETH/.test(await txValues[0].getText())) }) }) diff --git a/test/e2e/beta/metamask-beta-ui.spec.js b/test/e2e/beta/metamask-beta-ui.spec.js index f29f242c1..eaa7d18cd 100644 --- a/test/e2e/beta/metamask-beta-ui.spec.js +++ b/test/e2e/beta/metamask-beta-ui.spec.js @@ -371,7 +371,7 @@ describe('MetaMask', function () { it('balance renders', async () => { const balance = await findElement(driver, By.css('.balance-display .token-amount')) - await driver.wait(until.elementTextMatches(balance, /100.+ETH/)) + await driver.wait(until.elementTextMatches(balance, /100\s*ETH/)) await delay(regularDelayMs) }) }) @@ -420,7 +420,7 @@ describe('MetaMask', function () { if (process.env.SELENIUM_BROWSER !== 'firefox') { const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues, /-1\sETH/), 10000) + await driver.wait(until.elementTextMatches(txValues, /-1\s*ETH/), 10000) } }) }) @@ -462,7 +462,7 @@ describe('MetaMask', function () { assert.equal(transactions.length, 2) const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues, /-3\sETH/), 10000) + await driver.wait(until.elementTextMatches(txValues, /-3\s*ETH/), 10000) }) }) @@ -540,7 +540,7 @@ describe('MetaMask', function () { await findElements(driver, By.css('.transaction-list-item')) const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txListValue, /-4\sETH/), 10000) + await driver.wait(until.elementTextMatches(txListValue, /-4\s*ETH/), 10000) await txListValue.click() await delay(regularDelayMs) @@ -574,7 +574,7 @@ describe('MetaMask', function () { }, 10000) const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues[0], /-4\sETH/), 10000) + await driver.wait(until.elementTextMatches(txValues[0], /-4\s*ETH/), 10000) // const txAccounts = await findElements(driver, By.css('.tx-list-account')) // const firstTxAddress = await txAccounts[0].getText() @@ -606,7 +606,7 @@ describe('MetaMask', function () { }, 10000) const txValues = await findElement(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues, /-0\sETH/), 10000) + await driver.wait(until.elementTextMatches(txValues, /-0\s*ETH/), 10000) await closeAllWindowHandlesExcept(driver, [extension, dapp]) await driver.switchTo().window(extension) @@ -616,9 +616,9 @@ describe('MetaMask', function () { const balance = await findElement(driver, By.css('.transaction-view-balance__primary-balance')) await delay(regularDelayMs) if (process.env.SELENIUM_BROWSER !== 'firefox') { - await driver.wait(until.elementTextMatches(balance, /^92.*ETH.*$/), 10000) + await driver.wait(until.elementTextMatches(balance, /^92.*\s*ETH.*$/), 10000) const tokenAmount = await balance.getText() - assert.ok(/^92.*ETH.*$/.test(tokenAmount)) + assert.ok(/^92.*\s*ETH.*$/.test(tokenAmount)) await delay(regularDelayMs) } }) @@ -764,7 +764,7 @@ describe('MetaMask', function () { // test cancelled on firefox until https://github.com/mozilla/geckodriver/issues/906 is resolved, // or possibly until we use latest version of firefox in the tests if (process.env.SELENIUM_BROWSER !== 'firefox') { - await driver.wait(until.elementTextMatches(txValues[0], /-50\sTST/), 10000) + await driver.wait(until.elementTextMatches(txValues[0], /-50\s*TST/), 10000) } driver.wait(async () => { @@ -798,7 +798,7 @@ describe('MetaMask', function () { await findElements(driver, By.css('.transaction-list__pending-transactions')) const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txListValue, /-7\sTST/), 10000) + await driver.wait(until.elementTextMatches(txListValue, /-7\s*TST/), 10000) await txListValue.click() await delay(regularDelayMs) @@ -851,7 +851,7 @@ describe('MetaMask', function () { }, 10000) const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues[0], /-7\sTST/)) + await driver.wait(until.elementTextMatches(txValues[0], /-7\s*TST/)) const txStatuses = await findElements(driver, By.css('.transaction-list-item__action')) await driver.wait(until.elementTextMatches(txStatuses[0], /Sent\sToken/)) @@ -897,7 +897,7 @@ describe('MetaMask', function () { const [txListItem] = await findElements(driver, By.css('.transaction-list-item')) const [txListValue] = await findElements(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txListValue, /-7\sTST/)) + await driver.wait(until.elementTextMatches(txListValue, /-7\s*TST/)) await txListItem.click() await delay(regularDelayMs) }) @@ -974,7 +974,7 @@ describe('MetaMask', function () { }, 10000) const txValues = await findElements(driver, By.css('.transaction-list-item__amount--primary')) - await driver.wait(until.elementTextMatches(txValues[0], /-7\sTST/)) + await driver.wait(until.elementTextMatches(txValues[0], /-7\s*TST/)) const txStatuses = await findElements(driver, By.css('.transaction-list-item__action')) await driver.wait(until.elementTextMatches(txStatuses[0], /Approve/)) }) @@ -1027,7 +1027,7 @@ describe('MetaMask', function () { it('renders the balance for the chosen token', async () => { const balance = await findElement(driver, By.css('.transaction-view-balance__token-balance')) - await driver.wait(until.elementTextMatches(balance, /0\sBAT/)) + await driver.wait(until.elementTextMatches(balance, /0\s*BAT/)) await delay(regularDelayMs) }) }) diff --git a/test/e2e/beta/run-drizzle.sh b/test/e2e/beta/run-drizzle.sh index 7bfffd7e6..bfb7e6fdb 100755 --- a/test/e2e/beta/run-drizzle.sh +++ b/test/e2e/beta/run-drizzle.sh @@ -11,7 +11,7 @@ sleep 5 cd test/e2e/beta/ rm -rf drizzle-test mkdir drizzle-test && cd drizzle-test -npm install truffle +sudo npm install -g truffle truffle unbox drizzle echo "Deploying contracts for Drizzle test..." truffle compile && truffle migrate diff --git a/test/integration/lib/currency-localization.js b/test/integration/lib/currency-localization.js index 8d5acf5d0..f6b751ba2 100644 --- a/test/integration/lib/currency-localization.js +++ b/test/integration/lib/currency-localization.js @@ -25,5 +25,5 @@ async function runCurrencyLocalizationTest (assert, done) { const txView = await queryAsync($, '.transaction-view') const heroBalance = await findAsync($(txView), '.transaction-view-balance__balance') const fiatAmount = await findAsync($(heroBalance), '.transaction-view-balance__secondary-balance') - assert.equal(fiatAmount[0].textContent, '₱102,707.97 PHP') + assert.equal(fiatAmount[0].textContent, '₱102,707.97PHP') } diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js index e13016e68..271dd91cf 100644 --- a/test/integration/lib/send-new-ui.js +++ b/test/integration/lib/send-new-ui.js @@ -112,9 +112,9 @@ async function runSendFlowTest (assert, done) { errorMessage = $('.send-v2__error') assert.equal(errorMessage.length, 0, 'send should stop rendering amount error message after amount is corrected') - await customizeGas(assert, 0, 21000, '0 ETH', '$0.00 USD') - await customizeGas(assert, 1, 21000, '0.000021 ETH', '$0.03 USD') - await customizeGas(assert, 500, 60000, '0.03 ETH', '$36.03 USD') + await customizeGas(assert, 0, 21000, '0ETH', '$0.00USD') + await customizeGas(assert, 1, 21000, '0.000021ETH', '$0.03USD') + await customizeGas(assert, 500, 60000, '0.03ETH', '$36.03USD') const sendButton = await queryAsync($, 'button.btn-primary.btn--large.page-container__footer-button') assert.equal(sendButton[0].textContent, 'Next', 'next button rendered') diff --git a/test/unit/app/controllers/network-contoller-test.js b/test/unit/app/controllers/network-contoller-test.js index 822311931..7959e6cc1 100644 --- a/test/unit/app/controllers/network-contoller-test.js +++ b/test/unit/app/controllers/network-contoller-test.js @@ -47,7 +47,7 @@ describe('# Network Controller', function () { describe('#setNetworkState', function () { it('should update the network', function () { - networkController.setNetworkState(1) + networkController.setNetworkState(1, 'rpc') const networkState = networkController.getNetworkState() assert.equal(networkState, 1, 'network is 1') }) diff --git a/test/unit/app/controllers/preferences-controller-test.js b/test/unit/app/controllers/preferences-controller-test.js index b5ccf3fb5..c64c47ae9 100644 --- a/test/unit/app/controllers/preferences-controller-test.js +++ b/test/unit/app/controllers/preferences-controller-test.js @@ -375,6 +375,11 @@ describe('preferences controller', function () { await preferencesController.requestWatchAsset(req, res, asy.next, asy.end) sandbox.assert.called(stubEnd) sandbox.assert.notCalled(stubNext) + req.method = 'wallet_watchAsset' + req.params.type = 'someasset' + await preferencesController.requestWatchAsset(req, res, asy.next, asy.end) + sandbox.assert.calledTwice(stubEnd) + sandbox.assert.notCalled(stubNext) }) it('should through error if method is supported but asset type is not', async function () { req.method = 'metamask_watchAsset' @@ -479,5 +484,24 @@ describe('preferences controller', function () { assert.equal(preferencesController.store.getState().seedWords, 'foo bar baz') }) }) + + describe('on updateFrequentRpcList', function () { + it('should add custom RPC url to state', function () { + preferencesController.addToFrequentRpcList('rpc_url', 1) + preferencesController.addToFrequentRpcList('http://localhost:8545', 1) + assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] ) + preferencesController.addToFrequentRpcList('rpc_url', 1) + assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] ) + }) + + it('should remove custom RPC url from state', function () { + preferencesController.addToFrequentRpcList('rpc_url', 1) + assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, [{ rpcUrl: 'rpc_url', chainId: 1, ticker: 'ETH', nickname: '' }] ) + preferencesController.removeFromFrequentRpcList('other_rpc_url') + preferencesController.removeFromFrequentRpcList('http://localhost:8545') + preferencesController.removeFromFrequentRpcList('rpc_url') + assert.deepEqual(preferencesController.store.getState().frequentRpcListDetail, []) + }) + }) }) diff --git a/test/unit/ui/app/actions.spec.js b/test/unit/ui/app/actions.spec.js index 748a58b32..df7d2ee8f 100644 --- a/test/unit/ui/app/actions.spec.js +++ b/test/unit/ui/app/actions.spec.js @@ -1133,7 +1133,7 @@ describe('Actions', () => { { type: 'DISPLAY_WARNING', value: 'Had a problem changing networks!' }, ] - setRpcTargetSpy.callsFake((newRpc, callback) => { + setRpcTargetSpy.callsFake((newRpc, chainId, ticker, nickname, callback) => { callback(new Error('error')) }) diff --git a/ui/app/actions.js b/ui/app/actions.js index f8a375e2f..a8b9189e9 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -309,7 +309,7 @@ var actions = { setPreference, updatePreferences, UPDATE_PREFERENCES: 'UPDATE_PREFERENCES', - setUseETHAsPrimaryCurrencyPreference, + setUseNativeCurrencyAsPrimaryCurrencyPreference, setMouseUserState, SET_MOUSE_USER_STATE: 'SET_MOUSE_USER_STATE', @@ -1874,10 +1874,10 @@ function updateProviderType (type) { } } -function setRpcTarget (newRpc) { +function setRpcTarget (newRpc, chainId, ticker = 'ETH', nickname = '') { return (dispatch) => { - log.debug(`background.setRpcTarget: ${newRpc}`) - background.setCustomRpc(newRpc, (err, result) => { + log.debug(`background.setRpcTarget: ${newRpc} ${chainId} ${ticker} ${nickname}`) + background.setCustomRpc(newRpc, chainId, ticker, nickname, (err, result) => { if (err) { log.error(err) return dispatch(actions.displayWarning('Had a problem changing networks!')) @@ -2330,8 +2330,8 @@ function updatePreferences (value) { } } -function setUseETHAsPrimaryCurrencyPreference (value) { - return setPreference('useETHAsPrimaryCurrency', value) +function setUseNativeCurrencyAsPrimaryCurrencyPreference (value) { + return setPreference('useNativeCurrencyAsPrimaryCurrency', value) } function setNetworkNonce (networkNonce) { diff --git a/ui/app/app.js b/ui/app/app.js index aeb3d05ee..b3aff1f39 100644 --- a/ui/app/app.js +++ b/ui/app/app.js @@ -101,7 +101,7 @@ class App extends Component { network, isMouseUser, provider, - frequentRpcList, + frequentRpcListDetail, currentView, setMouseUserState, sidebar, @@ -147,7 +147,7 @@ class App extends Component { // network dropdown h(NetworkDropdown, { provider, - frequentRpcList, + frequentRpcListDetail, }, []), h(AccountMenu), @@ -230,7 +230,7 @@ App.propTypes = { alertMessage: PropTypes.string, network: PropTypes.string, provider: PropTypes.object, - frequentRpcList: PropTypes.array, + frequentRpcListDetail: PropTypes.array, currentView: PropTypes.object, sidebar: PropTypes.object, alertOpen: PropTypes.bool, @@ -322,7 +322,7 @@ function mapStateToProps (state) { forgottenPassword: state.appState.forgottenPassword, nextUnreadNotice, lostAccounts, - frequentRpcList: state.metamask.frequentRpcList || [], + frequentRpcListDetail: state.metamask.frequentRpcListDetail || [], currentCurrency: state.metamask.currentCurrency, isMouseUser: state.appState.isMouseUser, betaUI: state.metamask.featureFlags.betaUI, diff --git a/ui/app/components/account-dropdowns.js b/ui/app/components/account-dropdowns.js index 043008a36..06376e48b 100644 --- a/ui/app/components/account-dropdowns.js +++ b/ui/app/components/account-dropdowns.js @@ -6,10 +6,11 @@ const genAccountLink = require('etherscan-link').createAccountLink const connect = require('react-redux').connect const Dropdown = require('./dropdown').Dropdown const DropdownMenuItem = require('./dropdown').DropdownMenuItem -const Identicon = require('./identicon') const copyToClipboard = require('copy-to-clipboard') const { checksumAddress } = require('../util') +import Identicon from './identicon' + class AccountDropdowns extends Component { constructor (props) { super(props) diff --git a/ui/app/components/account-menu/index.js b/ui/app/components/account-menu/index.js index c9c5b60e1..94eae8d07 100644 --- a/ui/app/components/account-menu/index.js +++ b/ui/app/components/account-menu/index.js @@ -7,10 +7,10 @@ const PropTypes = require('prop-types') const h = require('react-hyperscript') const actions = require('../../actions') const { Menu, Item, Divider, CloseArea } = require('../dropdowns/components/menu') -const Identicon = require('../identicon') const { ENVIRONMENT_TYPE_POPUP } = require('../../../../app/scripts/lib/enums') const { getEnvironmentType } = require('../../../../app/scripts/lib/util') const Tooltip = require('../tooltip') +import Identicon from '../identicon' import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display' import { PRIMARY } from '../../constants/common' diff --git a/ui/app/components/account-panel.js b/ui/app/components/account-panel.js index abaaf8163..a379ed3ac 100644 --- a/ui/app/components/account-panel.js +++ b/ui/app/components/account-panel.js @@ -1,7 +1,7 @@ const inherits = require('util').inherits const Component = require('react').Component const h = require('react-hyperscript') -const Identicon = require('./identicon') +import Identicon from './identicon' const formatBalance = require('../util').formatBalance const addressSummary = require('../util').addressSummary diff --git a/ui/app/components/app-header/app-header.component.js b/ui/app/components/app-header/app-header.component.js index b8b002dcc..c82dc1de9 100644 --- a/ui/app/components/app-header/app-header.component.js +++ b/ui/app/components/app-header/app-header.component.js @@ -2,13 +2,13 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' import { matchPath } from 'react-router-dom' +import Identicon from '../identicon' const { ENVIRONMENT_TYPE_NOTIFICATION, ENVIRONMENT_TYPE_POPUP, } = require('../../../../app/scripts/lib/enums') const { DEFAULT_ROUTE, INITIALIZE_ROUTE, CONFIRM_TRANSACTION_ROUTE } = require('../../routes') -const Identicon = require('../identicon') const NetworkIndicator = require('../network') export default class AppHeader extends PureComponent { diff --git a/ui/app/components/balance-component.js b/ui/app/components/balance-component.js index e1fcf08e0..0f8514c81 100644 --- a/ui/app/components/balance-component.js +++ b/ui/app/components/balance-component.js @@ -2,11 +2,11 @@ const Component = require('react').Component const connect = require('react-redux').connect const h = require('react-hyperscript') const inherits = require('util').inherits -const TokenBalance = require('./token-balance') -const Identicon = require('./identicon') +import TokenBalance from './token-balance' +import Identicon from './identicon' import UserPreferencedCurrencyDisplay from './user-preferenced-currency-display' import { PRIMARY, SECONDARY } from '../constants/common' -const { getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors') +const { getNativeCurrency, getAssetImages, conversionRateSelector, getCurrentCurrency} = require('../selectors') const { formatBalance } = require('../util') @@ -21,6 +21,7 @@ function mapStateToProps (state) { return { account, network, + nativeCurrency: getNativeCurrency(state), conversionRate: conversionRateSelector(state), currentCurrency: getCurrentCurrency(state), assetImages: getAssetImages(state), @@ -66,10 +67,10 @@ BalanceComponent.prototype.renderTokenBalance = function () { BalanceComponent.prototype.renderBalance = function () { const props = this.props - const { account } = props + const { account, nativeCurrency } = props const balanceValue = account && account.balance const needsParse = 'needsParse' in props ? props.needsParse : true - const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse) : '...' + const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, needsParse, nativeCurrency) : '...' const showFiat = 'showFiat' in props ? props.showFiat : true if (formattedBalance === 'None' || formattedBalance === '...') { @@ -81,11 +82,12 @@ BalanceComponent.prototype.renderBalance = function () { } return h('div.flex-column.balance-display', {}, [ - h('div.token-amount', {}, h(UserPreferencedCurrencyDisplay, { + h(UserPreferencedCurrencyDisplay, { + className: 'token-amount', value: balanceValue, type: PRIMARY, ethNumberOfDecimals: 3, - })), + }), showFiat && h(UserPreferencedCurrencyDisplay, { value: balanceValue, diff --git a/ui/app/components/currency-display/currency-display.component.js b/ui/app/components/currency-display/currency-display.component.js index 5f5717be3..2d7413b57 100644 --- a/ui/app/components/currency-display/currency-display.component.js +++ b/ui/app/components/currency-display/currency-display.component.js @@ -1,7 +1,7 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' import classnames from 'classnames' -import { ETH, GWEI } from '../../constants/common' +import { GWEI } from '../../constants/common' export default class CurrencyDisplay extends PureComponent { static propTypes = { @@ -10,8 +10,9 @@ export default class CurrencyDisplay extends PureComponent { prefix: PropTypes.string, prefixComponent: PropTypes.node, style: PropTypes.object, + suffix: PropTypes.string, // Used in container - currency: PropTypes.oneOf([ETH]), + currency: PropTypes.string, denomination: PropTypes.oneOf([GWEI]), value: PropTypes.string, numberOfDecimals: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), @@ -19,17 +20,25 @@ export default class CurrencyDisplay extends PureComponent { } render () { - const { className, displayValue, prefix, prefixComponent, style } = this.props + const { className, displayValue, prefix, prefixComponent, style, suffix } = this.props const text = `${prefix || ''}${displayValue}` + const title = `${text} ${suffix}` return ( <div className={classnames('currency-display-component', className)} style={style} - title={text} + title={title} > { prefixComponent} <span className="currency-display-component__text">{ text }</span> + { + suffix && ( + <span className="currency-display-component__suffix"> + { suffix } + </span> + ) + } </div> ) } diff --git a/ui/app/components/currency-display/currency-display.container.js b/ui/app/components/currency-display/currency-display.container.js index b387229b5..1b3fe74da 100644 --- a/ui/app/components/currency-display/currency-display.container.js +++ b/ui/app/components/currency-display/currency-display.container.js @@ -26,14 +26,15 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { const convertedValue = getValueFromWeiHex({ value, toCurrency, conversionRate, numberOfDecimals, toDenomination: denomination, }) - const formattedValue = formatCurrency(convertedValue, toCurrency) - const displayValue = hideLabel ? formattedValue : `${formattedValue} ${toCurrency.toUpperCase()}` + const displayValue = formatCurrency(convertedValue, toCurrency) + const suffix = hideLabel ? undefined : toCurrency.toUpperCase() return { ...restStateProps, ...dispatchProps, ...restOwnProps, displayValue, + suffix, } } diff --git a/ui/app/components/currency-display/index.scss b/ui/app/components/currency-display/index.scss index 8c0196102..313c932b8 100644 --- a/ui/app/components/currency-display/index.scss +++ b/ui/app/components/currency-display/index.scss @@ -7,4 +7,8 @@ overflow: hidden; text-overflow: ellipsis; } + + &__suffix { + padding-left: 4px; + } } diff --git a/ui/app/components/currency-display/tests/currency-display.container.test.js b/ui/app/components/currency-display/tests/currency-display.container.test.js index b9f98c543..fb6678776 100644 --- a/ui/app/components/currency-display/tests/currency-display.container.test.js +++ b/ui/app/components/currency-display/tests/currency-display.container.test.js @@ -45,7 +45,8 @@ describe('CurrencyDisplay container', () => { currency: 'usd', }, result: { - displayValue: '$2.80 USD', + displayValue: '$2.80', + suffix: 'USD', }, }, { @@ -53,7 +54,8 @@ describe('CurrencyDisplay container', () => { value: '0x2386f26fc10000', }, result: { - displayValue: '$2.80 USD', + displayValue: '$2.80', + suffix: 'USD', }, }, { @@ -63,7 +65,8 @@ describe('CurrencyDisplay container', () => { numberOfDecimals: 3, }, result: { - displayValue: '1.266 ETH', + displayValue: '1.266', + suffix: 'ETH', }, }, { @@ -75,6 +78,7 @@ describe('CurrencyDisplay container', () => { }, result: { displayValue: '1.266', + suffix: undefined, }, }, { @@ -86,6 +90,7 @@ describe('CurrencyDisplay container', () => { }, result: { displayValue: '1', + suffix: undefined, }, }, { @@ -97,6 +102,7 @@ describe('CurrencyDisplay container', () => { }, result: { displayValue: '1000000000', + suffix: undefined, }, }, { @@ -108,6 +114,7 @@ describe('CurrencyDisplay container', () => { }, result: { displayValue: '1e-9', + suffix: undefined, }, }, ] diff --git a/ui/app/components/currency-input/currency-input.component.js b/ui/app/components/currency-input/currency-input.component.js index 54cd0e1ac..0761a75c5 100644 --- a/ui/app/components/currency-input/currency-input.component.js +++ b/ui/app/components/currency-input/currency-input.component.js @@ -14,6 +14,7 @@ export default class CurrencyInput extends PureComponent { static propTypes = { conversionRate: PropTypes.number, currentCurrency: PropTypes.string, + nativeCurrency: PropTypes.string, onChange: PropTypes.func, onBlur: PropTypes.func, suffix: PropTypes.string, @@ -77,13 +78,13 @@ export default class CurrencyInput extends PureComponent { } renderConversionComponent () { - const { useFiat, currentCurrency } = this.props + const { useFiat, currentCurrency, nativeCurrency } = this.props const { hexValue } = this.state let currency, numberOfDecimals if (useFiat) { // Display ETH - currency = ETH + currency = nativeCurrency || ETH numberOfDecimals = 6 } else { // Display Fiat diff --git a/ui/app/components/currency-input/currency-input.container.js b/ui/app/components/currency-input/currency-input.container.js index 18e5533de..1d1ed7b41 100644 --- a/ui/app/components/currency-input/currency-input.container.js +++ b/ui/app/components/currency-input/currency-input.container.js @@ -3,18 +3,19 @@ import CurrencyInput from './currency-input.component' import { ETH } from '../../constants/common' const mapStateToProps = state => { - const { metamask: { currentCurrency, conversionRate } } = state + const { metamask: { nativeCurrency, currentCurrency, conversionRate } } = state return { + nativeCurrency, currentCurrency, conversionRate, } } const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { currentCurrency } = stateProps + const { nativeCurrency, currentCurrency } = stateProps const { useFiat } = ownProps - const suffix = useFiat ? currentCurrency.toUpperCase() : ETH + const suffix = useFiat ? currentCurrency.toUpperCase() : nativeCurrency || ETH return { ...stateProps, diff --git a/ui/app/components/currency-input/tests/currency-input.component.test.js b/ui/app/components/currency-input/tests/currency-input.component.test.js index 8de0ef863..a33889f94 100644 --- a/ui/app/components/currency-input/tests/currency-input.component.test.js +++ b/ui/app/components/currency-input/tests/currency-input.component.test.js @@ -22,6 +22,7 @@ describe('CurrencyInput Component', () => { it('should render properly with a suffix', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -32,6 +33,7 @@ describe('CurrencyInput Component', () => { <Provider store={store}> <CurrencyInput suffix="ETH" + nativeCurrency="ETH" /> </Provider> ) @@ -45,6 +47,7 @@ describe('CurrencyInput Component', () => { it('should render properly with an ETH value', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -56,6 +59,7 @@ describe('CurrencyInput Component', () => { <CurrencyInput value="de0b6b3a7640000" suffix="ETH" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} /> @@ -69,12 +73,13 @@ describe('CurrencyInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'ETH') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$231.06USD') }) it('should render properly with a fiat value', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -87,6 +92,7 @@ describe('CurrencyInput Component', () => { value="f602f2234d0ea" suffix="USD" useFiat + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} /> @@ -100,7 +106,7 @@ describe('CurrencyInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'USD') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0.004328ETH') }) }) @@ -116,6 +122,7 @@ describe('CurrencyInput Component', () => { it('should call onChange and onBlur on input changes with the hex value for ETH', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -127,6 +134,7 @@ describe('CurrencyInput Component', () => { onChange={handleChangeSpy} onBlur={handleBlurSpy} suffix="ETH" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} /> @@ -140,14 +148,14 @@ describe('CurrencyInput Component', () => { const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance() assert.equal(currencyInputInstance.state.decimalValue, 0) assert.equal(currencyInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '$0.00 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$0.00USD') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('de0b6b3a7640000')) - assert.equal(wrapper.find('.currency-display-component').text(), '$231.06 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$231.06USD') assert.equal(currencyInputInstance.state.decimalValue, 1) assert.equal(currencyInputInstance.state.hexValue, 'de0b6b3a7640000') @@ -160,6 +168,7 @@ describe('CurrencyInput Component', () => { it('should call onChange and onBlur on input changes with the hex value for fiat', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -171,6 +180,7 @@ describe('CurrencyInput Component', () => { onChange={handleChangeSpy} onBlur={handleBlurSpy} suffix="USD" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} useFiat @@ -185,14 +195,14 @@ describe('CurrencyInput Component', () => { const currencyInputInstance = wrapper.find(CurrencyInput).at(0).instance() assert.equal(currencyInputInstance.state.decimalValue, 0) assert.equal(currencyInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '0 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0ETH') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('f602f2234d0ea')) - assert.equal(wrapper.find('.currency-display-component').text(), '0.004328 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0.004328ETH') assert.equal(currencyInputInstance.state.decimalValue, 1) assert.equal(currencyInputInstance.state.hexValue, 'f602f2234d0ea') @@ -205,6 +215,7 @@ describe('CurrencyInput Component', () => { it('should change the state and pass in a new decimalValue when props.value changes', () => { const mockStore = { metamask: { + nativeCurrency: 'ETH', currentCurrency: 'usd', conversionRate: 231.06, }, @@ -216,6 +227,7 @@ describe('CurrencyInput Component', () => { onChange={handleChangeSpy} onBlur={handleBlurSpy} suffix="USD" + nativeCurrency="ETH" currentCurrency="usd" conversionRate={231.06} useFiat diff --git a/ui/app/components/currency-input/tests/currency-input.container.test.js b/ui/app/components/currency-input/tests/currency-input.container.test.js index e77945e4d..5d72958e6 100644 --- a/ui/app/components/currency-input/tests/currency-input.container.test.js +++ b/ui/app/components/currency-input/tests/currency-input.container.test.js @@ -20,12 +20,14 @@ describe('CurrencyInput container', () => { metamask: { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', }, } assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', }) }) }) @@ -35,12 +37,14 @@ describe('CurrencyInput container', () => { const mockStateProps = { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', } const mockDispatchProps = {} assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, { useFiat: true }), { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', useFiat: true, suffix: 'USD', }) @@ -48,6 +52,7 @@ describe('CurrencyInput container', () => { assert.deepEqual(mergeProps(mockStateProps, mockDispatchProps, {}), { conversionRate: 280.45, currentCurrency: 'usd', + nativeCurrency: 'ETH', suffix: 'ETH', }) }) diff --git a/ui/app/components/dropdowns/components/account-dropdowns.js b/ui/app/components/dropdowns/components/account-dropdowns.js index b497f5c09..e6b3e0c0c 100644 --- a/ui/app/components/dropdowns/components/account-dropdowns.js +++ b/ui/app/components/dropdowns/components/account-dropdowns.js @@ -6,7 +6,7 @@ const genAccountLink = require('../../../../lib/account-link.js') const connect = require('react-redux').connect const Dropdown = require('./dropdown').Dropdown const DropdownMenuItem = require('./dropdown').DropdownMenuItem -const Identicon = require('../../identicon') +import Identicon from '../../identicon' const { checksumAddress } = require('../../../util') const copyToClipboard = require('copy-to-clipboard') const { formatBalance } = require('../../../util') @@ -26,14 +26,14 @@ class AccountDropdowns extends Component { } renderAccounts () { - const { identities, accounts, selected, menuItemStyles, actions, keyrings } = this.props + const { identities, accounts, selected, menuItemStyles, actions, keyrings, ticker } = this.props return Object.keys(identities).map((key, index) => { const identity = identities[key] const isSelected = identity.address === selected const balanceValue = accounts[key].balance - const formattedBalance = balanceValue ? formatBalance(balanceValue, 6) : '...' + const formattedBalance = balanceValue ? formatBalance(balanceValue, 6, true, ticker) : '...' const simpleAddress = identity.address.substring(2).toLowerCase() const keyring = keyrings.find((kr) => { @@ -421,6 +421,7 @@ AccountDropdowns.propTypes = { network: PropTypes.number, // actions.showExportPrivateKeyModal: , style: PropTypes.object, + ticker: PropTypes.string, enableAccountsSelector: PropTypes.bool, enableAccountOption: PropTypes.bool, enableAccountOptions: PropTypes.bool, @@ -458,6 +459,7 @@ const mapDispatchToProps = (dispatch) => { function mapStateToProps (state) { return { + ticker: state.metamask.ticker, keyrings: state.metamask.keyrings, sidebarOpen: state.appState.sidebar.isOpen, } diff --git a/ui/app/components/dropdowns/network-dropdown.js b/ui/app/components/dropdowns/network-dropdown.js index b252b25d9..d4cc695a6 100644 --- a/ui/app/components/dropdowns/network-dropdown.js +++ b/ui/app/components/dropdowns/network-dropdown.js @@ -24,8 +24,9 @@ const notToggleElementClassnames = [ function mapStateToProps (state) { return { provider: state.metamask.provider, - frequentRpcList: state.metamask.frequentRpcList || [], + frequentRpcListDetail: state.metamask.frequentRpcListDetail || [], networkDropdownOpen: state.appState.networkDropdownOpen, + network: state.metamask.network, } } @@ -40,8 +41,8 @@ function mapDispatchToProps (dispatch) { setDefaultRpcTarget: type => { dispatch(actions.setDefaultRpcTarget(type)) }, - setRpcTarget: (target) => { - dispatch(actions.setRpcTarget(target)) + setRpcTarget: (target, network, ticker, nickname) => { + dispatch(actions.setRpcTarget(target, network, ticker, nickname)) }, delRpcTarget: (target) => { dispatch(actions.delRpcTarget(target)) @@ -71,7 +72,7 @@ module.exports = compose( NetworkDropdown.prototype.render = function () { const props = this.props const { provider: { type: providerType, rpcTarget: activeNetwork } } = props - const rpcList = props.frequentRpcList + const rpcListDetail = props.frequentRpcListDetail const isOpen = this.props.networkDropdownOpen const dropdownMenuItemStyle = { fontSize: '16px', @@ -225,7 +226,7 @@ NetworkDropdown.prototype.render = function () { ), this.renderCustomOption(props.provider), - this.renderCommonRpc(rpcList, props.provider), + this.renderCommonRpc(rpcListDetail, props.provider), h( DropdownMenuItem, @@ -267,28 +268,33 @@ NetworkDropdown.prototype.getNetworkName = function () { } else if (providerName === 'rinkeby') { name = this.context.t('rinkeby') } else { - name = this.context.t('unknownNetwork') + name = provider.nickname || this.context.t('unknownNetwork') } return name } -NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { +NetworkDropdown.prototype.renderCommonRpc = function (rpcListDetail, provider) { const props = this.props - const reversedRpcList = rpcList.slice().reverse() + const reversedRpcListDetail = rpcListDetail.slice().reverse() + const network = props.network - return reversedRpcList.map((rpc) => { + return reversedRpcListDetail.map((entry) => { + const rpc = entry.rpcUrl + const ticker = entry.ticker || 'ETH' + const nickname = entry.nickname || '' const currentRpcTarget = provider.type === 'rpc' && rpc === provider.rpcTarget if ((rpc === 'http://localhost:8545') || currentRpcTarget) { return null } else { + const chainId = entry.chainId || network return h( DropdownMenuItem, { key: `common${rpc}`, closeMenu: () => this.props.hideNetworkDropdown(), - onClick: () => props.setRpcTarget(rpc), + onClick: () => props.setRpcTarget(rpc, chainId, ticker, nickname), style: { fontSize: '16px', lineHeight: '20px', @@ -302,7 +308,7 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { style: { color: currentRpcTarget ? '#ffffff' : '#9b9b9b', }, - }, rpc), + }, nickname || rpc), h('i.fa.fa-times.delete', { onClick: (e) => { @@ -317,8 +323,9 @@ NetworkDropdown.prototype.renderCommonRpc = function (rpcList, provider) { } NetworkDropdown.prototype.renderCustomOption = function (provider) { - const { rpcTarget, type } = provider + const { rpcTarget, type, ticker, nickname } = provider const props = this.props + const network = props.network if (type !== 'rpc') return null @@ -332,7 +339,7 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) { DropdownMenuItem, { key: rpcTarget, - onClick: () => props.setRpcTarget(rpcTarget), + onClick: () => props.setRpcTarget(rpcTarget, network, ticker, nickname), closeMenu: () => this.props.hideNetworkDropdown(), style: { fontSize: '16px', @@ -347,7 +354,7 @@ NetworkDropdown.prototype.renderCustomOption = function (provider) { style: { color: '#ffffff', }, - }, rpcTarget), + }, nickname || rpcTarget), ] ) } diff --git a/ui/app/components/dropdowns/tests/network-dropdown.test.js b/ui/app/components/dropdowns/tests/network-dropdown.test.js index 699b54605..88ad56851 100644 --- a/ui/app/components/dropdowns/tests/network-dropdown.test.js +++ b/ui/app/components/dropdowns/tests/network-dropdown.test.js @@ -45,8 +45,8 @@ describe('Network Dropdown', () => { provider: { 'type': 'test', }, - frequentRpcList: [ - 'http://localhost:7545', + frequentRpcListDetail: [ + { rpcUrl: 'http://localhost:7545' }, ], }, appState: { diff --git a/ui/app/components/eth-balance.js b/ui/app/components/eth-balance.js index c3d084bdc..2f6395a2d 100644 --- a/ui/app/components/eth-balance.js +++ b/ui/app/components/eth-balance.js @@ -1,5 +1,6 @@ const { Component } = require('react') const h = require('react-hyperscript') +const connect = require('react-redux').connect const { inherits } = require('util') const { formatBalance, @@ -8,7 +9,12 @@ const { const Tooltip = require('./tooltip.js') const FiatValue = require('./fiat-value.js') -module.exports = EthBalanceComponent +module.exports = connect(mapStateToProps)(EthBalanceComponent) +function mapStateToProps (state) { + return { + ticker: state.metamask.ticker, + } +} inherits(EthBalanceComponent, Component) function EthBalanceComponent () { @@ -17,9 +23,9 @@ function EthBalanceComponent () { EthBalanceComponent.prototype.render = function () { const props = this.props - const { value, style, width, needsParse = true } = props + const { ticker, value, style, width, needsParse = true } = props - const formattedValue = value ? formatBalance(value, 6, needsParse) : '...' + const formattedValue = value ? formatBalance(value, 6, needsParse, ticker) : '...' return ( diff --git a/ui/app/components/identicon.js b/ui/app/components/identicon.js deleted file mode 100644 index 7bd921892..000000000 --- a/ui/app/components/identicon.js +++ /dev/null @@ -1,124 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits -const connect = require('react-redux').connect -const isNode = require('detect-node') -const findDOMNode = require('react-dom').findDOMNode -const jazzicon = require('jazzicon') -const iconFactoryGen = require('../../lib/icon-factory') -const iconFactory = iconFactoryGen(jazzicon) -const { toDataUrl } = require('../../lib/blockies') - -module.exports = connect(mapStateToProps)(IdenticonComponent) - -inherits(IdenticonComponent, Component) -function IdenticonComponent () { - Component.call(this) - - this.defaultDiameter = 46 -} - -function mapStateToProps (state) { - return { - useBlockie: state.metamask.useBlockie, - } -} - -IdenticonComponent.prototype.render = function () { - var props = this.props - const { className = '', address, image } = props - var diameter = props.diameter || this.defaultDiameter - const style = { - height: diameter, - width: diameter, - borderRadius: diameter / 2, - } - if (image) { - return h('img', { - className: `${className} identicon`, - src: image, - style: { - ...style, - }, - }) - } else if (address) { - return h('div', { - className: `${className} identicon`, - key: 'identicon-' + address, - style: { - display: 'flex', - flexShrink: 0, - alignItems: 'center', - justifyContent: 'center', - ...style, - overflow: 'hidden', - }, - }) - } else { - return h('img.balance-icon', { - className, - src: './images/eth_logo.svg', - style: { - ...style, - }, - }) - } -} - -IdenticonComponent.prototype.componentDidMount = function () { - var props = this.props - const { address, useBlockie } = props - - if (!address) return - - if (!isNode) { - // eslint-disable-next-line react/no-find-dom-node - var container = findDOMNode(this) - - const diameter = props.diameter || this.defaultDiameter - - if (useBlockie) { - _generateBlockie(container, address, diameter) - } else { - _generateJazzicon(container, address, diameter) - } - } -} - -IdenticonComponent.prototype.componentDidUpdate = function () { - var props = this.props - const { address, useBlockie } = props - - if (!address) return - - if (!isNode) { - // eslint-disable-next-line react/no-find-dom-node - var container = findDOMNode(this) - - var children = container.children - for (var i = 0; i < children.length; i++) { - container.removeChild(children[i]) - } - - const diameter = props.diameter || this.defaultDiameter - - if (useBlockie) { - _generateBlockie(container, address, diameter) - } else { - _generateJazzicon(container, address, diameter) - } - } -} - -function _generateBlockie (container, address, diameter) { - const img = new Image() - img.src = toDataUrl(address) - img.height = diameter - img.width = diameter - container.appendChild(img) -} - -function _generateJazzicon (container, address, diameter) { - const img = iconFactory.iconForAddress(address, diameter) - container.appendChild(img) -} diff --git a/ui/app/components/identicon/identicon.component.js b/ui/app/components/identicon/identicon.component.js new file mode 100644 index 000000000..b892e5ae5 --- /dev/null +++ b/ui/app/components/identicon/identicon.component.js @@ -0,0 +1,99 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' +import { toDataUrl } from '../../../lib/blockies' +import contractMap from 'eth-contract-metadata' +import { checksumAddress } from '../../../app/util' +import Jazzicon from '../jazzicon' + +const getStyles = diameter => ( + { + height: diameter, + width: diameter, + borderRadius: diameter / 2, + } +) + +export default class Identicon extends PureComponent { + static propTypes = { + address: PropTypes.string, + className: PropTypes.string, + diameter: PropTypes.number, + image: PropTypes.string, + useBlockie: PropTypes.bool, + } + + static defaultProps = { + diameter: 46, + } + + renderImage () { + const { className, diameter, image } = this.props + + return ( + <img + className={classnames('identicon', className)} + src={image} + style={getStyles(diameter)} + /> + ) + } + + renderJazzicon () { + const { address, className, diameter } = this.props + + return ( + <Jazzicon + address={address} + diameter={diameter} + className={classnames('identicon', className)} + style={getStyles(diameter)} + /> + ) + } + + renderBlockie () { + const { address, className, diameter } = this.props + + return ( + <div + className={classnames('identicon', className)} + style={getStyles(diameter)} + > + <img + src={toDataUrl(address)} + height={diameter} + width={diameter} + /> + </div> + ) + } + + render () { + const { className, address, image, diameter, useBlockie } = this.props + + if (image) { + return this.renderImage() + } + + if (address) { + const checksummedAddress = checksumAddress(address) + + if (contractMap[checksummedAddress] && contractMap[checksummedAddress].logo) { + return this.renderJazzicon() + } + + return useBlockie + ? this.renderBlockie() + : this.renderJazzicon() + } + + return ( + <img + className={classnames('balance-icon', className)} + src="./images/eth_logo.svg" + style={getStyles(diameter)} + /> + ) + } +} diff --git a/ui/app/components/identicon/identicon.container.js b/ui/app/components/identicon/identicon.container.js new file mode 100644 index 000000000..bc49bc18e --- /dev/null +++ b/ui/app/components/identicon/identicon.container.js @@ -0,0 +1,12 @@ +import { connect } from 'react-redux' +import Identicon from './identicon.component' + +const mapStateToProps = state => { + const { metamask: { useBlockie } } = state + + return { + useBlockie, + } +} + +export default connect(mapStateToProps)(Identicon) diff --git a/ui/app/components/identicon/index.js b/ui/app/components/identicon/index.js new file mode 100644 index 000000000..799c886f2 --- /dev/null +++ b/ui/app/components/identicon/index.js @@ -0,0 +1 @@ +export { default } from './identicon.container' diff --git a/ui/app/components/identicon/index.scss b/ui/app/components/identicon/index.scss new file mode 100644 index 000000000..657afc48f --- /dev/null +++ b/ui/app/components/identicon/index.scss @@ -0,0 +1,7 @@ +.identicon { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + overflow: hidden; +} diff --git a/test/unit/ui/app/components/identicon.spec.js b/ui/app/components/identicon/tests/identicon.component.test.js index a2f8d8246..2944818f5 100644 --- a/test/unit/ui/app/components/identicon.spec.js +++ b/ui/app/components/identicon/tests/identicon.component.test.js @@ -3,11 +3,9 @@ import assert from 'assert' import thunk from 'redux-thunk' import configureMockStore from 'redux-mock-store' import { mount } from 'enzyme' +import Identicon from '../identicon.component' -import IdenticonComponent from '../../../../../ui/app/components/identicon' - -describe('Identicon Component', () => { - +describe('Identicon', () => { const state = { metamask: { useBlockie: false, @@ -19,18 +17,35 @@ describe('Identicon Component', () => { const store = mockStore(state) it('renders default eth_logo identicon with no props', () => { - const wrapper = mount(<IdenticonComponent store={store}/>) + const wrapper = mount( + <Identicon store={store}/> + ) + assert.equal(wrapper.find('img.balance-icon').prop('src'), './images/eth_logo.svg') }) it('renders custom image and add className props', () => { - const wrapper = mount(<IdenticonComponent store={store} className={'test-image'} image={'test-image'} />) - assert.equal(wrapper.find('img.test-image').prop('className'), 'test-image identicon') + const wrapper = mount( + <Identicon + store={store} + className="test-image" + image="test-image" + /> + ) + + assert.equal(wrapper.find('img.test-image').prop('className'), 'identicon test-image') assert.equal(wrapper.find('img.test-image').prop('src'), 'test-image') }) it('renders div with address prop', () => { - const wrapper = mount(<IdenticonComponent store={store} className={'test-address'} address={'0xTest'} />) - assert.equal(wrapper.find('div.test-address').prop('className'), 'test-address identicon') + const wrapper = mount( + <Identicon + store={store} + className="test-address" + address="0xTest" + /> + ) + + assert.equal(wrapper.find('div.test-address').prop('className'), 'identicon test-address') }) }) diff --git a/ui/app/components/index.scss b/ui/app/components/index.scss index beffdb221..72de6cb93 100644 --- a/ui/app/components/index.scss +++ b/ui/app/components/index.scss @@ -16,6 +16,8 @@ @import './export-text-container/index'; +@import './identicon/index'; + @import './info-box/index'; @import './menu-bar/index'; diff --git a/ui/app/components/jazzicon/index.js b/ui/app/components/jazzicon/index.js new file mode 100644 index 000000000..bea900ab9 --- /dev/null +++ b/ui/app/components/jazzicon/index.js @@ -0,0 +1 @@ +export { default } from './jazzicon.component' diff --git a/ui/app/components/jazzicon/jazzicon.component.js b/ui/app/components/jazzicon/jazzicon.component.js new file mode 100644 index 000000000..fcb1c59b1 --- /dev/null +++ b/ui/app/components/jazzicon/jazzicon.component.js @@ -0,0 +1,69 @@ +import React, { PureComponent } from 'react' +import PropTypes from 'prop-types' +import isNode from 'detect-node' +import { findDOMNode } from 'react-dom' +import jazzicon from 'jazzicon' +import iconFactoryGenerator from '../../../lib/icon-factory' +const iconFactory = iconFactoryGenerator(jazzicon) + +/** + * Wrapper around the jazzicon library to return a React component, as the library returns an + * HTMLDivElement which needs to be appended. + */ +export default class Jazzicon extends PureComponent { + static propTypes = { + address: PropTypes.string.isRequired, + className: PropTypes.string, + diameter: PropTypes.number, + style: PropTypes.object, + } + + static defaultProps = { + diameter: 46, + } + + componentDidMount () { + if (!isNode) { + this.appendJazzicon() + } + } + + componentDidUpdate (prevProps) { + const { address: prevAddress } = prevProps + const { address } = this.props + + if (!isNode && address !== prevAddress) { + this.removeExistingChildren() + this.appendJazzicon() + } + } + + removeExistingChildren () { + // eslint-disable-next-line react/no-find-dom-node + const container = findDOMNode(this) + const { children } = container + + for (let i = 0; i < children.length; i++) { + container.removeChild(children[i]) + } + } + + appendJazzicon () { + // eslint-disable-next-line react/no-find-dom-node + const container = findDOMNode(this) + const { address, diameter } = this.props + const image = iconFactory.iconForAddress(address, diameter) + container.appendChild(image) + } + + render () { + const { className, style } = this.props + + return ( + <div + className={className} + style={style} + /> + ) + } +} diff --git a/ui/app/components/modals/account-modal-container.js b/ui/app/components/modals/account-modal-container.js index aa0593df8..2a6c655e1 100644 --- a/ui/app/components/modals/account-modal-container.js +++ b/ui/app/components/modals/account-modal-container.js @@ -5,7 +5,7 @@ const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') const { getSelectedIdentity } = require('../../selectors') -const Identicon = require('../identicon') +import Identicon from '../identicon' function mapStateToProps (state, ownProps) { return { diff --git a/ui/app/components/modals/hide-token-confirmation-modal.js b/ui/app/components/modals/hide-token-confirmation-modal.js index fb38516d3..43f3009a5 100644 --- a/ui/app/components/modals/hide-token-confirmation-modal.js +++ b/ui/app/components/modals/hide-token-confirmation-modal.js @@ -4,7 +4,7 @@ const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect const actions = require('../../actions') -const Identicon = require('../identicon') +import Identicon from '../identicon' function mapStateToProps (state) { return { diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 15ca9deaa..338229a28 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -27,7 +27,6 @@ import TransactionConfirmed from './transaction-confirmed' import ConfirmCustomizeGasModal from './customize-gas' import CancelTransaction from './cancel-transaction' import WelcomeBeta from './welcome-beta' -import TransactionDetails from './transaction-details' import RejectTransactions from './reject-transactions' const modalContainerBaseStyle = { @@ -366,19 +365,6 @@ const MODALS = { }, }, - TRANSACTION_DETAILS: { - contents: h(TransactionDetails), - mobileModalStyle: { - ...modalContainerMobileStyle, - }, - laptopModalStyle: { - ...modalContainerLaptopStyle, - }, - contentStyle: { - borderRadius: '8px', - }, - }, - REJECT_TRANSACTIONS: { contents: h(RejectTransactions), mobileModalStyle: { diff --git a/ui/app/components/modals/transaction-details/index.js b/ui/app/components/modals/transaction-details/index.js deleted file mode 100644 index 1fc42c662..000000000 --- a/ui/app/components/modals/transaction-details/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './transaction-details.container' diff --git a/ui/app/components/modals/transaction-details/transaction-details.component.js b/ui/app/components/modals/transaction-details/transaction-details.component.js deleted file mode 100644 index f2fec3409..000000000 --- a/ui/app/components/modals/transaction-details/transaction-details.component.js +++ /dev/null @@ -1,54 +0,0 @@ -import React, { PureComponent } from 'react' -import PropTypes from 'prop-types' -import Modal from '../../modal' -import TransactionListItemDetails from '../../transaction-list-item-details' -import { hexToDecimal } from '../../../helpers/conversions.util' - -export default class TransactionConfirmed extends PureComponent { - static contextTypes = { - t: PropTypes.func, - } - - static propTypes = { - hideModal: PropTypes.func, - transaction: PropTypes.object, - onRetry: PropTypes.func, - showRetry: PropTypes.bool, - onCancel: PropTypes.func, - showCancel: PropTypes.bool, - } - - handleSubmit = () => { - this.props.hideModal() - } - - handleRetry = () => { - const { onRetry, hideModal } = this.props - - Promise.resolve(onRetry()).then(() => hideModal()) - } - - render () { - const { t } = this.context - const { transaction, showRetry, onCancel, showCancel } = this.props - const { txParams: { nonce } = {} } = transaction - const decimalNonce = nonce && hexToDecimal(nonce) - - return ( - <Modal - onSubmit={this.handleSubmit} - onClose={this.handleSubmit} - submitText={t('ok')} - headerText={t('transactionWithNonce', [`#${decimalNonce}`])} - > - <TransactionListItemDetails - transaction={transaction} - onRetry={this.handleRetry} - showRetry={showRetry} - onCancel={() => onCancel()} - showCancel={showCancel} - /> - </Modal> - ) - } -} diff --git a/ui/app/components/modals/transaction-details/transaction-details.container.js b/ui/app/components/modals/transaction-details/transaction-details.container.js deleted file mode 100644 index f212920bb..000000000 --- a/ui/app/components/modals/transaction-details/transaction-details.container.js +++ /dev/null @@ -1,4 +0,0 @@ -import TransactionDetails from './transaction-details.component' -import withModalProps from '../../../higher-order-components/with-modal-props' - -export default withModalProps(TransactionDetails) diff --git a/ui/app/components/network-display/network-display.component.js b/ui/app/components/network-display/network-display.component.js index 38626af20..82f9ff9c3 100644 --- a/ui/app/components/network-display/network-display.component.js +++ b/ui/app/components/network-display/network-display.component.js @@ -41,7 +41,7 @@ export default class NetworkDisplay extends Component { } render () { - const { network, provider: { type } } = this.props + const { network, provider: { type, nickname } } = this.props const networkClass = networkToClassHash[network] return ( @@ -61,7 +61,7 @@ export default class NetworkDisplay extends Component { /> } <div className="network-display__name"> - { this.context.t(type) } + { type === 'rpc' && nickname ? nickname : this.context.t(type) } </div> </div> ) diff --git a/ui/app/components/network.js b/ui/app/components/network.js index 83297c4f2..611aadb7b 100644 --- a/ui/app/components/network.js +++ b/ui/app/components/network.js @@ -23,9 +23,10 @@ Network.prototype.render = function () { const props = this.props const context = this.context const networkNumber = props.network - let providerName + let providerName, providerNick try { providerName = props.provider.type + providerNick = props.provider.nickname || '' } catch (e) { providerName = null } @@ -131,7 +132,7 @@ Network.prototype.render = function () { }, }), - h('.network-name', context.t('privateNetwork')), + h('.network-name', providerNick || context.t('privateNetwork')), h('i.fa.fa-chevron-down.fa-lg.network-caret'), ]) } diff --git a/ui/app/components/pages/settings/settings-tab/index.scss b/ui/app/components/pages/settings/settings-tab/index.scss index 3bf840c86..ef32b0e4c 100644 --- a/ui/app/components/pages/settings/settings-tab/index.scss +++ b/ui/app/components/pages/settings/settings-tab/index.scss @@ -5,12 +5,9 @@ color: $crimson; } - &__rpc-save-button { - align-self: flex-end; - padding: 5px; - text-transform: uppercase; - color: $dusty-gray; - cursor: pointer; + &__advanced-link { + color: $curious-blue; + padding-left: 5px; } &__rpc-save-button { @@ -19,6 +16,9 @@ text-transform: uppercase; color: $dusty-gray; cursor: pointer; + width: 25%; + min-width: 80px; + height: 33px; } &__button--red { diff --git a/ui/app/components/pages/settings/settings-tab/settings-tab.component.js b/ui/app/components/pages/settings/settings-tab/settings-tab.component.js index a9e2a723e..cd81491a8 100644 --- a/ui/app/components/pages/settings/settings-tab/settings-tab.component.js +++ b/ui/app/components/pages/settings/settings-tab/settings-tab.component.js @@ -55,12 +55,17 @@ export default class SettingsTab extends PureComponent { sendHexData: PropTypes.bool, currentCurrency: PropTypes.string, conversionDate: PropTypes.number, - useETHAsPrimaryCurrency: PropTypes.bool, - setUseETHAsPrimaryCurrencyPreference: PropTypes.func, + nativeCurrency: PropTypes.string, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, + setUseNativeCurrencyAsPrimaryCurrencyPreference: PropTypes.func, } state = { newRpc: '', + chainId: '', + showOptions: false, + ticker: '', + nickname: '', } renderCurrentConversion () { @@ -121,37 +126,98 @@ export default class SettingsTab extends PureComponent { renderNewRpcUrl () { const { t } = this.context - const { newRpc } = this.state + const { newRpc, chainId, ticker, nickname } = this.state return ( <div className="settings-page__content-row"> <div className="settings-page__content-item"> - <span>{ t('newRPC') }</span> + <span>{ t('newNetwork') }</span> </div> <div className="settings-page__content-item"> <div className="settings-page__content-item-col"> <TextField type="text" id="new-rpc" - placeholder={t('newRPC')} + placeholder={t('rpcURL')} value={newRpc} onChange={e => this.setState({ newRpc: e.target.value })} onKeyPress={e => { if (e.key === 'Enter') { - this.validateRpc(newRpc) + this.validateRpc(newRpc, chainId, ticker, nickname) } }} fullWidth - margin="none" + margin="dense" /> - <div - className="settings-tab__rpc-save-button" - onClick={e => { - e.preventDefault() - this.validateRpc(newRpc) + <TextField + type="text" + id="chainid" + placeholder={t('optionalChainId')} + value={chainId} + onChange={e => this.setState({ chainId: e.target.value })} + onKeyPress={e => { + if (e.key === 'Enter') { + this.validateRpc(newRpc, chainId, ticker, nickname) + } }} - > - { t('save') } + style={{ + display: this.state.showOptions ? null : 'none', + }} + fullWidth + margin="dense" + /> + <TextField + type="text" + id="ticker" + placeholder={t('optionalSymbol')} + value={ticker} + onChange={e => this.setState({ ticker: e.target.value })} + onKeyPress={e => { + if (e.key === 'Enter') { + this.validateRpc(newRpc, chainId, ticker, nickname) + } + }} + style={{ + display: this.state.showOptions ? null : 'none', + }} + fullWidth + margin="dense" + /> + <TextField + type="text" + id="nickname" + placeholder={t('optionalNickname')} + value={nickname} + onChange={e => this.setState({ nickname: e.target.value })} + onKeyPress={e => { + if (e.key === 'Enter') { + this.validateRpc(newRpc, chainId, ticker, nickname) + } + }} + style={{ + display: this.state.showOptions ? null : 'none', + }} + fullWidth + margin="dense" + /> + <div className="flex-row flex-align-center space-between"> + <span className="settings-tab__advanced-link" + onClick={e => { + e.preventDefault() + this.setState({ showOptions: !this.state.showOptions }) + }} + > + { t(this.state.showOptions ? 'hideAdvancedOptions' : 'showAdvancedOptions') } + </span> + <button + className="button btn-primary settings-tab__rpc-save-button" + onClick={e => { + e.preventDefault() + this.validateRpc(newRpc, chainId, ticker, nickname) + }} + > + { t('save') } + </button> </div> </div> </div> @@ -159,11 +225,11 @@ export default class SettingsTab extends PureComponent { ) } - validateRpc (newRpc) { + validateRpc (newRpc, chainId, ticker = 'ETH', nickname) { const { setRpcTarget, displayWarning } = this.props if (validUrl.isWebUri(newRpc)) { - setRpcTarget(newRpc) + setRpcTarget(newRpc, chainId, ticker, nickname) } else { const appendedRpc = `http://${newRpc}` @@ -341,9 +407,13 @@ export default class SettingsTab extends PureComponent { ) } - renderUseEthAsPrimaryCurrency () { + renderUsePrimaryCurrencyOptions () { const { t } = this.context - const { useETHAsPrimaryCurrency, setUseETHAsPrimaryCurrencyPreference } = this.props + const { + nativeCurrency, + setUseNativeCurrencyAsPrimaryCurrencyPreference, + useNativeCurrencyAsPrimaryCurrency, + } = this.props return ( <div className="settings-page__content-row"> @@ -359,23 +429,23 @@ export default class SettingsTab extends PureComponent { <div className="settings-tab__radio-button"> <input type="radio" - id="eth-primary-currency" - onChange={() => setUseETHAsPrimaryCurrencyPreference(true)} - checked={Boolean(useETHAsPrimaryCurrency)} + id="native-primary-currency" + onChange={() => setUseNativeCurrencyAsPrimaryCurrencyPreference(true)} + checked={Boolean(useNativeCurrencyAsPrimaryCurrency)} /> <label - htmlFor="eth-primary-currency" + htmlFor="native-primary-currency" className="settings-tab__radio-label" > - { t('eth') } + { nativeCurrency } </label> </div> <div className="settings-tab__radio-button"> <input type="radio" id="fiat-primary-currency" - onChange={() => setUseETHAsPrimaryCurrencyPreference(false)} - checked={!useETHAsPrimaryCurrency} + onChange={() => setUseNativeCurrencyAsPrimaryCurrencyPreference(false)} + checked={!useNativeCurrencyAsPrimaryCurrency} /> <label htmlFor="fiat-primary-currency" @@ -398,7 +468,7 @@ export default class SettingsTab extends PureComponent { <div className="settings-page__content"> { warning && <div className="settings-tab__error">{ warning }</div> } { this.renderCurrentConversion() } - { this.renderUseEthAsPrimaryCurrency() } + { this.renderUsePrimaryCurrencyOptions() } { this.renderCurrentLocale() } { this.renderNewRpcUrl() } { this.renderStateLogs() } diff --git a/ui/app/components/pages/settings/settings-tab/settings-tab.container.js b/ui/app/components/pages/settings/settings-tab/settings-tab.container.js index de30f309c..f4110207e 100644 --- a/ui/app/components/pages/settings/settings-tab/settings-tab.container.js +++ b/ui/app/components/pages/settings/settings-tab/settings-tab.container.js @@ -11,7 +11,7 @@ import { updateCurrentLocale, setFeatureFlag, showModal, - setUseETHAsPrimaryCurrencyPreference, + setUseNativeCurrencyAsPrimaryCurrencyPreference, } from '../../../../actions' import { preferencesSelector } from '../../../../selectors' @@ -20,13 +20,14 @@ const mapStateToProps = state => { const { currentCurrency, conversionDate, + nativeCurrency, useBlockie, featureFlags: { sendHexData } = {}, provider = {}, isMascara, currentLocale, } = metamask - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { warning, @@ -34,17 +35,18 @@ const mapStateToProps = state => { currentLocale, currentCurrency, conversionDate, + nativeCurrency, useBlockie, sendHexData, provider, - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, } } const mapDispatchToProps = dispatch => { return { setCurrentCurrency: currency => dispatch(setCurrentCurrency(currency)), - setRpcTarget: newRpc => dispatch(setRpcTarget(newRpc)), + setRpcTarget: (newRpc, chainId, ticker, nickname) => dispatch(setRpcTarget(newRpc, chainId, ticker, nickname)), displayWarning: warning => dispatch(displayWarning(warning)), revealSeedConfirmation: () => dispatch(revealSeedConfirmation()), setUseBlockie: value => dispatch(setUseBlockie(value)), @@ -54,8 +56,8 @@ const mapDispatchToProps = dispatch => { }, setHexDataFeatureFlag: shouldShow => dispatch(setFeatureFlag('sendHexData', shouldShow)), showResetAccountConfirmationModal: () => dispatch(showModal({ name: 'CONFIRM_RESET_ACCOUNT' })), - setUseETHAsPrimaryCurrencyPreference: value => { - return dispatch(setUseETHAsPrimaryCurrencyPreference(value)) + setUseNativeCurrencyAsPrimaryCurrencyPreference: value => { + return dispatch(setUseNativeCurrencyAsPrimaryCurrencyPreference(value)) }, } } diff --git a/ui/app/components/send/account-list-item/account-list-item.container.js b/ui/app/components/send/account-list-item/account-list-item.container.js index 4b4519288..f8e73d923 100644 --- a/ui/app/components/send/account-list-item/account-list-item.container.js +++ b/ui/app/components/send/account-list-item/account-list-item.container.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux' import { getConversionRate, getCurrentCurrency, + getNativeCurrency, } from '../send.selectors.js' import AccountListItem from './account-list-item.component' @@ -11,5 +12,6 @@ function mapStateToProps (state) { return { conversionRate: getConversionRate(state), currentCurrency: getCurrentCurrency(state), + nativeCurrency: getNativeCurrency(state), } } diff --git a/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js b/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js index f88c0dbd0..6ffc0b1c6 100644 --- a/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js +++ b/ui/app/components/send/account-list-item/tests/account-list-item-component.test.js @@ -28,6 +28,7 @@ describe('AccountListItem Component', function () { className={'mockClassName'} conversionRate={4} currentCurrency={'mockCurrentyCurrency'} + nativeCurrency={'ETH'} displayAddress={false} displayBalance={false} handleClick={propsMethodSpies.handleClick} diff --git a/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js b/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js index af0859117..7c2f5fcb2 100644 --- a/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js +++ b/ui/app/components/send/account-list-item/tests/account-list-item-container.test.js @@ -13,6 +13,7 @@ proxyquire('../account-list-item.container.js', { '../send.selectors.js': { getConversionRate: (s) => `mockConversionRate:${s}`, getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`, + getNativeCurrency: (s) => `mockNativeCurrency:${s}`, }, }) @@ -24,6 +25,7 @@ describe('account-list-item container', () => { assert.deepEqual(mapStateToProps('mockState'), { conversionRate: 'mockConversionRate:mockState', currentCurrency: 'mockCurrentCurrency:mockState', + nativeCurrency: 'mockNativeCurrency:mockState', }) }) diff --git a/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js index e63db4a2d..56e80cb83 100644 --- a/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js +++ b/ui/app/components/send/send-content/send-amount-row/tests/send-amount-row-component.test.js @@ -151,7 +151,6 @@ describe('SendAmountRow Component', function () { }) it('should render a UserPreferencedTokenInput as the second child of the SendRowWrapper', () => { - console.log('HI', wrapper.find(SendRowWrapper).childAt(1)) assert(wrapper.find(SendRowWrapper).childAt(1).is(UserPreferencedTokenInput)) }) diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index 22e379693..eb22a08b7 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -19,6 +19,7 @@ const selectors = { getCurrentNetwork, getCurrentViewContext, getForceGasMin, + getNativeCurrency, getGasLimit, getGasPrice, getGasPriceFromRecentBlocks, @@ -111,6 +112,10 @@ function getCurrentCurrency (state) { return state.metamask.currentCurrency } +function getNativeCurrency (state) { + return state.metamask.nativeCurrency +} + function getCurrentNetwork (state) { return state.metamask.network } diff --git a/ui/app/components/send/tests/send-selectors-test-data.js b/ui/app/components/send/tests/send-selectors-test-data.js index 8b939dadb..30a2666cf 100644 --- a/ui/app/components/send/tests/send-selectors-test-data.js +++ b/ui/app/components/send/tests/send-selectors-test-data.js @@ -26,6 +26,7 @@ module.exports = { 'currentCurrency': 'USD', 'conversionRate': 1200.88200327, 'conversionDate': 1489013762, + 'nativeCurrency': 'ETH', 'noActiveNotices': true, 'frequentRpcList': [], 'network': '3', diff --git a/ui/app/components/send/tests/send-selectors.test.js b/ui/app/components/send/tests/send-selectors.test.js index 1a47cd209..e7e901f0d 100644 --- a/ui/app/components/send/tests/send-selectors.test.js +++ b/ui/app/components/send/tests/send-selectors.test.js @@ -12,6 +12,7 @@ const { getCurrentCurrency, getCurrentNetwork, getCurrentViewContext, + getNativeCurrency, getForceGasMin, getGasLimit, getGasPrice, @@ -178,6 +179,15 @@ describe('send selectors', () => { }) }) + describe('getNativeCurrency()', () => { + it('should return the ticker symbol of the selected network', () => { + assert.equal( + getNativeCurrency(mockState), + 'ETH' + ) + }) + }) + describe('getCurrentNetwork()', () => { it('should return the id of the currently selected network', () => { assert.equal( diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js index b87bf959e..0461b615a 100644 --- a/ui/app/components/shift-list-item.js +++ b/ui/app/components/shift-list-item.js @@ -52,12 +52,12 @@ ShiftListItem.prototype.render = function () { }, }, [ h('img', { - src: 'https://info.shapeshift.io/sites/default/files/logo.png', + src: 'https://shapeshift.io/logo.png', style: { height: '35px', width: '132px', position: 'absolute', - clip: 'rect(0px,23px,34px,0px)', + clip: 'rect(0px,30px,34px,0px)', }, }), ]), @@ -132,7 +132,6 @@ ShiftListItem.prototype.renderInfo = function () { case 'no_deposits': return h('.flex-column', { style: { - width: '200px', overflow: 'hidden', }, }, [ diff --git a/ui/app/components/signature-request.js b/ui/app/components/signature-request.js index d76eb5ef8..85af3b00b 100644 --- a/ui/app/components/signature-request.js +++ b/ui/app/components/signature-request.js @@ -2,7 +2,7 @@ const Component = require('react').Component const PropTypes = require('prop-types') const h = require('react-hyperscript') const inherits = require('util').inherits -const Identicon = require('./identicon') +import Identicon from './identicon' const connect = require('react-redux').connect const ethUtil = require('ethereumjs-util') const classnames = require('classnames') diff --git a/ui/app/components/token-cell.js b/ui/app/components/token-cell.js index 477d97597..75ba347fa 100644 --- a/ui/app/components/token-cell.js +++ b/ui/app/components/token-cell.js @@ -2,7 +2,7 @@ const Component = require('react').Component const h = require('react-hyperscript') const inherits = require('util').inherits const connect = require('react-redux').connect -const Identicon = require('./identicon') +import Identicon from './identicon' const prefixForNetwork = require('../../lib/etherscan-prefix-for-network') const selectors = require('../selectors') const actions = require('../actions') diff --git a/ui/app/components/token-input/tests/token-input.component.test.js b/ui/app/components/token-input/tests/token-input.component.test.js index 2131e7705..2dacb9bc4 100644 --- a/ui/app/components/token-input/tests/token-input.component.test.js +++ b/ui/app/components/token-input/tests/token-input.component.test.js @@ -122,7 +122,7 @@ describe('TokenInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '2 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '2ETH') }) it('should render properly with a token value for fiat', () => { @@ -157,7 +157,7 @@ describe('TokenInput Component', () => { assert.equal(wrapper.find('.unit-input__suffix').length, 1) assert.equal(wrapper.find('.unit-input__suffix').text(), 'ABC') assert.equal(wrapper.find('.unit-input__input').props().value, '1') - assert.equal(wrapper.find('.currency-display-component').text(), '$462.12 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$462.12USD') }) }) @@ -201,14 +201,14 @@ describe('TokenInput Component', () => { const tokenInputInstance = wrapper.find(TokenInput).at(0).instance() assert.equal(tokenInputInstance.state.decimalValue, 0) assert.equal(tokenInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '0 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '0ETH') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('2710')) - assert.equal(wrapper.find('.currency-display-component').text(), '2 ETH') + assert.equal(wrapper.find('.currency-display-component').text(), '2ETH') assert.equal(tokenInputInstance.state.decimalValue, 1) assert.equal(tokenInputInstance.state.hexValue, '2710') @@ -250,14 +250,14 @@ describe('TokenInput Component', () => { const tokenInputInstance = wrapper.find(TokenInput).at(0).instance() assert.equal(tokenInputInstance.state.decimalValue, 0) assert.equal(tokenInputInstance.state.hexValue, undefined) - assert.equal(wrapper.find('.currency-display-component').text(), '$0.00 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$0.00USD') const input = wrapper.find('input') assert.equal(input.props().value, 0) input.simulate('change', { target: { value: 1 } }) assert.equal(handleChangeSpy.callCount, 1) assert.ok(handleChangeSpy.calledWith('2710')) - assert.equal(wrapper.find('.currency-display-component').text(), '$462.12 USD') + assert.equal(wrapper.find('.currency-display-component').text(), '$462.12USD') assert.equal(tokenInputInstance.state.decimalValue, 1) assert.equal(tokenInputInstance.state.hexValue, '2710') diff --git a/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js index 85d56a6a2..a7c35f51e 100644 --- a/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js +++ b/ui/app/components/transaction-activity-log/tests/transaction-activity-log.container.test.js @@ -18,10 +18,11 @@ describe('TransactionActivityLog container', () => { const mockState = { metamask: { conversionRate: 280.45, + nativeCurrency: 'ETH', }, } - assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45 }) + assert.deepEqual(mapStateToProps(mockState), { conversionRate: 280.45, nativeCurrency: 'ETH' }) }) }) }) diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js index c4cf57d14..58d932a0f 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.component.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.component.js @@ -4,7 +4,6 @@ import classnames from 'classnames' import { getActivities } from './transaction-activity-log.util' import Card from '../card' import { getEthConversionFromWeiHex, getValueFromWeiHex } from '../../helpers/conversions.util' -import { ETH } from '../../constants/common' import { formatDate } from '../../util' export default class TransactionActivityLog extends PureComponent { @@ -16,6 +15,7 @@ export default class TransactionActivityLog extends PureComponent { transaction: PropTypes.object, className: PropTypes.string, conversionRate: PropTypes.number, + nativeCurrency: PropTypes.string, } state = { @@ -27,10 +27,14 @@ export default class TransactionActivityLog extends PureComponent { } componentDidUpdate (prevProps) { - const { transaction: { history: prevHistory = [] } = {} } = prevProps - const { transaction: { history = [] } = {} } = this.props + const { + transaction: { history: prevHistory = [], txReceipt: { status: prevStatus } = {} } = {}, + } = prevProps + const { + transaction: { history = [], txReceipt: { status } = {} } = {}, + } = this.props - if (prevHistory.length !== history.length) { + if (prevHistory.length !== history.length || prevStatus !== status) { this.setActivites() } } @@ -41,16 +45,17 @@ export default class TransactionActivityLog extends PureComponent { } renderActivity (activity, index) { - const { conversionRate } = this.props + const { conversionRate, nativeCurrency } = this.props const { eventKey, value, timestamp } = activity const ethValue = index === 0 ? `${getValueFromWeiHex({ value, - toCurrency: ETH, + fromCurrency: nativeCurrency, + toCurrency: nativeCurrency, conversionRate, numberOfDecimals: 6, - })} ${ETH}` - : getEthConversionFromWeiHex({ value, toCurrency: ETH, conversionRate }) + })} ${nativeCurrency}` + : getEthConversionFromWeiHex({ value, fromCurrency: nativeCurrency, conversionRate }) const formattedTimestamp = formatDate(timestamp) const activityText = this.context.t(eventKey, [ethValue, formattedTimestamp]) diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.container.js b/ui/app/components/transaction-activity-log/transaction-activity-log.container.js index 4c8b6d971..622f77df1 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.container.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.container.js @@ -1,10 +1,11 @@ import { connect } from 'react-redux' import TransactionActivityLog from './transaction-activity-log.component' -import { conversionRateSelector } from '../../selectors' +import { conversionRateSelector, getNativeCurrency } from '../../selectors' const mapStateToProps = state => { return { conversionRate: conversionRateSelector(state), + nativeCurrency: getNativeCurrency(state), } } diff --git a/ui/app/components/transaction-activity-log/transaction-activity-log.util.js b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js index 97aa9a8f1..16597ae1a 100644 --- a/ui/app/components/transaction-activity-log/transaction-activity-log.util.js +++ b/ui/app/components/transaction-activity-log/transaction-activity-log.util.js @@ -18,6 +18,7 @@ const TRANSACTION_SUBMITTED_EVENT = 'transactionSubmitted' const TRANSACTION_CONFIRMED_EVENT = 'transactionConfirmed' const TRANSACTION_DROPPED_EVENT = 'transactionDropped' const TRANSACTION_UPDATED_EVENT = 'transactionUpdated' +const TRANSACTION_ERRORED_EVENT = 'transactionErrored' const eventPathsHash = { [STATUS_PATH]: true, @@ -39,9 +40,9 @@ function eventCreator (eventKey, timestamp, value) { } export function getActivities (transaction) { - const { history = [] } = transaction + const { history = [], txReceipt: { status } = {} } = transaction - return history.reduce((acc, base) => { + const historyActivities = history.reduce((acc, base) => { // First history item should be transaction creation if (!Array.isArray(base) && base.status === UNAPPROVED_STATUS && base.txParams) { const { time, txParams: { value } = {} } = base @@ -83,4 +84,10 @@ export function getActivities (transaction) { return acc }, []) + + // If txReceipt.status is '0x0', that means that an on-chain error occured for the transaction, + // so we add an error entry to the Activity Log. + return status === '0x0' + ? historyActivities.concat(eventCreator(TRANSACTION_ERRORED_EVENT)) + : historyActivities } diff --git a/ui/app/components/transaction-breakdown/index.js b/ui/app/components/transaction-breakdown/index.js index c887f504f..4a5b52663 100644 --- a/ui/app/components/transaction-breakdown/index.js +++ b/ui/app/components/transaction-breakdown/index.js @@ -1 +1 @@ -export { default } from './transaction-breakdown.component' +export { default } from './transaction-breakdown.container' diff --git a/ui/app/components/transaction-breakdown/transaction-breakdown.component.js b/ui/app/components/transaction-breakdown/transaction-breakdown.component.js index 77bedcad7..3a7647873 100644 --- a/ui/app/components/transaction-breakdown/transaction-breakdown.component.js +++ b/ui/app/components/transaction-breakdown/transaction-breakdown.component.js @@ -6,7 +6,7 @@ import Card from '../card' import CurrencyDisplay from '../currency-display' import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display' import HexToDecimal from '../hex-to-decimal' -import { ETH, GWEI, PRIMARY, SECONDARY } from '../../constants/common' +import { GWEI, PRIMARY, SECONDARY } from '../../constants/common' import { getHexGasTotal } from '../../helpers/confirm-transaction/util' import { sumHexes } from '../../helpers/transactions.util' @@ -18,6 +18,7 @@ export default class TransactionBreakdown extends PureComponent { static propTypes = { transaction: PropTypes.object, className: PropTypes.string, + nativeCurrency: PropTypes.string.isRequired, } static defaultProps = { @@ -26,7 +27,7 @@ export default class TransactionBreakdown extends PureComponent { render () { const { t } = this.context - const { transaction, className } = this.props + const { transaction, className, nativeCurrency } = this.props const { txParams: { gas, gasPrice, value } = {}, txReceipt: { gasUsed } = {} } = transaction const gasLimit = typeof gasUsed === 'string' ? gasUsed : gas @@ -72,7 +73,7 @@ export default class TransactionBreakdown extends PureComponent { <TransactionBreakdownRow title={t('gasPrice')}> <CurrencyDisplay className="transaction-breakdown__value" - currency={ETH} + currency={nativeCurrency} denomination={GWEI} value={gasPrice} hideLabel diff --git a/ui/app/components/transaction-breakdown/transaction-breakdown.container.js b/ui/app/components/transaction-breakdown/transaction-breakdown.container.js new file mode 100644 index 000000000..ed2708e03 --- /dev/null +++ b/ui/app/components/transaction-breakdown/transaction-breakdown.container.js @@ -0,0 +1,11 @@ +import { connect } from 'react-redux' +import TransactionBreakdown from './transaction-breakdown.component' +import { getNativeCurrency } from '../../selectors' + +const mapStateToProps = (state) => { + return { + nativeCurrency: getNativeCurrency(state), + } +} + +export default connect(mapStateToProps)(TransactionBreakdown) diff --git a/ui/app/components/transaction-list-item/index.scss b/ui/app/components/transaction-list-item/index.scss index 9d694546b..ac0e7beeb 100644 --- a/ui/app/components/transaction-list-item/index.scss +++ b/ui/app/components/transaction-list-item/index.scss @@ -85,6 +85,7 @@ text-align: end; grid-area: primary-amount; align-self: end; + justify-self: end; @media screen and (max-width: $break-small) { padding-bottom: 2px; @@ -97,6 +98,7 @@ color: #5e6064; grid-area: secondary-amount; align-self: start; + justify-self: end; } } diff --git a/ui/app/components/transaction-list-item/transaction-list-item.component.js b/ui/app/components/transaction-list-item/transaction-list-item.component.js index 88573d2d5..696634fe0 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.component.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.component.js @@ -10,7 +10,6 @@ import TransactionListItemDetails from '../transaction-list-item-details' import { CONFIRM_TRANSACTION_ROUTE } from '../../routes' import { UNAPPROVED_STATUS, TOKEN_METHOD_TRANSFER } from '../../constants/transactions' import { PRIMARY, SECONDARY } from '../../constants/common' -import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../app/scripts/lib/enums' import { getStatusKey } from '../../helpers/transactions.util' export default class TransactionListItem extends PureComponent { @@ -24,7 +23,6 @@ export default class TransactionListItem extends PureComponent { showCancelModal: PropTypes.func, showCancel: PropTypes.bool, showRetry: PropTypes.bool, - showTransactionDetailsModal: PropTypes.func, token: PropTypes.object, tokenData: PropTypes.object, transaction: PropTypes.object, @@ -39,31 +37,16 @@ export default class TransactionListItem extends PureComponent { const { transaction, history, - showTransactionDetailsModal, - methodData, - showCancel, - showRetry, } = this.props const { id, status } = transaction const { showTransactionDetails } = this.state - const windowType = window.METAMASK_UI_TYPE if (status === UNAPPROVED_STATUS) { history.push(`${CONFIRM_TRANSACTION_ROUTE}/${id}`) return } - if (windowType === ENVIRONMENT_TYPE_FULLSCREEN) { - this.setState({ showTransactionDetails: !showTransactionDetails }) - } else { - showTransactionDetailsModal({ - transaction, - onRetry: this.handleRetry, - showRetry: showRetry && methodData.done, - onCancel: this.handleCancel, - showCancel, - }) - } + this.setState({ showTransactionDetails: !showTransactionDetails }) } handleCancel = () => { diff --git a/ui/app/components/transaction-list-item/transaction-list-item.container.js b/ui/app/components/transaction-list-item/transaction-list-item.container.js index 72f5f5d61..62ed7a73f 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.container.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.container.js @@ -28,16 +28,6 @@ const mapDispatchToProps = dispatch => { showCancelModal: (transactionId, originalGasPrice) => { return dispatch(showModal({ name: 'CANCEL_TRANSACTION', transactionId, originalGasPrice })) }, - showTransactionDetailsModal: ({ transaction, onRetry, showRetry, onCancel, showCancel }) => { - return dispatch(showModal({ - name: 'TRANSACTION_DETAILS', - transaction, - onRetry, - showRetry, - onCancel, - showCancel, - })) - }, } } diff --git a/ui/app/components/transaction-list/index.scss b/ui/app/components/transaction-list/index.scss index 777f701f9..ba7ffd87b 100644 --- a/ui/app/components/transaction-list/index.scss +++ b/ui/app/components/transaction-list/index.scss @@ -2,9 +2,7 @@ display: flex; flex-direction: column; flex: 1; - overflow-y: hidden; margin-top: 8px; - border-top: 1px solid $geyser; &__completed-transactions { display: flex; @@ -26,7 +24,6 @@ &__transactions { flex: 1; - overflow-y: auto; } &__pending-transactions { diff --git a/ui/app/components/transaction-view-balance/index.scss b/ui/app/components/transaction-view-balance/index.scss index 12045ab6d..659f896ff 100644 --- a/ui/app/components/transaction-view-balance/index.scss +++ b/ui/app/components/transaction-view-balance/index.scss @@ -4,11 +4,13 @@ align-items: center; flex: 1; height: 54px; + min-width: 0; &__balance { - margin-left: 12px; + margin: 0 12px; display: flex; flex-direction: column; + min-width: 0; @media screen and (max-width: $break-small) { align-items: center; @@ -21,7 +23,8 @@ font-size: 1.5rem; @media screen and (max-width: $break-small) { - margin-bottom: 12px; + margin: 12px 0; + margin-left: 0; font-size: 1.75rem; } } @@ -30,7 +33,6 @@ font-size: 1.5rem; @media screen and (max-width: $break-small) { - margin-bottom: 12px; font-size: 1.75rem; } } @@ -45,6 +47,7 @@ display: flex; flex-direction: row; align-items: center; + min-width: 0; @media screen and (max-width: $break-small) { flex-direction: column; diff --git a/ui/app/components/transaction-view-balance/transaction-view-balance.container.js b/ui/app/components/transaction-view-balance/transaction-view-balance.container.js index 30c5cab16..cb8078ec1 100644 --- a/ui/app/components/transaction-view-balance/transaction-view-balance.container.js +++ b/ui/app/components/transaction-view-balance/transaction-view-balance.container.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux' import { withRouter } from 'react-router-dom' import { compose } from 'recompose' import TransactionViewBalance from './transaction-view-balance.component' -import { getSelectedToken, getSelectedAddress, getSelectedTokenAssetImage } from '../../selectors' +import { getSelectedToken, getSelectedAddress, getNativeCurrency, getSelectedTokenAssetImage } from '../../selectors' import { showModal } from '../../actions' const mapStateToProps = state => { @@ -15,6 +15,7 @@ const mapStateToProps = state => { selectedToken: getSelectedToken(state), network, balance, + nativeCurrency: getNativeCurrency(state), assetImage: getSelectedTokenAssetImage(state), } } diff --git a/ui/app/components/transaction-view/index.scss b/ui/app/components/transaction-view/index.scss index af9771ce0..13187f0e5 100644 --- a/ui/app/components/transaction-view/index.scss +++ b/ui/app/components/transaction-view/index.scss @@ -4,6 +4,7 @@ min-width: 0; display: flex; flex-direction: column; + overflow-y: auto; &__balance-wrapper { @media screen and (max-width: $break-small) { diff --git a/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js b/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js index 41ad3b73e..ba1c23d83 100644 --- a/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js +++ b/ui/app/components/user-preferenced-currency-display/tests/user-preferenced-currency-display.container.test.js @@ -18,14 +18,16 @@ describe('UserPreferencedCurrencyDisplay container', () => { it('should return the correct props', () => { const mockState = { metamask: { + nativeCurrency: 'ETH', preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, } assert.deepEqual(mapStateToProps(mockState), { - useETHAsPrimaryCurrency: true, + nativeCurrency: 'ETH', + useNativeCurrencyAsPrimaryCurrency: true, }) }) }) @@ -37,33 +39,38 @@ describe('UserPreferencedCurrencyDisplay container', () => { const tests = [ { stateProps: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, + nativeCurrency: 'ETH', }, ownProps: { type: 'PRIMARY', }, result: { currency: 'ETH', + nativeCurrency: 'ETH', numberOfDecimals: 6, prefix: undefined, }, }, { stateProps: { - useETHAsPrimaryCurrency: false, + useNativeCurrencyAsPrimaryCurrency: false, + nativeCurrency: 'ETH', }, ownProps: { type: 'PRIMARY', }, result: { currency: undefined, + nativeCurrency: 'ETH', numberOfDecimals: 2, prefix: undefined, }, }, { stateProps: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, + nativeCurrency: 'ETH', }, ownProps: { type: 'SECONDARY', @@ -71,6 +78,7 @@ describe('UserPreferencedCurrencyDisplay container', () => { fiatPrefix: '-', }, result: { + nativeCurrency: 'ETH', currency: undefined, numberOfDecimals: 4, prefix: '-', @@ -78,7 +86,8 @@ describe('UserPreferencedCurrencyDisplay container', () => { }, { stateProps: { - useETHAsPrimaryCurrency: false, + useNativeCurrencyAsPrimaryCurrency: false, + nativeCurrency: 'ETH', }, ownProps: { type: 'SECONDARY', @@ -89,6 +98,7 @@ describe('UserPreferencedCurrencyDisplay container', () => { }, result: { currency: 'ETH', + nativeCurrency: 'ETH', numberOfDecimals: 3, prefix: 'b', }, diff --git a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js index 4d948ca6a..f2a834ea7 100644 --- a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js +++ b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.component.js @@ -21,6 +21,7 @@ export default class UserPreferencedCurrencyDisplay extends PureComponent { fiatPrefix: PropTypes.string, // From container currency: PropTypes.string, + nativeCurrency: PropTypes.string, } renderEthLogo () { diff --git a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js index 23240c649..7999301ad 100644 --- a/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js +++ b/ui/app/components/user-preferenced-currency-display/user-preferenced-currency-display.container.js @@ -4,15 +4,16 @@ import { preferencesSelector } from '../../selectors' import { ETH, PRIMARY, SECONDARY } from '../../constants/common' const mapStateToProps = (state, ownProps) => { - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, + nativeCurrency: state.metamask.nativeCurrency, } } const mergeProps = (stateProps, dispatchProps, ownProps) => { - const { useETHAsPrimaryCurrency, ...restStateProps } = stateProps + const { useNativeCurrencyAsPrimaryCurrency, nativeCurrency, ...restStateProps } = stateProps const { type, numberOfDecimals: propsNumberOfDecimals, @@ -26,14 +27,14 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { let currency, numberOfDecimals, prefix - if (type === PRIMARY && useETHAsPrimaryCurrency || - type === SECONDARY && !useETHAsPrimaryCurrency) { + if (type === PRIMARY && useNativeCurrencyAsPrimaryCurrency || + type === SECONDARY && !useNativeCurrencyAsPrimaryCurrency) { // Display ETH - currency = ETH + currency = nativeCurrency || ETH numberOfDecimals = propsNumberOfDecimals || ethNumberOfDecimals || 6 prefix = propsPrefix || ethPrefix - } else if (type === SECONDARY && useETHAsPrimaryCurrency || - type === PRIMARY && !useETHAsPrimaryCurrency) { + } else if (type === SECONDARY && useNativeCurrencyAsPrimaryCurrency || + type === PRIMARY && !useNativeCurrencyAsPrimaryCurrency) { // Display Fiat numberOfDecimals = propsNumberOfDecimals || fiatNumberOfDecimals || 2 prefix = propsPrefix || fiatPrefix @@ -43,6 +44,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { ...restStateProps, ...dispatchProps, ...restOwnProps, + nativeCurrency, currency, numberOfDecimals, prefix, diff --git a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js index 0af80a03d..710b5d519 100644 --- a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js +++ b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.component.test.js @@ -15,17 +15,17 @@ describe('UserPreferencedCurrencyInput Component', () => { assert.equal(wrapper.find(CurrencyInput).length, 1) }) - it('should render useFiat for CurrencyInput based on preferences.useETHAsPrimaryCurrency', () => { + it('should render useFiat for CurrencyInput based on preferences.useNativeCurrencyAsPrimaryCurrency', () => { const wrapper = shallow( <UserPreferencedCurrencyInput - useETHAsPrimaryCurrency + useNativeCurrencyAsPrimaryCurrency /> ) assert.ok(wrapper) assert.equal(wrapper.find(CurrencyInput).length, 1) assert.equal(wrapper.find(CurrencyInput).props().useFiat, false) - wrapper.setProps({ useETHAsPrimaryCurrency: false }) + wrapper.setProps({ useNativeCurrencyAsPrimaryCurrency: false }) assert.equal(wrapper.find(CurrencyInput).props().useFiat, true) }) }) diff --git a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js index d860c38da..959726443 100644 --- a/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js +++ b/ui/app/components/user-preferenced-currency-input/tests/user-preferenced-currency-input.container.test.js @@ -18,13 +18,13 @@ describe('UserPreferencedCurrencyInput container', () => { const mockState = { metamask: { preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, } assert.deepEqual(mapStateToProps(mockState), { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }) }) }) diff --git a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js index 6e0e00a1d..463e66b80 100644 --- a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js +++ b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.component.js @@ -4,16 +4,16 @@ import CurrencyInput from '../currency-input' export default class UserPreferencedCurrencyInput extends PureComponent { static propTypes = { - useETHAsPrimaryCurrency: PropTypes.bool, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, } render () { - const { useETHAsPrimaryCurrency, ...restProps } = this.props + const { useNativeCurrencyAsPrimaryCurrency, ...restProps } = this.props return ( <CurrencyInput {...restProps} - useFiat={!useETHAsPrimaryCurrency} + useFiat={!useNativeCurrencyAsPrimaryCurrency} /> ) } diff --git a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js index 397cdc7cc..0b88eb5a7 100644 --- a/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js +++ b/ui/app/components/user-preferenced-currency-input/user-preferenced-currency-input.container.js @@ -3,10 +3,10 @@ import UserPreferencedCurrencyInput from './user-preferenced-currency-input.comp import { preferencesSelector } from '../../selectors' const mapStateToProps = state => { - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, } } diff --git a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js index 910c7089f..d85bddeeb 100644 --- a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js +++ b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.component.test.js @@ -15,17 +15,17 @@ describe('UserPreferencedCurrencyInput Component', () => { assert.equal(wrapper.find(TokenInput).length, 1) }) - it('should render showFiat for TokenInput based on preferences.useETHAsPrimaryCurrency', () => { + it('should render showFiat for TokenInput based on preferences.useNativeCurrencyAsPrimaryCurrency', () => { const wrapper = shallow( <UserPreferencedTokenInput - useETHAsPrimaryCurrency + useNativeCurrencyAsPrimaryCurrency /> ) assert.ok(wrapper) assert.equal(wrapper.find(TokenInput).length, 1) assert.equal(wrapper.find(TokenInput).props().showFiat, false) - wrapper.setProps({ useETHAsPrimaryCurrency: false }) + wrapper.setProps({ useNativeCurrencyAsPrimaryCurrency: false }) assert.equal(wrapper.find(TokenInput).props().showFiat, true) }) }) diff --git a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js index e3509149a..2f89fba90 100644 --- a/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js +++ b/ui/app/components/user-preferenced-token-input/tests/user-preferenced-token-input.container.test.js @@ -18,13 +18,13 @@ describe('UserPreferencedTokenInput container', () => { const mockState = { metamask: { preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, } assert.deepEqual(mapStateToProps(mockState), { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }) }) }) diff --git a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js index f2b537f11..8f14231ac 100644 --- a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js +++ b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.component.js @@ -4,16 +4,16 @@ import TokenInput from '../token-input' export default class UserPreferencedTokenInput extends PureComponent { static propTypes = { - useETHAsPrimaryCurrency: PropTypes.bool, + useNativeCurrencyAsPrimaryCurrency: PropTypes.bool, } render () { - const { useETHAsPrimaryCurrency, ...restProps } = this.props + const { useNativeCurrencyAsPrimaryCurrency, ...restProps } = this.props return ( <TokenInput {...restProps} - showFiat={!useETHAsPrimaryCurrency} + showFiat={!useNativeCurrencyAsPrimaryCurrency} /> ) } diff --git a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js index 416d069dd..3305d4e29 100644 --- a/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js +++ b/ui/app/components/user-preferenced-token-input/user-preferenced-token-input.container.js @@ -3,10 +3,10 @@ import UserPreferencedTokenInput from './user-preferenced-token-input.component' import { preferencesSelector } from '../../selectors' const mapStateToProps = state => { - const { useETHAsPrimaryCurrency } = preferencesSelector(state) + const { useNativeCurrencyAsPrimaryCurrency } = preferencesSelector(state) return { - useETHAsPrimaryCurrency, + useNativeCurrencyAsPrimaryCurrency, } } diff --git a/ui/app/components/wallet-view.js b/ui/app/components/wallet-view.js index 8a7cb0f8d..e050e0ee6 100644 --- a/ui/app/components/wallet-view.js +++ b/ui/app/components/wallet-view.js @@ -7,7 +7,7 @@ const { compose } = require('recompose') const inherits = require('util').inherits const classnames = require('classnames') const { checksumAddress } = require('../util') -const Identicon = require('./identicon') +import Identicon from './identicon' // const AccountDropdowns = require('./dropdowns/index.js').AccountDropdowns const Tooltip = require('./tooltip-v2.js').default const copyToClipboard = require('copy-to-clipboard') @@ -127,7 +127,6 @@ WalletView.prototype.render = function () { identities, } = this.props // temporary logs + fake extra wallets - // console.log('walletview, selectedAccount:', selectedAccount) const checksummedAddress = checksumAddress(selectedAddress) diff --git a/ui/app/css/itcss/components/buttons.scss b/ui/app/css/itcss/components/buttons.scss index 655188a3e..5826a8b49 100644 --- a/ui/app/css/itcss/components/buttons.scss +++ b/ui/app/css/itcss/components/buttons.scss @@ -3,7 +3,7 @@ */ .button { - height: 44px; + min-height: 44px; background: $white; display: flex; justify-content: center; @@ -82,12 +82,13 @@ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.08); padding: 6px; height: initial; + min-height: initial; width: initial; min-width: initial; } .btn--large { - height: 54px; + min-height: 54px; } .btn-green { diff --git a/ui/app/css/itcss/components/request-signature.scss b/ui/app/css/itcss/components/request-signature.scss index 445b9ebf5..6c950d846 100644 --- a/ui/app/css/itcss/components/request-signature.scss +++ b/ui/app/css/itcss/components/request-signature.scss @@ -19,7 +19,7 @@ } @media screen and (min-width: $break-large) { - max-height: 620px; + height: 620px; } } diff --git a/ui/app/css/itcss/components/wallet-balance.scss b/ui/app/css/itcss/components/wallet-balance.scss index 293771550..3c3349ae0 100644 --- a/ui/app/css/itcss/components/wallet-balance.scss +++ b/ui/app/css/itcss/components/wallet-balance.scss @@ -29,7 +29,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and ( align-items: center; margin: 20px 24px; flex-direction: row; - flex-grow: 3; + min-width: 0; @media #{$wallet-balance-breakpoint-range} { margin: 10% 4%; @@ -38,8 +38,7 @@ $wallet-balance-breakpoint-range: "screen and (min-width: #{$break-large}) and ( .balance-display { margin-left: 15px; - justify-content: flex-start; - align-items: flex-start; + min-width: 0; .token-amount { font-size: 1.5rem; diff --git a/ui/app/ducks/confirm-transaction.duck.js b/ui/app/ducks/confirm-transaction.duck.js index 2ceafbe08..275eb1551 100644 --- a/ui/app/ducks/confirm-transaction.duck.js +++ b/ui/app/ducks/confirm-transaction.duck.js @@ -2,6 +2,7 @@ import { conversionRateSelector, currentCurrencySelector, unconfirmedTransactionsHashSelector, + getNativeCurrency, } from '../selectors/confirm-transaction' import { @@ -292,16 +293,17 @@ export function updateTxDataAndCalculate (txData) { const state = getState() const currentCurrency = currentCurrencySelector(state) const conversionRate = conversionRateSelector(state) + const nativeCurrency = getNativeCurrency(state) dispatch(updateTxData(txData)) const { txParams: { value = '0x0', gas: gasLimit = '0x0', gasPrice = '0x0' } = {} } = txData const fiatTransactionAmount = getValueFromWeiHex({ - value, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2, + value, fromCurrency: nativeCurrency, toCurrency: currentCurrency, conversionRate, numberOfDecimals: 2, }) const ethTransactionAmount = getValueFromWeiHex({ - value, toCurrency: 'ETH', conversionRate, numberOfDecimals: 6, + value, fromCurrency: nativeCurrency, toCurrency: nativeCurrency, conversionRate, numberOfDecimals: 6, }) dispatch(updateTransactionAmounts({ @@ -314,13 +316,15 @@ export function updateTxDataAndCalculate (txData) { const fiatTransactionFee = getTransactionFee({ value: hexTransactionFee, + fromCurrency: nativeCurrency, toCurrency: currentCurrency, numberOfDecimals: 2, conversionRate, }) const ethTransactionFee = getTransactionFee({ value: hexTransactionFee, - toCurrency: 'ETH', + fromCurrency: nativeCurrency, + toCurrency: nativeCurrency, numberOfDecimals: 6, conversionRate, }) @@ -329,7 +333,6 @@ export function updateTxDataAndCalculate (txData) { const fiatTransactionTotal = addFiat(fiatTransactionFee, fiatTransactionAmount) const ethTransactionTotal = addEth(ethTransactionFee, ethTransactionAmount) - console.log('HIHIH', value, hexTransactionFee) const hexTransactionTotal = sumHexes(value, hexTransactionFee) dispatch(updateTransactionTotals({ diff --git a/ui/app/helpers/confirm-transaction/util.js b/ui/app/helpers/confirm-transaction/util.js index bcac22500..eb334a4b8 100644 --- a/ui/app/helpers/confirm-transaction/util.js +++ b/ui/app/helpers/confirm-transaction/util.js @@ -55,6 +55,7 @@ export function addFiat (...args) { export function getValueFromWeiHex ({ value, + fromCurrency = 'ETH', toCurrency, conversionRate, numberOfDecimals, @@ -63,7 +64,7 @@ export function getValueFromWeiHex ({ return conversionUtil(value, { fromNumericBase: 'hex', toNumericBase: 'dec', - fromCurrency: 'ETH', + fromCurrency, toCurrency, numberOfDecimals, fromDenomination: 'WEI', @@ -74,6 +75,7 @@ export function getValueFromWeiHex ({ export function getTransactionFee ({ value, + fromCurrency = 'ETH', toCurrency, conversionRate, numberOfDecimals, @@ -82,7 +84,7 @@ export function getTransactionFee ({ fromNumericBase: 'BN', toNumericBase: 'dec', fromDenomination: 'WEI', - fromCurrency: 'ETH', + fromCurrency, toCurrency, numberOfDecimals, conversionRate, @@ -99,6 +101,7 @@ export function formatCurrency (value, currencyCode) { export function convertTokenToFiat ({ value, + fromCurrency = 'ETH', toCurrency, conversionRate, contractExchangeRate, @@ -108,6 +111,7 @@ export function convertTokenToFiat ({ return conversionUtil(value, { fromNumericBase: 'dec', toNumericBase: 'dec', + fromCurrency, toCurrency, numberOfDecimals: 2, conversionRate: totalExchangeRate, diff --git a/ui/app/helpers/conversions.util.js b/ui/app/helpers/conversions.util.js index 777537e1e..cb5e1b90b 100644 --- a/ui/app/helpers/conversions.util.js +++ b/ui/app/helpers/conversions.util.js @@ -20,8 +20,8 @@ export function decimalToHex (decimal) { }) } -export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDecimals = 6 }) { - const denominations = [ETH, GWEI, WEI] +export function getEthConversionFromWeiHex ({ value, fromCurrency = ETH, conversionRate, numberOfDecimals = 6 }) { + const denominations = [fromCurrency, GWEI, WEI] let nonZeroDenomination @@ -29,7 +29,8 @@ export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDec const convertedValue = getValueFromWeiHex({ value, conversionRate, - toCurrency: ETH, + fromCurrency, + toCurrency: fromCurrency, numberOfDecimals, toDenomination: denominations[i], }) @@ -45,6 +46,7 @@ export function getEthConversionFromWeiHex ({ value, conversionRate, numberOfDec export function getValueFromWeiHex ({ value, + fromCurrency = ETH, toCurrency, conversionRate, numberOfDecimals, @@ -53,7 +55,7 @@ export function getValueFromWeiHex ({ return conversionUtil(value, { fromNumericBase: 'hex', toNumericBase: 'dec', - fromCurrency: ETH, + fromCurrency, toCurrency, numberOfDecimals, fromDenomination: WEI, diff --git a/ui/app/helpers/transactions.util.js b/ui/app/helpers/transactions.util.js index e50196196..64ec82225 100644 --- a/ui/app/helpers/transactions.util.js +++ b/ui/app/helpers/transactions.util.js @@ -27,10 +27,21 @@ export function getTokenData (data = '') { const registry = new MethodRegistry({ provider: global.ethereumProvider }) +/** + * Attempts to return the method data from the MethodRegistry library, if the method exists in the + * registry. Otherwise, returns an empty object. + * @param {string} data - The hex data (@code txParams.data) of a transaction + * @returns {Object} + */ export async function getMethodData (data = '') { const prefixedData = ethUtil.addHexPrefix(data) const fourBytePrefix = prefixedData.slice(0, 10) const sig = await registry.lookup(fourBytePrefix) + + if (!sig) { + return {} + } + const parsedResult = registry.parse(sig) return { diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js index 37d8a9187..22fa53098 100644 --- a/ui/app/reducers/metamask.js +++ b/ui/app/reducers/metamask.js @@ -52,7 +52,7 @@ function reduceMetamask (state, action) { welcomeScreenSeen: false, currentLocale: '', preferences: { - useETHAsPrimaryCurrency: true, + useNativeCurrencyAsPrimaryCurrency: true, }, }, state.metamask) diff --git a/ui/app/selectors.js b/ui/app/selectors.js index 9f11551be..7209f19d1 100644 --- a/ui/app/selectors.js +++ b/ui/app/selectors.js @@ -26,6 +26,7 @@ const selectors = { getAddressBook, getSendFrom, getCurrentCurrency, + getNativeCurrency, getSendAmount, getSelectedTokenToFiatRate, getSelectedTokenContract, @@ -143,6 +144,10 @@ function getCurrentCurrency (state) { return state.metamask.currentCurrency } +function getNativeCurrency (state) { + return state.metamask.nativeCurrency +} + function getSelectedTokenToFiatRate (state) { const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state) const conversionRate = conversionRateSelector(state) diff --git a/ui/app/selectors/confirm-transaction.js b/ui/app/selectors/confirm-transaction.js index 86b10bac3..90924c036 100644 --- a/ui/app/selectors/confirm-transaction.js +++ b/ui/app/selectors/confirm-transaction.js @@ -93,6 +93,7 @@ export const unconfirmedTransactionsCountSelector = createSelector( export const currentCurrencySelector = state => state.metamask.currentCurrency export const conversionRateSelector = state => state.metamask.conversionRate +export const getNativeCurrency = state => state.metamask.nativeCurrency const txDataSelector = state => state.confirmTransaction.txData const tokenDataSelector = state => state.confirmTransaction.tokenData diff --git a/ui/app/util.js b/ui/app/util.js index 37c0fb698..b19a028cc 100644 --- a/ui/app/util.js +++ b/ui/app/util.js @@ -128,7 +128,7 @@ function parseBalance (balance) { // Takes wei hex, returns an object with three properties. // Its "formatted" property is what we generally use to render values. -function formatBalance (balance, decimalsToKeep, needsParse = true) { +function formatBalance (balance, decimalsToKeep, needsParse = true, ticker = 'ETH') { var parsed = needsParse ? parseBalance(balance) : balance.split('.') var beforeDecimal = parsed[0] var afterDecimal = parsed[1] @@ -138,14 +138,14 @@ function formatBalance (balance, decimalsToKeep, needsParse = true) { if (afterDecimal !== '0') { var sigFigs = afterDecimal.match(/^0*(.{2})/) // default: grabs 2 most significant digits if (sigFigs) { afterDecimal = sigFigs[0] } - formatted = '0.' + afterDecimal + ' ETH' + formatted = '0.' + afterDecimal + ` ${ticker}` } } else { - formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ' ETH' + formatted = beforeDecimal + '.' + afterDecimal.slice(0, 3) + ` ${ticker}` } } else { afterDecimal += Array(decimalsToKeep).join('0') - formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ' ETH' + formatted = beforeDecimal + '.' + afterDecimal.slice(0, decimalsToKeep) + ` ${ticker}` } return formatted } |