aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--app/_locales/it/messages.json819
-rw-r--r--app/_locales/ph/messages.json609
-rw-r--r--app/scripts/controllers/transactions.js53
-rw-r--r--app/scripts/lib/tx-state-manager.js42
-rw-r--r--development/states/confirm-new-ui.json2
-rw-r--r--development/states/send-edit.json2
-rw-r--r--gulpfile.js6
-rw-r--r--mascara/src/app/first-time/backup-phrase-screen.js10
-rw-r--r--old-ui/app/components/transaction-list-item.js18
-rw-r--r--old-ui/app/components/transaction-list.js2
-rw-r--r--old-ui/app/css/index.css4
-rw-r--r--package.json6
-rw-r--r--test/integration/lib/send-new-ui.js10
-rw-r--r--test/lib/react-trigger-change.js161
-rw-r--r--test/unit/components/binary-renderer-test.js2
-rw-r--r--test/unit/components/bn-as-decimal-input-test.js2
-rw-r--r--test/unit/nameForAccount_test.js2
-rw-r--r--test/unit/tx-controller-test.js45
-rw-r--r--ui/app/account-detail.js117
-rw-r--r--ui/app/actions.js4
-rw-r--r--ui/app/components/balance.js89
-rw-r--r--ui/app/components/binary-renderer.js46
-rw-r--r--ui/app/components/currency-input.js20
-rw-r--r--ui/app/components/customize-gas-modal/index.js31
-rw-r--r--ui/app/components/dropdowns/account-options-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/account-selection-dropdown.js29
-rw-r--r--ui/app/components/dropdowns/index.js6
-rw-r--r--ui/app/components/mini-account-panel.js74
-rw-r--r--ui/app/components/pending-personal-msg-details.js60
-rw-r--r--ui/app/components/pending-tx/confirm-send-ether.js90
-rw-r--r--ui/app/components/pending-tx/confirm-send-token.js89
-rw-r--r--ui/app/components/pending-typed-msg-details.js60
-rw-r--r--ui/app/components/pending-typed-msg.js47
-rw-r--r--ui/app/components/range-slider.js58
-rw-r--r--ui/app/components/send-token/index.js440
-rw-r--r--ui/app/components/send/currency-toggle.js44
-rw-r--r--ui/app/components/send/eth-fee-display.js37
-rw-r--r--ui/app/components/send/gas-fee-display-v2.js11
-rw-r--r--ui/app/components/send/gas-fee-display.js62
-rw-r--r--ui/app/components/send/send-v2-container.js1
-rw-r--r--ui/app/components/send/usd-fee-display.js35
-rw-r--r--ui/app/components/template.js18
-rw-r--r--ui/app/components/transaction-list-item-icon.js68
-rw-r--r--ui/app/components/transaction-list-item.js239
-rw-r--r--ui/app/components/transaction-list.js87
-rw-r--r--ui/app/components/tx-list-item.js70
-rw-r--r--ui/app/components/tx-list.js13
-rw-r--r--ui/app/components/typed-message-renderer.js42
-rw-r--r--ui/app/components/wallet-content-display.js56
-rw-r--r--ui/app/conversion-util.js13
-rw-r--r--ui/app/css/itcss/components/account-menu.scss3
-rw-r--r--ui/app/css/itcss/components/network.scss3
-rw-r--r--ui/app/css/itcss/components/newui-sections.scss1
-rw-r--r--ui/app/css/itcss/components/send.scss27
-rw-r--r--ui/app/css/itcss/components/transaction-list.scss55
-rw-r--r--ui/app/css/itcss/generic/index.scss2
-rw-r--r--ui/app/css/itcss/generic/reset.scss4
-rw-r--r--ui/app/css/itcss/settings/variables.scss1
-rw-r--r--ui/app/info.js154
-rw-r--r--ui/app/reducers/metamask.js2
-rw-r--r--ui/app/selectors.js5
-rw-r--r--ui/app/send-v2.js9
-rw-r--r--ui/app/send.js547
-rw-r--r--ui/app/template.js30
-rw-r--r--ui/app/token-tracker.js0
-rw-r--r--ui/lib/contract-namer.js33
67 files changed, 2134 insertions, 2623 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7d4a09fe..75ba7670f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
## Current Master
+- MetaMask will no longer allow nonces to be specified by the dapp
- Add ability for internationalization.
- Will now throw an error if the `to` field in txParams is not valid.
- Will strip null values from the `to` field.
diff --git a/app/_locales/it/messages.json b/app/_locales/it/messages.json
new file mode 100644
index 000000000..f54ef98ca
--- /dev/null
+++ b/app/_locales/it/messages.json
@@ -0,0 +1,819 @@
+{
+ "accept": {
+ "message": "Accetta"
+ },
+ "account": {
+ "message": "Account"
+ },
+ "accountDetails": {
+ "message": "Dettagli Account"
+ },
+ "accountName": {
+ "message": "Nome Account"
+ },
+ "address": {
+ "message": "Indirizzo"
+ },
+ "addCustomToken": {
+ "message": "Aggiungi un token personalizzato"
+ },
+ "addToken": {
+ "message": "Aggiungi Token"
+ },
+ "addTokens": {
+ "message": "Aggiungi più token"
+ },
+ "amount": {
+ "message": "Importo"
+ },
+ "amountPlusGas": {
+ "message": "Importo + Gas"
+ },
+ "appDescription": {
+ "message": "Ethereum Browser Extension",
+ "description": "La descrizione dell'applicazione"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "Il nome dell'applicazione"
+ },
+ "attemptingConnect": {
+ "message": "Tentativo di connessione alla blockchain."
+ },
+ "attributions": {
+ "message": "Attribuzioni"
+ },
+ "available": {
+ "message": "Disponibile"
+ },
+ "back": {
+ "message": "Indietro"
+ },
+ "balance": {
+ "message": "Bilancio:"
+ },
+ "balances": {
+ "message": "I tuoi bilanci"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Bilancio insufficiente per il gas totale corrente"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "deve essere maggiore o uguale a $1 e minore o uguale a $2.",
+ "description": "aiuto per inserire un input esadecimale come decimale"
+ },
+ "blockiesIdenticon": {
+ "message": "Usa le icone Blockie"
+ },
+ "borrowDharma": {
+ "message": "Prendi in presisito con Dharma (Beta)"
+ },
+ "builtInCalifornia": {
+ "message": "MetaMask è progettato e costruito in California."
+ },
+ "buy": {
+ "message": "Compra"
+ },
+ "buyCoinbase": {
+ "message": "Compra su Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase è il servizio più popolare al mondo per comprare e vendere bitcoin, ethereum e litecoin."
+ },
+ "cancel": {
+ "message": "Cancella"
+ },
+ "classicInterface": {
+ "message": "Usa l'interfaccia classica"
+ },
+ "clickCopy": {
+ "message": "Clicca per Copiare"
+ },
+ "confirm": {
+ "message": "Conferma"
+ },
+ "confirmContract": {
+ "message": "Conferma Contratto"
+ },
+ "confirmPassword": {
+ "message": "Conferma Password"
+ },
+ "confirmTransaction": {
+ "message": "Conferma Transazione"
+ },
+ "continue": {
+ "message": "Continua"
+ },
+ "continueToCoinbase": {
+ "message": "Continua su Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Distribuzione Contratto"
+ },
+ "conversionProgress": {
+ "message": "Conversione in corso"
+ },
+ "copiedButton": {
+ "message": "Copiato"
+ },
+ "copiedClipboard": {
+ "message": "Copiato negli Appunti"
+ },
+ "copiedExclamation": {
+ "message": "Copiato!"
+ },
+ "copiedSafe": {
+ "message": "Le ho copiate in un posto sicuro"
+ },
+ "copy": {
+ "message": "Copia"
+ },
+ "copyToClipboard": {
+ "message": "Copia negli appunti"
+ },
+ "copyButton": {
+ "message": " Copia "
+ },
+ "copyPrivateKey": {
+ "message": "Questa è la tua chiave privata (clicca per copiare)"
+ },
+ "create": {
+ "message": "Crea"
+ },
+ "createAccount": {
+ "message": "Crea Account"
+ },
+ "createDen": {
+ "message": "Crea"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Tipo di exchange (cryptomonete)"
+ },
+ "currentConversion": {
+ "message": "Cambio Corrente"
+ },
+ "currentNetwork": {
+ "message": "Rete Corrente"
+ },
+ "customGas": {
+ "message": "Personalizza Gas"
+ },
+ "customize": {
+ "message": "Personalizza"
+ },
+ "customRPC": {
+ "message": "RPC Personalizzata"
+ },
+ "decimalsMustZerotoTen": {
+ "message": "Il numero di decimali deve essere almeno 0, e non oltre 36."
+ },
+ "decimal": {
+ "message": "Precisione Decimali"
+ },
+ "defaultNetwork": {
+ "message": "La rete predefinita per transazioni in Ether è la Rete Ethereum Principale."
+ },
+ "denExplainer": {
+ "message": "Il DEN è il tuo archivio crittato con password dentro Metamask."
+ },
+ "deposit": {
+ "message": "Deposita"
+ },
+ "depositBTC": {
+ "message": "Deposita i tuoi BTC all'indirizzo sotto:"
+ },
+ "depositCoin": {
+ "message": "Deposita $1 all'indirizzo sotto",
+ "description": "Dice all'utente quale moneta ha selezionato per depositare con Shapeshift"
+ },
+ "depositEth": {
+ "message": "Deposita Eth"
+ },
+ "depositEther": {
+ "message": "Deposita Ether"
+ },
+ "depositFiat": {
+ "message": "Deposita con moneta Fiat"
+ },
+ "depositFromAccount": {
+ "message": "Deposita da un altro account"
+ },
+ "depositShapeShift": {
+ "message": "Deposita con ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Se possiedi altre criptomonete, puoi scambiare e depositare Ether direttamente nel tuo portafoglio MetaMask. Nessun account richiesto."
+ },
+ "details": {
+ "message": "Dettagli"
+ },
+ "directDeposit": {
+ "message": "Deposito Diretto"
+ },
+ "directDepositEther": {
+ "message": "Deposita Direttamente Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Se possiedi già degli Ether, questa è la via più veloce per aggiungere Ether al tuo portafoglio con un deposito diretto."
+ },
+ "done": {
+ "message": "Finito"
+ },
+ "downloadStatelogs": {
+ "message": "Scarica i log di Stato"
+ },
+ "edit": {
+ "message": "Modifica"
+ },
+ "editAccountName": {
+ "message": "Modifica Nome Account"
+ },
+ "emailUs": {
+ "message": "Mandaci una mail!"
+ },
+ "encryptNewDen": {
+ "message": "Cripta il tuo nuovo DEN"
+ },
+ "enterPassword": {
+ "message": "Inserisci password"
+ },
+ "enterPasswordConfirm": {
+ "message": "Inserisci la tua password per confermare"
+ },
+ "etherscanView": {
+ "message": "Vedi account su Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Tasso di cambio"
+ },
+ "exportPrivateKey": {
+ "message": "Esporta Chiave Privata"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Esporta chiave privata a tuo rischio."
+ },
+ "failed": {
+ "message": "Fallito"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Tipo di scambio"
+ },
+ "fileImportFail": {
+ "message": "Importazione file non funziona? Clicca qui!",
+ "description": "Aiuta gli utenti a importare il loro account da un file JSON"
+ },
+ "followTwitter": {
+ "message": "Seguici su Twitter"
+ },
+ "from": {
+ "message": "Da"
+ },
+ "fromToSame": {
+ "message": "Gli indirizzi Da e A non possono essere uguali"
+ },
+ "fromShapeShift": {
+ "message": "Da ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Piccola indicazione del costo del gas"
+ },
+ "gasFee": {
+ "message": "Costo Gas"
+ },
+ "gasLimit": {
+ "message": "Gas Limite"
+ },
+ "gasLimitCalculation": {
+ "message": "Calcoliamo il gas limite suggerito in base al successo delle transazioni in rete."
+ },
+ "gasLimitRequired": {
+ "message": "Gas Limite Richiesto"
+ },
+ "gasLimitTooLow": {
+ "message": "Il Gas Limite deve essere almeno 21000"
+ },
+ "generatingSeed": {
+ "message": "Generando la frase seed..."
+ },
+ "gasPrice": {
+ "message": "Prezzo del Gas (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Calcoliamo il gas limite suggerito in base al successo delle transazioni in rete."
+ },
+ "gasPriceRequired": {
+ "message": "Prezzo Gas Richiesto"
+ },
+ "getEther": {
+ "message": "Ottieni Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Ottieni Get Ether da un faucet per $1",
+ "description": "Visualizza il nome della rete per il faucet Ether"
+ },
+ "greaterThanMin": {
+ "message": "deve essere maggiore o uguale a $1.",
+ "description": "aiuto per inserire un input esadecimale come decimale"
+ },
+ "here": {
+ "message": "qui",
+ "description": "per intendere -clicca qui- per maggiori informazioni (va con troubleTokenBalances)"
+ },
+ "hereList": {
+ "message": "Questa è una lista!!!!"
+ },
+ "hide": {
+ "message": "Nascondi"
+ },
+ "hideToken": {
+ "message": "Nascondi Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Nascondi Token?"
+ },
+ "howToDeposit": {
+ "message": "Come vuoi depositare Ether?"
+ },
+ "holdEther": {
+ "message": "Ti permette di tenere ether & token, e serve da ponte per le applicazioni decentralizzate."
+ },
+ "import": {
+ "message": "Importa",
+ "description": "Tasto per importare un account da un file selezionato"
+ },
+ "importAccount": {
+ "message": "Importa Account"
+ },
+ "importAccountMsg": {
+ "message":" Gli account importati non saranno associati alla frase seed originariamente creata con MetaMask. Impara di più sugli account importati "
+ },
+ "importAnAccount": {
+ "message": "Importa un account"
+ },
+ "importDen": {
+ "message": "Importa un DEN Esistente"
+ },
+ "imported": {
+ "message": "Importato",
+ "description": "stato che conferma che un account è stato totalmente caricato nel portachiavi"
+ },
+ "infoHelp": {
+ "message": "Informazioni & Aiuto"
+ },
+ "insufficientFunds": {
+ "message": "Fondi non sufficienti."
+ },
+ "insufficientTokens": {
+ "message": "Token non sufficienti."
+ },
+ "invalidAddress": {
+ "message": "Indirizzo non valido"
+ },
+ "invalidAddressRecipient": {
+ "message": "Indirizzo destinatario invalido"
+ },
+ "invalidGasParams": {
+ "message": "Parametri del Gas non validi"
+ },
+ "invalidInput": {
+ "message": "Input non valido."
+ },
+ "invalidRequest": {
+ "message": "Richiesta non Valida"
+ },
+ "invalidRPC": {
+ "message": "URI RPC invalido"
+ },
+ "jsonFail": {
+ "message": "Qualcosa è andato storto. Assicurati che il file JSON sia formattato correttamente."
+ },
+ "jsonFile": {
+ "message": "File JSON",
+ "description": "formato per importare un account"
+ },
+ "kovan": {
+ "message": "Rete di test Kovan"
+ },
+ "knowledgeDataBase": {
+ "message": "Visita la nostra Knowledge Base"
+ },
+ "lessThanMax": {
+ "message": "deve essere minore o uguale a $1.",
+ "description": "aiuto per inserire un input esadecimale come decimale"
+ },
+ "likeToAddTokens": {
+ "message": "Vorresti aggiungere questi token?"
+ },
+ "limit": {
+ "message": "Limite"
+ },
+ "loading": {
+ "message": "Caricamento..."
+ },
+ "loadingTokens": {
+ "message": "Caricamento Tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "Connetti"
+ },
+ "logout": {
+ "message": "Disconnetti"
+ },
+ "loose": {
+ "message": "Libero"
+ },
+ "loweCaseWords": {
+ "message": "le frasi seed hanno solo lettere minuscole"
+ },
+ "mainnet": {
+ "message": "Rete Ethereum Principale"
+ },
+ "message": {
+ "message": "Messaggio"
+ },
+ "metamaskDescription": {
+ "message": "MetaMask è una cassaforte sicura per identità su Ethereum."
+ },
+ "min": {
+ "message": "Minimo"
+ },
+ "myAccounts": {
+ "message": "Account Miei"
+ },
+ "mustSelectOne": {
+ "message": "Devi selezionare almeno un token."
+ },
+ "needEtherInWallet": {
+ "message": "Per interagire con applicazioni decentralizzate con MetaMask, devi possedere Ether nel tuo portafoglio."
+ },
+ "needImportFile": {
+ "message": "Devi selezionare un file da importare.",
+ "description": "L'utente sta importando un account e deve aggiungere un file per continuare"
+ },
+ "needImportPassword": {
+ "message": "Dei inserire una password per il file selezionato.",
+ "description": "Password e file necessari per importare un account"
+ },
+ "negativeETH": {
+ "message": "Non puoi inviare una quantità di ETH negativa."
+ },
+ "networks": {
+ "message": "Reti"
+ },
+ "newAccount": {
+ "message": "Nuovo Account"
+ },
+ "newAccountNumberName": {
+ "message": "Account $1",
+ "description": "Nome predefinito per il prossimo account da creare nella schermata di creazione account"
+ },
+ "newContract": {
+ "message": "Nuovo Contratto"
+ },
+ "newPassword": {
+ "message": "Nuova Password (minimo 8 caratteri)"
+ },
+ "newRecipient": {
+ "message": "Nuovo Destinatario"
+ },
+ "newRPC": {
+ "message": "Nuovo URL RPC"
+ },
+ "next": {
+ "message": "Prossimo"
+ },
+ "noAddressForName": {
+ "message": "Nessun indirizzo è stato impostato per questo nome."
+ },
+ "noDeposits": {
+ "message": "Nessun deposito ricevuto"
+ },
+ "noTransactionHistory": {
+ "message": "Nessuna cronologia delle transazioni."
+ },
+ "noTransactions": {
+ "message": "Nessuna Transazione"
+ },
+ "notStarted": {
+ "message": "Non Iniziato"
+ },
+ "oldUI": {
+ "message": "Vecchia interfaccia"
+ },
+ "oldUIMessage": {
+ "message": "Sei ritornato alla vecchia interfaccia. Puoi ritornare alla nuova interfaccia tramite l'opzione nel menu a discesa in alto a destra."
+ },
+ "or": {
+ "message": "o",
+ "description": "scelta tra creare o importare un nuovo account"
+ },
+ "passwordCorrect": {
+ "message": "Assicurati che la password sia corretta."
+ },
+ "passwordMismatch": {
+ "message": "le password non corrispondono",
+ "description": "nella creazione della password, le due password all'interno dei campi non corrispondono"
+ },
+ "passwordShort": {
+ "message": "password non sufficientemente lunga",
+ "description": "nella creazione della password, la password non è lunga abbastanza"
+ },
+ "pastePrivateKey": {
+ "message": "Incolla la tua chiave privata qui:",
+ "description": "Per importare un account da una chiave privata"
+ },
+ "pasteSeed": {
+ "message": "Incolla la tua frase seed qui!"
+ },
+ "personalAddressDetected": {
+ "message": "Rilevato indirizzo personale. Inserisci l'indirizzo del contratto del token."
+ },
+ "pleaseReviewTransaction": {
+ "message": "Ricontrolla la tua transazione."
+ },
+ "privacyMsg": {
+ "message": "Politica sulla Privacy"
+ },
+ "privateKey": {
+ "message": "Chiave Privata",
+ "description": "seleziona questo tipo di file per importare un account"
+ },
+ "privateKeyWarning": {
+ "message": "Attenzione: non dire a nessuno questa chiave! Chiunque con la tua chiave privata può rubare qualsiasi moneta contenuta nel tuo account."
+ },
+ "privateNetwork": {
+ "message": "Rete Privata"
+ },
+ "qrCode": {
+ "message": "Mostra Codice QR"
+ },
+ "readdToken": {
+ "message": "Puoi aggiungere nuovamente questo token in futuro andando in “Aggiungi token” nel menu delle opzioni del tuo account."
+ },
+ "readMore": {
+ "message": "Leggi di più qui."
+ },
+ "readMore2": {
+ "message": "Leggi di più."
+ },
+ "receive": {
+ "message": "Ricevi"
+ },
+ "recipientAddress": {
+ "message": "Indirizzo Destinatario"
+ },
+ "refundAddress": {
+ "message": "Indirizzo di Rimborso"
+ },
+ "rejected": {
+ "message": "Respinta"
+ },
+ "resetAccount": {
+ "message": "Resetta Account"
+ },
+ "restoreFromSeed": {
+ "message": "Ripristina da una frase seed"
+ },
+ "required": {
+ "message": "Richiesto"
+ },
+ "retryWithMoreGas": {
+ "message": "Riprova con un prezzo del Gas maggiore qui"
+ },
+ "revealSeedWords": {
+ "message": "Rivela Frase Seed"
+ },
+ "revealSeedWordsWarning": {
+ "message": "Non ripristinare la tua frase seed in pubblico!. Queste parole possono essere usate per rubare il tuo account."
+ },
+ "revert": {
+ "message": "Annulla"
+ },
+ "rinkeby": {
+ "message": "Rete di test Rinkeby"
+ },
+ "ropsten": {
+ "message": "Rete di test Ropsten"
+ },
+ "sampleAccountName": {
+ "message": "Es: Il mio nuovo account",
+ "description": "Aiuta l'utente a capire il concetto di aggiungere un nome leggibile al loro account"
+ },
+ "save": {
+ "message": "Salva"
+ },
+ "saveAsFile": {
+ "message": "Salva come File",
+ "description": "Processo per esportare un account"
+ },
+ "saveSeedAsFile": {
+ "message": "Salva la Frase Seed come File"
+ },
+ "search": {
+ "message": "Cerca"
+ },
+ "secretPhrase": {
+ "message": "Inserisci la tua frase segreta di dodici parole per ripristinare la cassaforte."
+ },
+ "seedPhraseReq": {
+ "message": "le frasi seed sono lunghe 12 parole"
+ },
+ "select": {
+ "message": "Seleziona"
+ },
+ "selectCurrency": {
+ "message": "Seleziona Moneta"
+ },
+ "selectService": {
+ "message": "Seleziona Servizio"
+ },
+ "selectType": {
+ "message": "Seleziona Tipo"
+ },
+ "send": {
+ "message": "Invia"
+ },
+ "sendETH": {
+ "message": "Invia ETH"
+ },
+ "sendTokens": {
+ "message": "Invia Tokens"
+ },
+ "sendTokensAnywhere": {
+ "message": "Invia Tokens a chiunque abbia un account Ethereum"
+ },
+ "settings": {
+ "message": "Impostazioni"
+ },
+ "shapeshiftBuy": {
+ "message": "Compra con Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Mostra Chiave Privata"
+ },
+ "showQRCode": {
+ "message": "Mostra Codie QR"
+ },
+ "sign": {
+ "message": "Firma"
+ },
+ "signMessage": {
+ "message": "Firma Messaggio"
+ },
+ "signNotice": {
+ "message": "Firmare questo messaggio può avere effetti collaterali pericolosi. \nFirma messaggi da siti di cui ti fidi totalmente. \nQuesto metodo pericoloso sarà rimosso in versioni future."
+ },
+ "sigRequest": {
+ "message": "Firma Richiesta"
+ },
+ "sigRequested": {
+ "message": "Richiesta Firma"
+ },
+ "spaceBetween": {
+ "message": "ci può essere solo uno spazio tra le parole"
+ },
+ "status": {
+ "message": "Stato"
+ },
+ "stateLogs": {
+ "message": "Log di Stato"
+ },
+ "stateLogsDescription": {
+ "message": "I log di stato contengono i tuoi indirizzi pubblici e le transazioni effettuate."
+ },
+ "submit": {
+ "message": "Invia"
+ },
+ "supportCenter": {
+ "message": "Visita il nostro Centro di Supporto"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "Il simbolo deve essere lungo tra 0 e 10 caratteri."
+ },
+ "takesTooLong": {
+ "message": "Ci sta mettendo troppo?"
+ },
+ "terms": {
+ "message": "Termini di Uso"
+ },
+ "testFaucet": {
+ "message": "Prova Faucet"
+ },
+ "to": {
+ "message": "A"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 a ETH via ShapeShift",
+ "description": "il sistema riempirà il tipo di deposito all'inizio del messaggio"
+ },
+ "tokenAddress": {
+ "message": "Indirizzo Token"
+ },
+ "tokenAlreadyAdded": {
+ "message": "Il token è già stato aggiunto."
+ },
+ "tokenBalance": {
+ "message": "Bilancio Token:"
+ },
+ "tokenSelection": {
+ "message": "Cerca un token o seleziona dalla lista di token più popolari."
+ },
+ "tokenSymbol": {
+ "message": "Simbolo Token"
+ },
+ "tokenWarning1": {
+ "message": "Tieni traccia dei token che hai acquistato con il tuo account MetaMask. Se hai acquistato token con un account diverso, quei token non appariranno qui."
+ },
+ "total": {
+ "message": "Totale"
+ },
+ "transactions": {
+ "message": "transazioni"
+ },
+ "transactionMemo": {
+ "message": "Promemoria Transazione (opzionale)"
+ },
+ "transactionNumber": {
+ "message": "Numero Transazione"
+ },
+ "transfers": {
+ "message": "Trasferimenti"
+ },
+ "troubleTokenBalances": {
+ "message": "Abbiamo avuto un problema a caricare il bilancio dei tuoi token. Puoi vederlo ",
+ "description": "Seguito da un link (qui) per vedere il bilancio dei token"
+ },
+ "twelveWords": {
+ "message": "Queste 12 parole sono l'unico modo per ripristinare i tuoi account MetaMask. \nSalvale in un posto sicuro e segreto."
+ },
+ "typePassword": {
+ "message": "Inserisci Password"
+ },
+ "uiWelcome": {
+ "message": "Benvenuto alla nuova interfaccia (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Stai utilizzanto la nuova interfaccia di MetaMask. Guarda in giro, prova nuove funzionalità come inviare token, e facci sapere se hai dei problemi."
+ },
+ "unavailable": {
+ "message": "Non Disponibile"
+ },
+ "unknown": {
+ "message": "Sconosciuto"
+ },
+ "unknownNetwork": {
+ "message": "Rete Privata Sconosciuta"
+ },
+ "unknownNetworkId": {
+ "message": "ID rete sconosciuto"
+ },
+ "uriErrorMsg": {
+ "message": "Gli URI richiedono un prefisso HTTP/HTTPS."
+ },
+ "usaOnly": {
+ "message": "Solo USA",
+ "description": "Usare questo sito di scambio è possibile solo per persone residenti in USA."
+ },
+ "usedByClients": {
+ "message": "Usato da una varietà di clients diversi"
+ },
+ "useOldUI": {
+ "message": "Use la vecchia UI"
+ },
+ "validFileImport": {
+ "message": "Devi selezionare un file valido da importare."
+ },
+ "vaultCreated": {
+ "message": "Cassaforte Creata"
+ },
+ "viewAccount": {
+ "message": "Vedi Account"
+ },
+ "visitWebSite": {
+ "message": "Visita il nostro sito web"
+ },
+ "warning": {
+ "message": "Attenzione"
+ },
+ "welcomeBeta": {
+ "message": "Benvenuto nella Beta di MetaMask"
+ },
+ "whatsThis": {
+ "message": "Cos'è questo?"
+ },
+ "yourSigRequested": {
+ "message": "E' richiesta la tua firma"
+ },
+ "youSign": {
+ "message": "Ti stai connettendo"
+ }
+} \ No newline at end of file
diff --git a/app/_locales/ph/messages.json b/app/_locales/ph/messages.json
new file mode 100644
index 000000000..29d63be02
--- /dev/null
+++ b/app/_locales/ph/messages.json
@@ -0,0 +1,609 @@
+{
+ "accept": {
+ "message": "Tanggapin"
+ },
+ "account": {
+ "message": "Account"
+ },
+ "accountDetails": {
+ "message": "Detalye ng Account"
+ },
+ "accountName": {
+ "message": "Pangalan ng Account"
+ },
+ "address": {
+ "message": "Address"
+ },
+ "addToken": {
+ "message": "Magdagdag ng Token"
+ },
+ "amount": {
+ "message": "Halaga"
+ },
+ "amountPlusGas": {
+ "message": "Halaga + Gas"
+ },
+ "appDescription": {
+ "message": "Ethereum Browser Extension",
+ "description": "Ang deskripsyon ng application"
+ },
+ "appName": {
+ "message": "MetaMask",
+ "description": "Ang pangalan ng application"
+ },
+ "attemptingConnect": {
+ "message": "Sinusubukang kumonekta sa blockchain."
+ },
+ "available": {
+ "message": "Magagamit"
+ },
+ "back": {
+ "message": "Bumalik"
+ },
+ "balance": {
+ "message": "Balanse:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Kulang ang balanse para sa kasalukuyang gas total"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "dapat mas malaki o katumbas ng $1 at mas mababa o katumbas ng $2.",
+ "description": "helper para sa pag-input ng hex bilang decimal input"
+ },
+ "borrowDharma": {
+ "message": "Humiram sa Dharma (Beta)"
+ },
+ "buy": {
+ "message": "Bumili"
+ },
+ "buyCoinbase": {
+ "message": "Bumili sa Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Ang Coinbase ang pinakasikat na paraan upang bumili at magbenta ng bitcoin, ethereum, at litecoin sa buong mundo."
+ },
+ "cancel": {
+ "message": "Kanselahin"
+ },
+ "clickCopy": {
+ "message": "I-click upang Makopya"
+ },
+ "confirm": {
+ "message": "Tiyakin"
+ },
+ "confirmContract": {
+ "message": "Tiyakin ang Contract"
+ },
+ "confirmPassword": {
+ "message": "Tiyakin ang Password"
+ },
+ "confirmTransaction": {
+ "message": "Tiyakin ang Transaksyon"
+ },
+ "continueToCoinbase": {
+ "message": "Magpatuloy sa Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Pag-deploy ng Contract"
+ },
+ "conversionProgress": {
+ "message": "Isinasagawa ang conversion"
+ },
+ "copiedButton": {
+ "message": "Kinopya"
+ },
+ "copiedClipboard": {
+ "message": "Kinopya sa Clipboard"
+ },
+ "copiedExclamation": {
+ "message": "Kinopya!"
+ },
+ "copy": {
+ "message": "Kinopya"
+ },
+ "copyToClipboard": {
+ "message": "Kinopya sa clipboard"
+ },
+ "copyButton": {
+ "message": " Kinopya "
+ },
+ "copyPrivateKey": {
+ "message": "Ito ang iyong private key (i-click upang makopya)"
+ },
+ "create": {
+ "message": "Gumawa"
+ },
+ "createAccount": {
+ "message": "Gumawa ng Account"
+ },
+ "createDen": {
+ "message": "Gumawa"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Type ng exchange (cryptocurrencies)"
+ },
+ "customGas": {
+ "message": "I-customize ang Gas"
+ },
+ "customize": {
+ "message": "I-customize"
+ },
+ "customRPC": {
+ "message": "Custom RPC"
+ },
+ "defaultNetwork": {
+ "message": "Ang default network para sa Ether transactions ay ang Main Net."
+ },
+ "denExplainer": {
+ "message": "Ang iyong DEN ang nagsisilbing password-encrypted storage mo sa loob ng MetaMask."
+ },
+ "deposit": {
+ "message": "Deposito"
+ },
+ "depositBTC": {
+ "message": "I-deposito ang iyong BTC sa address na ito:"
+ },
+ "depositCoin": {
+ "message": "I-deposito ang iyong $1 sa address na ito",
+ "description": "Sinasabihan ang user kung ano ang coin na kanilang pinili para I-deposito gamit ang shapeshift"
+ },
+ "depositEth": {
+ "message": "I-deposito ang Eth"
+ },
+ "depositEther": {
+ "message": "I-deposito ang Ether"
+ },
+ "depositFiat": {
+ "message": "I-deposito ang Fiat"
+ },
+ "depositFromAccount": {
+ "message": "I-deposito mula sa ibang account"
+ },
+ "depositShapeShift": {
+ "message": "I-deposito gamit ang ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "Kung ikaw ay nagmamay-ari ng iba pang cryptocurrencies, pwede kang mag-trade at mag-deposito ng Ether diretso sa iyong MetaMask wallet. Hindi mo na kailangan ng account."
+ },
+ "details": {
+ "message": "Detalye"
+ },
+ "directDeposit": {
+ "message": "Direktang Deposito"
+ },
+ "directDepositEther": {
+ "message": "Direktang I-deposito ang Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "Kung ika ay mayroon nang Ether, ang pinakamabilis na paraan upang makuha ang Ether sa iyong bagong wallet ay sa pamamagitan ng direktang deposito."
+ },
+ "done": {
+ "message": "Tapos na"
+ },
+ "edit": {
+ "message": "I-edit"
+ },
+ "editAccountName": {
+ "message": "I-edit ang Pangalang ng Account"
+ },
+ "encryptNewDen": {
+ "message": "I-encrypt ang iyong bagong DEN"
+ },
+ "enterPassword": {
+ "message": "I-enter ang password"
+ },
+ "etherscanView": {
+ "message": "Tingnan ang account sa Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Exchange Rate"
+ },
+ "exportPrivateKey": {
+ "message": "I-export ang Private Key"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "I-export ang private keys at intindihin ang panganib na kasama nito."
+ },
+ "failed": {
+ "message": "Nabigo"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Type ng exchange"
+ },
+ "fileImportFail": {
+ "message": "Hindi gumagana ang file import? I-click ito!",
+ "description": "Tinutulungan ang user na i-import ang kanilang account mula sa JSON file"
+ },
+ "from": {
+ "message": "Mula sa"
+ },
+ "fromShapeShift": {
+ "message": "Mula sa ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Maikling indikasyon ng gas cost"
+ },
+ "gasFee": {
+ "message": "Gas Fee"
+ },
+ "gasLimit": {
+ "message": "Gas Limit"
+ },
+ "gasLimitCalculation": {
+ "message": "Kinalkula namin ang iminungkahing gas limit base sa network success rates."
+ },
+ "gasLimitRequired": {
+ "message": "Kailangan ang Gas Limit"
+ },
+ "gasLimitTooLow": {
+ "message": "Ang gas limit ay hindi dabat bababa sa 21000"
+ },
+ "gasPrice": {
+ "message": "Gas Price (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "Kinalkula namin ang iminungkahing gas prices base sa network success rates."
+ },
+ "gasPriceRequired": {
+ "message": "Kailangan ang Gas Price"
+ },
+ "getEther": {
+ "message": "Kumuha ng Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Kumuha ng Ether mula sa faucet para sa $1",
+ "description": "Ipinapakita ang pangalan ng network para sa Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "dapat mas malaki o katumbas ng $1.",
+ "description": "helper para sa pag-input ng hex bilang decimal input"
+ },
+ "here": {
+ "message": "i-click ito",
+ "description": "tulad ng -i-click dito- para sa mas maraming impormasyon (kasama ng troubleTokenBalances)"
+ },
+ "hide": {
+ "message": "Itago"
+ },
+ "hideToken": {
+ "message": "Itago ang Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Itago ang Token?"
+ },
+ "howToDeposit": {
+ "message": "Paano mo gustong mag-deposito ng Ether?"
+ },
+ "import": {
+ "message": "I-import",
+ "description": "Button para i-import ang account mula sa napiling file"
+ },
+ "importAccount": {
+ "message": "I-import ang Account"
+ },
+ "importAnAccount": {
+ "message": "I-import ang account"
+ },
+ "importDen": {
+ "message": "I-import ang Existing DEN"
+ },
+ "imported": {
+ "message": "Na-import na",
+ "description": "status na nagpapakita na ang account ay lubos na na-load sa keyring"
+ },
+ "infoHelp": {
+ "message": "Impormasyon at Tulong"
+ },
+ "invalidAddress": {
+ "message": "Invalid ang address"
+ },
+ "invalidGasParams": {
+ "message": "Invalid ang Gas Parameters"
+ },
+ "invalidInput": {
+ "message": "Invalid ang input."
+ },
+ "invalidRequest": {
+ "message": "Invalid ang Request"
+ },
+ "jsonFile": {
+ "message": "JSON File",
+ "description": "format para sa pag-import ng account"
+ },
+ "kovan": {
+ "message": "Kovan Test Network"
+ },
+ "lessThanMax": {
+ "message": "dapat mas mababa o katumbas ng $1.",
+ "description": "helper para sa pag-input ng hex bilang decimal input"
+ },
+ "limit": {
+ "message": "Limitasyon"
+ },
+ "loading": {
+ "message": "Naglo-load..."
+ },
+ "loadingTokens": {
+ "message": "Naglo-load ang Tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "logout": {
+ "message": "Log out"
+ },
+ "loose": {
+ "message": "Loose"
+ },
+ "mainnet": {
+ "message": "Main Ethereum Network"
+ },
+ "message": {
+ "message": "Mensahe"
+ },
+ "min": {
+ "message": "Minimum"
+ },
+ "myAccounts": {
+ "message": "Aking mga Account"
+ },
+ "needEtherInWallet": {
+ "message": "Upang makipag-ugnayan sa decentralized applications gamit ang MetaMask, kakailanganin mo ng Ether sa iyong wallet."
+ },
+ "needImportFile": {
+ "message": "Dapat kang pumili ng file para i-import.",
+ "description": "Ang user ay nag-iimport ng account at kailangan magdagdag ng file upang tumuloy"
+ },
+ "needImportPassword": {
+ "message": "Dapat mong i-enter ang password para sa napiling file.",
+ "description": "Password at file na kailangan upang ma-import ang account"
+ },
+ "networks": {
+ "message": "Networks"
+ },
+ "newAccount": {
+ "message": "Bagong Account"
+ },
+ "newAccountNumberName": {
+ "message": "Account $1",
+ "description": "Ang default na pangalan ng susunod na account na gagawin sa create account screen"
+ },
+ "newContract": {
+ "message": "Bagong Contract"
+ },
+ "newPassword": {
+ "message": "Bagong Password (min 8 chars)"
+ },
+ "newRecipient": {
+ "message": "Bagong Recipient"
+ },
+ "next": {
+ "message": "Sunod"
+ },
+ "noAddressForName": {
+ "message": "Walang naka-set na address para sa pangalang ito."
+ },
+ "noDeposits": {
+ "message": "Walang natanggap na mga deposito"
+ },
+ "noTransactionHistory": {
+ "message": "Walang kasaysayan ng transaksyon."
+ },
+ "noTransactions": {
+ "message": "Walang mga Transaksyon"
+ },
+ "notStarted": {
+ "message": "Hindi Sinimulan"
+ },
+ "oldUI": {
+ "message": "Lumang UI"
+ },
+ "oldUIMessage": {
+ "message": "Ikaw ay bumalik sa lumang UI. Maaari kang bumalik sa bagong UI mula sa isang opsyon sa dropdown menu na matatagpuan sa bandang taas at kanan."
+ },
+ "or": {
+ "message": "o",
+ "description": "Pagpili sa pagitan ng paggawa of pag-import ng bagong account"
+ },
+ "passwordMismatch": {
+ "message": "hindi nagtugma ang mga password",
+ "description": "Sa proseso ng paggawa ng password, ang dalawang password fields ay hindi nagtugma"
+ },
+ "passwordShort": {
+ "message": "hindi sapat ang haba ng password",
+ "description": "Sa proseso ng paggawa ng password, ang password ay hindi in password creation process, hind sapat ang haba ng password upang maging ligtas"
+ },
+ "pastePrivateKey": {
+ "message": "I-paste dito ang iyong private key string:",
+ "description": "Para sa pag-import ng account mula sa private key"
+ },
+ "pasteSeed": {
+ "message": "I-paste dito ang iyong seed phrase!"
+ },
+ "pleaseReviewTransaction": {
+ "message": "Mangyaring suriin ang iyong transaksyon."
+ },
+ "privateKey": {
+ "message": "Private Key",
+ "description": "Piliin ang ganitong type ng file upang gamitin sa pag-import ng account"
+ },
+ "privateKeyWarning": {
+ "message": "Babala: Huwag sabihin sa kahit na sino ang key na ito. Maaring makuha at manakaw ng sinumang nakakaalam ng iyong private key ang mga assets sa iyong account."
+ },
+ "privateNetwork": {
+ "message": "Pribadong Network"
+ },
+ "qrCode": {
+ "message": "Ipakita ang QR Code"
+ },
+ "readdToken": {
+ "message": "Upang muling idagdag ang token na ito, pumunta sa “Magdagdag ng Token” sa options menu ng iyong account."
+ },
+ "readMore": {
+ "message": "Alamin ang iba pang impormasyon dito."
+ },
+ "receive": {
+ "message": "Tanggapin"
+ },
+ "recipientAddress": {
+ "message": "Address ng Tatanggap"
+ },
+ "refundAddress": {
+ "message": "Ang Iyong Refund Address"
+ },
+ "rejected": {
+ "message": "Tinanggihan"
+ },
+ "required": {
+ "message": "Kailangan"
+ },
+ "retryWithMoreGas": {
+ "message": "Muling subukan ng may mas mataas na gas price dito"
+ },
+ "revert": {
+ "message": "Ibalik"
+ },
+ "rinkeby": {
+ "message": "Rinkeby Test Network"
+ },
+ "ropsten": {
+ "message": "Ropsten Test Network"
+ },
+ "sampleAccountName": {
+ "message": "Halimbawa: Ang aking bagong account",
+ "description": "Tulungan ang user na intindihin ang konsepto ng pagdagdag ng human-readable name sa kanilang account"
+ },
+ "save": {
+ "message": "I-save"
+ },
+ "saveAsFile": {
+ "message": "I-save bilang File",
+ "description": "Proseso sa pag-export ng Account"
+ },
+ "selectService": {
+ "message": "Piliin ang Service"
+ },
+ "send": {
+ "message": "Magpadala"
+ },
+ "sendTokens": {
+ "message": "Magpadala ng Tokens"
+ },
+ "sendTokensAnywhere": {
+ "message": "Magpadala ng Tokens sa sinumang may Ethereum account"
+ },
+ "settings": {
+ "message": "Mga Setting"
+ },
+ "shapeshiftBuy": {
+ "message": "Bumili gamit ang Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Ipakita ang Private Keys"
+ },
+ "showQRCode": {
+ "message": "Ipakita ang QR Code"
+ },
+ "sign": {
+ "message": "I-sign"
+ },
+ "signMessage": {
+ "message": "I-sign ang mensahe"
+ },
+ "signNotice": {
+ "message": "Ang pag-sign ng mensaheng ito ay maaring magdulot ng mapanganib na epekto. I-sign lamang ang mga mensahe mula sa mga site na pinagkakatiwalaan mo ng iyong account. Ang mapanganib na paraang ito ay aalisin sa isa sa mga susunod na bersyon. "
+ },
+ "sigRequest": {
+ "message": "Hiling na Signature"
+ },
+ "sigRequested": {
+ "message": "Hiniling ang Signature"
+ },
+ "status": {
+ "message": "Istado"
+ },
+ "submit": {
+ "message": "I-submit"
+ },
+ "takesTooLong": {
+ "message": "Masyadong matagal?"
+ },
+ "testFaucet": {
+ "message": "Test Faucet"
+ },
+ "to": {
+ "message": "To"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 sa ETH sa pamamagitan ng ShapeShift",
+ "description": "Pupunan ng system ang deposit type sa simula ng mensahe"
+ },
+ "tokenBalance": {
+ "message": "Ang iyong Token Balance ay:"
+ },
+ "total": {
+ "message": "Kabuuan"
+ },
+ "transactionMemo": {
+ "message": "Memo ng transaksyon (opsyonal)"
+ },
+ "transactionNumber": {
+ "message": "Numero ng Transaksyon"
+ },
+ "transfers": {
+ "message": "Mga Inilipat"
+ },
+ "troubleTokenBalances": {
+ "message": "Nagkaroon kami ng problema sa paglo-load ng iyong mga balanseng token. Tingnan ito dito ",
+ "description": "Susundan ng link (dito) para tingnan ang token balances"
+ },
+ "typePassword": {
+ "message": "I-type ang iyong Password"
+ },
+ "uiWelcome": {
+ "message": "Maligayang pagdating sa Bagong UI (Beta)"
+ },
+ "uiWelcomeMessage": {
+ "message": "Ginagamit mo na ngayon ang bagong MetaMask UI. I-explore at subukan ang mga bagong features tulad ng pagpapadala ng mga token, at ipaalam sa amin kung mayroon kang anumang mga isyu."
+ },
+ "unavailable": {
+ "message": "Hindi Magagamit"
+ },
+ "unknown": {
+ "message": "Hindi Alam"
+ },
+ "unknownNetwork": {
+ "message": "Hindi Alam ang Pribadong Network"
+ },
+ "unknownNetworkId": {
+ "message": "Hindi alam ang network ID"
+ },
+ "usaOnly": {
+ "message": "USA lamang",
+ "description": "Ang paggamit ng exchange na ito ay limitado sa mga tao sa loob ng Estados Unidos"
+ },
+ "usedByClients": {
+ "message": "Ginagamit ng iba't ibang mga clients"
+ },
+ "viewAccount": {
+ "message": "Tingnan ang Account"
+ },
+ "warning": {
+ "message": "Babala"
+ },
+ "whatsThis": {
+ "message": "Ano ito?"
+ },
+ "yourSigRequested": {
+ "message": "Hinihiling ang iyong signature"
+ },
+ "youSign": {
+ "message": "Ikaw ay nagsa-sign"
+ }
+}
diff --git a/app/scripts/controllers/transactions.js b/app/scripts/controllers/transactions.js
index 9c2ca0dc8..3e3909361 100644
--- a/app/scripts/controllers/transactions.js
+++ b/app/scripts/controllers/transactions.js
@@ -6,7 +6,6 @@ const EthQuery = require('ethjs-query')
const TransactionStateManager = require('../lib/tx-state-manager')
const TxGasUtil = require('../lib/tx-gas-utils')
const PendingTransactionTracker = require('../lib/pending-tx-tracker')
-const createId = require('../lib/random-id')
const NonceTracker = require('../lib/nonce-tracker')
/*
@@ -92,8 +91,8 @@ module.exports = class TransactionController extends EventEmitter {
this.pendingTxTracker.on('tx:warning', (txMeta) => {
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning')
})
+ this.pendingTxTracker.on('tx:confirmed', (txId) => this._markNonceDuplicatesDropped(txId))
this.pendingTxTracker.on('tx:failed', this.txStateManager.setTxStatusFailed.bind(this.txStateManager))
- this.pendingTxTracker.on('tx:confirmed', this.txStateManager.setTxStatusConfirmed.bind(this.txStateManager))
this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
if (!txMeta.firstRetryBlockNumber) {
txMeta.firstRetryBlockNumber = latestBlockNumber
@@ -186,14 +185,7 @@ module.exports = class TransactionController extends EventEmitter {
// validate
await this.txGasUtil.validateTxParams(txParams)
// construct txMeta
- const txMeta = {
- id: createId(),
- time: (new Date()).getTime(),
- status: 'unapproved',
- metamaskNetworkId: this.getNetwork(),
- txParams: txParams,
- loadingDefaults: true,
- }
+ const txMeta = this.txStateManager.generateTxMeta({txParams})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
// add default tx params
@@ -215,7 +207,6 @@ module.exports = class TransactionController extends EventEmitter {
const txParams = txMeta.txParams
// ensure value
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
- txMeta.nonceSpecified = Boolean(txParams.nonce)
let gasPrice = txParams.gasPrice
if (!gasPrice) {
gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.gasPrice()
@@ -226,11 +217,17 @@ module.exports = class TransactionController extends EventEmitter {
return await this.txGasUtil.analyzeGasUsage(txMeta)
}
- async retryTransaction (txId) {
- this.txStateManager.setTxStatusUnapproved(txId)
- const txMeta = this.txStateManager.getTx(txId)
- txMeta.lastGasPrice = txMeta.txParams.gasPrice
- this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry')
+ async retryTransaction (originalTxId) {
+ const originalTxMeta = this.txStateManager.getTx(originalTxId)
+ const lastGasPrice = originalTxMeta.txParams.gasPrice
+ const txMeta = this.txStateManager.generateTxMeta({
+ txParams: originalTxMeta.txParams,
+ lastGasPrice,
+ loadingDefaults: false,
+ })
+ this.addTx(txMeta)
+ this.emit('newUnapprovedTx', txMeta)
+ return txMeta
}
async updateTransaction (txMeta) {
@@ -253,11 +250,9 @@ module.exports = class TransactionController extends EventEmitter {
// wait for a nonce
nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
// add nonce to txParams
- const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce
- if (nonce > nonceLock.nextNonce) {
- const message = `Specified nonce may not be larger than account's next valid nonce.`
- throw new Error(message)
- }
+ // if txMeta has lastGasPrice then it is a retry at same nonce with higher
+ // gas price transaction and their for the nonce should not be calculated
+ const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
// add nonce debugging information to txMeta
txMeta.nonceDetails = nonceLock.nonceDetails
@@ -314,6 +309,22 @@ module.exports = class TransactionController extends EventEmitter {
// PRIVATE METHODS
//
+ _markNonceDuplicatesDropped (txId) {
+ this.txStateManager.setTxStatusConfirmed(txId)
+ // get the confirmed transactions nonce and from address
+ const txMeta = this.txStateManager.getTx(txId)
+ const { nonce, from } = txMeta.txParams
+ const sameNonceTxs = this.txStateManager.getFilteredTxList({nonce, from})
+ if (!sameNonceTxs.length) return
+ // mark all same nonce transactions as dropped and give i a replacedBy hash
+ sameNonceTxs.forEach((otherTxMeta) => {
+ if (otherTxMeta.id === txId) return
+ otherTxMeta.replacedBy = txMeta.hash
+ this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:confirmed reference to confirmed txHash with same nonce')
+ this.txStateManager.setTxStatusDropped(otherTxMeta.id)
+ })
+ }
+
_updateMemstore () {
const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({
diff --git a/app/scripts/lib/tx-state-manager.js b/app/scripts/lib/tx-state-manager.js
index 2eb006380..ad07c813f 100644
--- a/app/scripts/lib/tx-state-manager.js
+++ b/app/scripts/lib/tx-state-manager.js
@@ -1,9 +1,21 @@
const extend = require('xtend')
const EventEmitter = require('events')
const ObservableStore = require('obs-store')
+const createId = require('./random-id')
const ethUtil = require('ethereumjs-util')
const txStateHistoryHelper = require('./tx-state-history-helper')
+// STATUS METHODS
+ // statuses:
+ // - `'unapproved'` the user has not responded
+ // - `'rejected'` the user has responded no!
+ // - `'approved'` the user has approved the tx
+ // - `'signed'` the tx is signed
+ // - `'submitted'` the tx is sent to a server
+ // - `'confirmed'` the tx has been included in a block.
+ // - `'failed'` the tx failed for some reason, included on tx data.
+ // - `'dropped'` the tx nonce was already used
+
module.exports = class TransactionStateManager extends EventEmitter {
constructor ({ initState, txHistoryLimit, getNetwork }) {
super()
@@ -16,6 +28,16 @@ module.exports = class TransactionStateManager extends EventEmitter {
this.getNetwork = getNetwork
}
+ generateTxMeta (opts) {
+ return extend({
+ id: createId(),
+ time: (new Date()).getTime(),
+ status: 'unapproved',
+ metamaskNetworkId: this.getNetwork(),
+ loadingDefaults: true,
+ }, opts)
+ }
+
// Returns the number of txs for the current network.
getTxCount () {
return this.getTxList().length
@@ -164,16 +186,6 @@ module.exports = class TransactionStateManager extends EventEmitter {
})
}
- // STATUS METHODS
- // statuses:
- // - `'unapproved'` the user has not responded
- // - `'rejected'` the user has responded no!
- // - `'approved'` the user has approved the tx
- // - `'signed'` the tx is signed
- // - `'submitted'` the tx is sent to a server
- // - `'confirmed'` the tx has been included in a block.
- // - `'failed'` the tx failed for some reason, included on tx data.
-
// get::set status
// should return the status of the tx.
@@ -202,7 +214,11 @@ module.exports = class TransactionStateManager extends EventEmitter {
}
// should update the status of the tx to 'submitted'.
+ // and add a time stamp for when it was called
setTxStatusSubmitted (txId) {
+ const txMeta = this.getTx(txId)
+ txMeta.submittedTime = (new Date()).getTime()
+ this.updateTx(txMeta, 'txStateManager - add submitted time stamp')
this._setTxStatus(txId, 'submitted')
}
@@ -211,6 +227,12 @@ module.exports = class TransactionStateManager extends EventEmitter {
this._setTxStatus(txId, 'confirmed')
}
+ // should update the status dropped
+ setTxStatusDropped (txId) {
+ this._setTxStatus(txId, 'dropped')
+ }
+
+
setTxStatusFailed (txId, err) {
const txMeta = this.getTx(txId)
txMeta.err = {
diff --git a/development/states/confirm-new-ui.json b/development/states/confirm-new-ui.json
index 6ea8e64cd..6981781a9 100644
--- a/development/states/confirm-new-ui.json
+++ b/development/states/confirm-new-ui.json
@@ -116,7 +116,7 @@
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
- "gasTotal": "0xb451dc41b578",
+ "gasTotal": "0xaa87bee538000",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
diff --git a/development/states/send-edit.json b/development/states/send-edit.json
index 6ea8e64cd..6981781a9 100644
--- a/development/states/send-edit.json
+++ b/development/states/send-edit.json
@@ -116,7 +116,7 @@
"send": {
"gasLimit": "0xea60",
"gasPrice": "0xba43b7400",
- "gasTotal": "0xb451dc41b578",
+ "gasTotal": "0xaa87bee538000",
"tokenBalance": null,
"from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",
diff --git a/gulpfile.js b/gulpfile.js
index adfb148a9..dbbb1e4ff 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -408,7 +408,11 @@ function bundleTask(opts) {
.pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
// Minification
.pipe(gulpif(opts.isBuild, uglify({
- mangle: { reserved: [ 'MetamaskInpageProvider' ] },
+ mangle: { reserved: [ 'MetamaskInpageProvider' ] },
+ })))
+ // Transpile to ES5
+ .pipe(gulpif(opts.isBuild, babel({
+ presets: ['env']
})))
// writes .map file
.pipe(gulpif(debug, sourcemaps.write('./')))
diff --git a/mascara/src/app/first-time/backup-phrase-screen.js b/mascara/src/app/first-time/backup-phrase-screen.js
index c8cc56c6c..6819abcf3 100644
--- a/mascara/src/app/first-time/backup-phrase-screen.js
+++ b/mascara/src/app/first-time/backup-phrase-screen.js
@@ -78,14 +78,16 @@ class BackupPhraseScreen extends Component {
{this.props.seedWords}
</div>
{!isShowingSecret && (
- <div className="backup-phrase__secret-blocker">
+ <div
+ className="backup-phrase__secret-blocker"
+ onClick={() => this.setState({ isShowingSecret: true })}
+ >
<LockIcon width="28px" height="35px" fill="#FFFFFF" />
- <button
+ <div
className="backup-phrase__reveal-button"
- onClick={() => this.setState({ isShowingSecret: true })}
>
Click here to reveal secret words
- </button>
+ </div>
</div>
)}
</div>
diff --git a/old-ui/app/components/transaction-list-item.js b/old-ui/app/components/transaction-list-item.js
index e7251df8d..7ab3414e5 100644
--- a/old-ui/app/components/transaction-list-item.js
+++ b/old-ui/app/components/transaction-list-item.js
@@ -29,9 +29,16 @@ function TransactionListItem () {
}
TransactionListItem.prototype.showRetryButton = function () {
- const { transaction = {} } = this.props
- const { status, time } = transaction
- return status === 'submitted' && Date.now() - time > 30000
+ const { transaction = {}, transactions } = this.props
+ const { status, submittedTime, txParams } = transaction
+ const currentNonce = txParams.nonce
+ const currentNonceTxs = transactions.filter(tx => tx.txParams.nonce === currentNonce)
+ const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
+ const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[0]
+ const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
+ && lastSubmittedTxWithCurrentNonce.id === transaction.id
+
+ return currentTxIsLatestWithNonce && Date.now() - submittedTime > 30000
}
TransactionListItem.prototype.render = function () {
@@ -201,6 +208,11 @@ function formatDate (date) {
function renderErrorOrWarning (transaction) {
const { status, err, warning } = transaction
+ // show dropped
+ if (status === 'dropped') {
+ return h('span.dropped', ' (Dropped)')
+ }
+
// show rejected
if (status === 'rejected') {
return h('span.error', ' (Rejected)')
diff --git a/old-ui/app/components/transaction-list.js b/old-ui/app/components/transaction-list.js
index 345e3ca16..c77852921 100644
--- a/old-ui/app/components/transaction-list.js
+++ b/old-ui/app/components/transaction-list.js
@@ -62,7 +62,7 @@ TransactionList.prototype.render = function () {
}
return h(TransactionListItem, {
transaction, i, network, key,
- conversionRate,
+ conversionRate, transactions,
showTx: (txId) => {
this.props.viewPendingTx(txId)
},
diff --git a/old-ui/app/css/index.css b/old-ui/app/css/index.css
index 67c327f62..7af713336 100644
--- a/old-ui/app/css/index.css
+++ b/old-ui/app/css/index.css
@@ -247,6 +247,10 @@ app sections
color: #FFAE00;
}
+.dropped {
+ color: #6195ED;
+}
+
.lock {
width: 50px;
height: 50px;
diff --git a/package.json b/package.json
index 00587ece6..8f05bc7f1 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,10 @@
"mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"",
"mascara": "gulp build && cross-env METAMASK_DEBUG=true node ./mascara/example/server",
- "dist": "npm run dist:clear && npm install && gulp dist",
+ "dist": "npm run dist:clear && npm install && gulp dist && npm run test:es5",
"dist:clear": "rm -rf node_modules/eth-contract-metadata && rm -rf node_modules/eth-phishing-detect",
"test": "npm run lint && npm run test:coverage && npm run test:integration",
+ "test:es5": "es-check es5 ./dist/**/*.js",
"test:unit": "cross-env METAMASK_ENV=test mocha --exit --require babel-core/register --require test/helper.js --recursive \"test/unit/**/*.js\"",
"test:single": "cross-env METAMASK_ENV=test mocha --require test/helper.js",
"test:integration": "npm run test:integration:build && npm run test:flat && npm run test:mascara",
@@ -25,7 +26,7 @@
"test:flat:build:states": "node development/genStates.js",
"test:flat:build:ui": "npm run test:flat:build:states && browserify ./development/mock-dev.js -o ./development/bundle.js",
"test:mascara": "npm run test:mascara:build && karma start test/mascara.conf.js",
- "test:mascara:build": "mkdir -p dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests",
+ "test:mascara:build": "mkdirp dist/mascara && npm run test:mascara:build:ui && npm run test:mascara:build:background && npm run test:mascara:build:tests",
"test:mascara:build:ui": "browserify mascara/test/test-ui.js -o dist/mascara/ui.js",
"test:mascara:build:background": "browserify mascara/src/background.js -o dist/mascara/background.js",
"test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
@@ -200,6 +201,7 @@
"envify": "^4.0.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.5",
+ "es-check": "^2.0.2",
"eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0",
"eslint-plugin-react": "^7.4.0",
diff --git a/test/integration/lib/send-new-ui.js b/test/integration/lib/send-new-ui.js
index 5d21ba2a3..573faaee3 100644
--- a/test/integration/lib/send-new-ui.js
+++ b/test/integration/lib/send-new-ui.js
@@ -1,4 +1,4 @@
-const reactTriggerChange = require('react-trigger-change')
+const reactTriggerChange = require('../../lib/react-trigger-change')
const {
timeout,
queryAsync,
@@ -93,7 +93,7 @@ async function runSendFlowTest(assert, done) {
'send gas field should show estimated gas total converted to USD'
)
- const sendGasOpenCustomizeModalButton = await queryAsync($, '.send-v2__sliders-icon-container')
+ const sendGasOpenCustomizeModalButton = await queryAsync($, '.sliders-icon-container')
sendGasOpenCustomizeModalButton[0].click()
const customizeGasModal = await queryAsync($, '.send-v2__customize-gas')
@@ -135,9 +135,9 @@ async function runSendFlowTest(assert, done) {
assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name')
const confirmScreenRows = await queryAsync($, '.confirm-screen-rows')
- const confirmScreenGas = confirmScreenRows.find('.confirm-screen-row-info')[2]
- assert.equal(confirmScreenGas.textContent, '3.6 USD', 'confirm screen should show correct gas')
- const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[3]
+ const confirmScreenGas = confirmScreenRows.find('.currency-display__converted-value')[0]
+ assert.equal(confirmScreenGas.textContent, '3.60 USD', 'confirm screen should show correct gas')
+ const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[2]
assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total')
const confirmScreenBackButton = await queryAsync($, '.confirm-screen-back-button')
diff --git a/test/lib/react-trigger-change.js b/test/lib/react-trigger-change.js
new file mode 100644
index 000000000..a25ddff00
--- /dev/null
+++ b/test/lib/react-trigger-change.js
@@ -0,0 +1,161 @@
+// Trigger React's synthetic change events on input, textarea and select elements
+// https://github.com/vitalyq/react-trigger-change
+
+/******************IMPORTANT NOTE******************/
+/* This file is a modification of the */
+/* 'react-trigger-change' library linked above. */
+/* That library breaks when 'onFocus' events are */
+/* added to components under test because it */
+/* dispatches focus events to ensure changes are */
+/* triggered in some versions of IE. */
+/* This modification removes the accomodations */
+/* 'react-trigger-change' makes for IE to ensure */
+/* our tests can pass in chrome and firefox. */
+/**************************************************/
+
+'use strict';
+
+// Constants and functions are declared inside the closure.
+// In this way, reactTriggerChange can be passed directly to executeScript in Selenium.
+module.exports = function reactTriggerChange(node) {
+ var supportedInputTypes = {
+ color: true,
+ date: true,
+ datetime: true,
+ 'datetime-local': true,
+ email: true,
+ month: true,
+ number: true,
+ password: true,
+ range: true,
+ search: true,
+ tel: true,
+ text: true,
+ time: true,
+ url: true,
+ week: true
+ };
+ var nodeName = node.nodeName.toLowerCase();
+ var type = node.type;
+ var event;
+ var descriptor;
+ var initialValue;
+ var initialChecked;
+ var initialCheckedRadio;
+
+ // Do not try to delete non-configurable properties.
+ // Value and checked properties on DOM elements are non-configurable in PhantomJS.
+ function deletePropertySafe(elem, prop) {
+ var desc = Object.getOwnPropertyDescriptor(elem, prop);
+ if (desc && desc.configurable) {
+ delete elem[prop];
+ }
+ }
+
+ function getCheckedRadio(radio) {
+ var name = radio.name;
+ var radios;
+ var i;
+ if (name) {
+ radios = document.querySelectorAll('input[type="radio"][name="' + name + '"]');
+ for (i = 0; i < radios.length; i += 1) {
+ if (radios[i].checked) {
+ return radios[i] !== radio ? radios[i] : null;
+ }
+ }
+ }
+ return null;
+ }
+
+ function preventChecking(e) {
+ e.preventDefault();
+ if (!initialChecked) {
+ e.target.checked = false;
+ }
+ if (initialCheckedRadio) {
+ initialCheckedRadio.checked = true;
+ }
+ }
+
+ if (nodeName === 'select' ||
+ (nodeName === 'input' && type === 'file')) {
+ // IE9-IE11, non-IE
+ // Dispatch change.
+ event = document.createEvent('HTMLEvents');
+ event.initEvent('change', true, false);
+ node.dispatchEvent(event);
+ } else if ((nodeName === 'input' && supportedInputTypes[type]) ||
+ nodeName === 'textarea') {
+ // React 16
+ // Cache artificial value property descriptor.
+ // Property doesn't exist in React <16, descriptor is undefined.
+ descriptor = Object.getOwnPropertyDescriptor(node, 'value');
+
+ // Update inputValueTracking cached value.
+ // Remove artificial value property.
+ // Restore initial value to trigger event with it.
+ initialValue = node.value;
+ node.value = initialValue + '#';
+ deletePropertySafe(node, 'value');
+ node.value = initialValue;
+
+ // React 0.14: IE10-IE11, non-IE
+ // React 15: non-IE
+ // React 16: IE10-IE11, non-IE
+ event = document.createEvent('HTMLEvents');
+ event.initEvent('input', true, false);
+ node.dispatchEvent(event);
+
+ // React 16
+ // Restore artificial value property descriptor.
+ if (descriptor) {
+ Object.defineProperty(node, 'value', descriptor);
+ }
+ } else if (nodeName === 'input' && type === 'checkbox') {
+ // Invert inputValueTracking cached value.
+ node.checked = !node.checked;
+
+ // Dispatch click.
+ // Click event inverts checked value.
+ event = document.createEvent('MouseEvents');
+ event.initEvent('click', true, true);
+ node.dispatchEvent(event);
+ } else if (nodeName === 'input' && type === 'radio') {
+ // Cache initial checked value.
+ initialChecked = node.checked;
+
+ // Find and cache initially checked radio in the group.
+ initialCheckedRadio = getCheckedRadio(node);
+
+ // React 16
+ // Cache property descriptor.
+ // Invert inputValueTracking cached value.
+ // Remove artificial checked property.
+ // Restore initial value, otherwise preventDefault will eventually revert the value.
+ descriptor = Object.getOwnPropertyDescriptor(node, 'checked');
+ node.checked = !initialChecked;
+ deletePropertySafe(node, 'checked');
+ node.checked = initialChecked;
+
+ // Prevent toggling during event capturing phase.
+ // Set checked value to false if initialChecked is false,
+ // otherwise next listeners will see true.
+ // Restore initially checked radio in the group.
+ node.addEventListener('click', preventChecking, true);
+
+ // Dispatch click.
+ // Click event inverts checked value.
+ event = document.createEvent('MouseEvents');
+ event.initEvent('click', true, true);
+ node.dispatchEvent(event);
+
+ // Remove listener to stop further change prevention.
+ node.removeEventListener('click', preventChecking, true);
+
+ // React 16
+ // Restore artificial checked property descriptor.
+ if (descriptor) {
+ Object.defineProperty(node, 'checked', descriptor);
+ }
+ }
+};
diff --git a/test/unit/components/binary-renderer-test.js b/test/unit/components/binary-renderer-test.js
index ee2fa8b60..7bf9250cc 100644
--- a/test/unit/components/binary-renderer-test.js
+++ b/test/unit/components/binary-renderer-test.js
@@ -1,5 +1,5 @@
var assert = require('assert')
-var BinaryRenderer = require('../../../ui/app/components/binary-renderer')
+var BinaryRenderer = require('../../../old-ui/app/components/binary-renderer')
describe('BinaryRenderer', function () {
let binaryRenderer
diff --git a/test/unit/components/bn-as-decimal-input-test.js b/test/unit/components/bn-as-decimal-input-test.js
index 58ecc9c89..7b9d9814f 100644
--- a/test/unit/components/bn-as-decimal-input-test.js
+++ b/test/unit/components/bn-as-decimal-input-test.js
@@ -6,7 +6,7 @@ const ReactTestUtils = require('react-addons-test-utils')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
-var BnInput = require('../../../ui/app/components/bn-as-decimal-input')
+var BnInput = require('../../../old-ui/app/components/bn-as-decimal-input')
describe('BnInput', function () {
it('can tolerate a gas decimal number at a high precision', function (done) {
diff --git a/test/unit/nameForAccount_test.js b/test/unit/nameForAccount_test.js
index e7c0b18b4..32af49e9d 100644
--- a/test/unit/nameForAccount_test.js
+++ b/test/unit/nameForAccount_test.js
@@ -2,7 +2,7 @@ var assert = require('assert')
var sinon = require('sinon')
var path = require('path')
-var contractNamer = require(path.join(__dirname, '..', '..', 'ui', 'lib', 'contract-namer.js'))
+var contractNamer = require(path.join(__dirname, '..', '..', 'old-ui', 'lib', 'contract-namer.js'))
describe('contractNamer', function () {
beforeEach(function () {
diff --git a/test/unit/tx-controller-test.js b/test/unit/tx-controller-test.js
index cc99afee4..712097fce 100644
--- a/test/unit/tx-controller-test.js
+++ b/test/unit/tx-controller-test.js
@@ -392,6 +392,49 @@ describe('Transaction Controller', function () {
})
})
+ describe('#retryTransaction', function () {
+ it('should create a new txMeta with the same txParams as the original one', function (done) {
+ let txParams = {
+ nonce: '0x00',
+ from: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',
+ to: '0xB09d8505E1F4EF1CeA089D47094f5DD3464083d4',
+ data: '0x0',
+ }
+ txController.txStateManager._saveTxList([
+ { id: 1, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams },
+ ])
+ txController.retryTransaction(1)
+ .then((txMeta) => {
+ assert.equal(txMeta.txParams.nonce, txParams.nonce, 'nonce should be the same')
+ assert.equal(txMeta.txParams.from, txParams.from, 'from should be the same')
+ assert.equal(txMeta.txParams.to, txParams.to, 'to should be the same')
+ assert.equal(txMeta.txParams.data, txParams.data, 'data should be the same')
+ assert.ok(('lastGasPrice' in txMeta), 'should have the key `lastGasPrice`')
+ assert.equal(txController.txStateManager.getTxList().length, 2)
+ done()
+ }).catch(done)
+ })
+ })
+
+ describe('#_markNonceDuplicatesDropped', function () {
+ it('should mark all nonce duplicates as dropped without marking the confirmed transaction as dropped', function () {
+ txController.txStateManager._saveTxList([
+ { id: 1, status: 'confirmed', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 2, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 3, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 4, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 5, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 6, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ { id: 7, status: 'submitted', metamaskNetworkId: currentNetworkId, history: [{}], txParams: { nonce: '0x01' } },
+ ])
+ txController._markNonceDuplicatesDropped(1)
+ const confirmedTx = txController.txStateManager.getTx(1)
+ const droppedTxs = txController.txStateManager.getFilteredTxList({ nonce: '0x01', status: 'dropped' })
+ assert.equal(confirmedTx.status, 'confirmed', 'the confirmedTx should remain confirmed')
+ assert.equal(droppedTxs.length, 6, 'their should be 6 dropped txs')
+
+ })
+ })
describe('#getPendingTransactions', function () {
beforeEach(function () {
@@ -401,7 +444,7 @@ describe('Transaction Controller', function () {
{ id: 3, status: 'approved', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 4, status: 'signed', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 5, status: 'submitted', metamaskNetworkId: currentNetworkId, txParams: {} },
- { id: 6, status: 'confimed', metamaskNetworkId: currentNetworkId, txParams: {} },
+ { id: 6, status: 'confirmed', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 7, status: 'failed', metamaskNetworkId: currentNetworkId, txParams: {} },
])
})
diff --git a/ui/app/account-detail.js b/ui/app/account-detail.js
deleted file mode 100644
index 0da435298..000000000
--- a/ui/app/account-detail.js
+++ /dev/null
@@ -1,117 +0,0 @@
-const inherits = require('util').inherits
-const extend = require('xtend')
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const actions = require('./actions')
-const valuesFor = require('./util').valuesFor
-const TransactionList = require('./components/transaction-list')
-const ExportAccountView = require('./components/account-export')
-const TabBar = require('./components/tab-bar')
-const TokenList = require('./components/token-list')
-
-module.exports = connect(mapStateToProps)(AccountDetailScreen)
-
-function mapStateToProps (state) {
- return {
- metamask: state.metamask,
- identities: state.metamask.identities,
- accounts: state.metamask.accounts,
- address: state.metamask.selectedAddress,
- accountDetail: state.appState.accountDetail,
- network: state.metamask.network,
- unapprovedMsgs: valuesFor(state.metamask.unapprovedMsgs),
- shapeShiftTxList: state.metamask.shapeShiftTxList,
- transactions: state.metamask.selectedAddressTxList || [],
- conversionRate: state.metamask.conversionRate,
- currentCurrency: state.metamask.currentCurrency,
- currentAccountTab: state.metamask.currentAccountTab,
- tokens: state.metamask.tokens,
- computedBalances: state.metamask.computedBalances,
- }
-}
-
-inherits(AccountDetailScreen, Component)
-function AccountDetailScreen () {
- Component.call(this)
-}
-
-// Note: This component is no longer used. Leaving the file for reference:
-// - structuring routing for add token
-// - state required for TxList
-// Delete file when those features are complete
-AccountDetailScreen.prototype.render = function () {}
-
-AccountDetailScreen.prototype.subview = function () {
- var subview
- try {
- subview = this.props.accountDetail.subview
- } catch (e) {
- subview = null
- }
-
- switch (subview) {
- case 'transactions':
- return this.tabSections()
- case 'export':
- var state = extend({key: 'export'}, this.props)
- return h(ExportAccountView, state)
- default:
- return this.tabSections()
- }
-}
-
-AccountDetailScreen.prototype.tabSections = function () {
- const { currentAccountTab } = this.props
-
- return h('section.tabSection.full-flex-height.grow-tenx', [
-
- h(TabBar, {
- tabs: [
- { content: 'Sent', key: 'history' },
- { content: 'Tokens', key: 'tokens' },
- ],
- defaultTab: currentAccountTab || 'history',
- tabSelected: (key) => {
- this.props.dispatch(actions.setCurrentAccountTab(key))
- },
- }),
-
- this.tabSwitchView(),
- ])
-}
-
-AccountDetailScreen.prototype.tabSwitchView = function () {
- const props = this.props
- const { address, network } = props
- const { currentAccountTab, tokens } = this.props
-
- switch (currentAccountTab) {
- case 'tokens':
- return h(TokenList, {
- userAddress: address,
- network,
- tokens,
- addToken: () => this.props.dispatch(actions.showAddTokenPage()),
- })
- default:
- return this.transactionList()
- }
-}
-
-AccountDetailScreen.prototype.transactionList = function () {
- const {transactions, unapprovedMsgs, address,
- network, shapeShiftTxList, conversionRate } = this.props
-
- return h(TransactionList, {
- transactions: transactions.sort((a, b) => b.time - a.time),
- network,
- unapprovedMsgs,
- conversionRate,
- address,
- shapeShiftTxList,
- viewPendingTx: (txId) => {
- this.props.dispatch(actions.viewPendingTx(txId))
- },
- })
-}
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 092af080b..bc7ee3d07 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -1278,8 +1278,10 @@ function retryTransaction (txId) {
if (err) {
return dispatch(actions.displayWarning(err.message))
}
+ const { selectedAddressTxList } = newState
+ const { id: newTxId } = selectedAddressTxList[selectedAddressTxList.length - 1]
dispatch(actions.updateMetamaskState(newState))
- dispatch(actions.viewPendingTx(txId))
+ dispatch(actions.viewPendingTx(newTxId))
})
}
}
diff --git a/ui/app/components/balance.js b/ui/app/components/balance.js
deleted file mode 100644
index 57ca84564..000000000
--- a/ui/app/components/balance.js
+++ /dev/null
@@ -1,89 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-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
-
-inherits(EthBalanceComponent, Component)
-function EthBalanceComponent () {
- Component.call(this)
-}
-
-EthBalanceComponent.prototype.render = function () {
- var props = this.props
- let { value } = props
- var style = props.style
- var needsParse = this.props.needsParse !== undefined ? this.props.needsParse : true
- value = value ? formatBalance(value, 6, needsParse) : '...'
- var width = props.width
-
- return (
-
- h('.ether-balance.ether-balance-amount', {
- style: style,
- }, [
- h('div', {
- style: {
- display: 'inline',
- width: width,
- },
- }, this.renderBalance(value)),
- ])
-
- )
-}
-EthBalanceComponent.prototype.renderBalance = function (value) {
- var props = this.props
- if (value === 'None') return value
- if (value === '...') return value
- var balanceObj = generateBalanceObject(value, props.shorten ? 1 : 3)
- var balance
- var splitBalance = value.split(' ')
- var ethNumber = splitBalance[0]
- var ethSuffix = splitBalance[1]
- const showFiat = 'showFiat' in props ? props.showFiat : true
-
- if (props.shorten) {
- balance = balanceObj.shortBalance
- } else {
- balance = balanceObj.balance
- }
-
- var label = balanceObj.label
-
- return (
- h(Tooltip, {
- position: 'bottom',
- title: `${ethNumber} ${ethSuffix}`,
- }, h('div.flex-column', [
- h('.flex-row', {
- style: {
- alignItems: 'flex-end',
- lineHeight: '13px',
- fontFamily: 'Montserrat Light',
- textRendering: 'geometricPrecision',
- },
- }, [
- h('div', {
- style: {
- width: '100%',
- textAlign: 'right',
- },
- }, this.props.incoming ? `+${balance}` : balance),
- h('div', {
- style: {
- color: ' #AEAEAE',
- fontSize: '12px',
- marginLeft: '5px',
- },
- }, label),
- ]),
-
- showFiat ? h(FiatValue, { value: props.value }) : null,
- ]))
- )
-}
diff --git a/ui/app/components/binary-renderer.js b/ui/app/components/binary-renderer.js
deleted file mode 100644
index 0b6a1f5c2..000000000
--- a/ui/app/components/binary-renderer.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const ethUtil = require('ethereumjs-util')
-const extend = require('xtend')
-
-module.exports = BinaryRenderer
-
-inherits(BinaryRenderer, Component)
-function BinaryRenderer () {
- Component.call(this)
-}
-
-BinaryRenderer.prototype.render = function () {
- const props = this.props
- const { value, style } = props
- const text = this.hexToText(value)
-
- const defaultStyle = extend({
- width: '315px',
- maxHeight: '210px',
- resize: 'none',
- border: 'none',
- background: 'white',
- padding: '3px',
- }, style)
-
- return (
- h('textarea.font-small', {
- readOnly: true,
- style: defaultStyle,
- defaultValue: text,
- })
- )
-}
-
-BinaryRenderer.prototype.hexToText = function (hex) {
- try {
- const stripped = ethUtil.stripHexPrefix(hex)
- const buff = Buffer.from(stripped, 'hex')
- return buff.toString('utf8')
- } catch (e) {
- return hex
- }
-}
-
diff --git a/ui/app/components/currency-input.js b/ui/app/components/currency-input.js
index 6f7862e51..940238fa5 100644
--- a/ui/app/components/currency-input.js
+++ b/ui/app/components/currency-input.js
@@ -8,8 +8,12 @@ inherits(CurrencyInput, Component)
function CurrencyInput (props) {
Component.call(this)
+ const sanitizedValue = sanitizeValue(props.value)
+
this.state = {
- value: sanitizeValue(props.value),
+ value: sanitizedValue,
+ emptyState: false,
+ focused: false,
}
}
@@ -58,9 +62,11 @@ CurrencyInput.prototype.handleChange = function (newValue) {
if (value === '0' && newValue[newValueLastIndex] === '0') {
parsedValue = parsedValue.slice(0, newValueLastIndex)
}
-
const sanitizedValue = sanitizeValue(parsedValue)
- this.setState({ value: sanitizedValue })
+ this.setState({
+ value: sanitizedValue,
+ emptyState: newValue === '' && sanitizedValue === '0',
+ })
onInputChange(sanitizedValue)
}
@@ -86,17 +92,19 @@ CurrencyInput.prototype.render = function () {
readOnly,
inputRef,
} = this.props
+ const { emptyState, focused } = this.state
const inputSizeMultiplier = readOnly ? 1 : 1.2
const valueToRender = this.getValueToRender()
-
return h('input', {
className,
- value: valueToRender,
- placeholder,
+ value: emptyState ? '' : valueToRender,
+ placeholder: focused ? '' : placeholder,
size: valueToRender.length * inputSizeMultiplier,
readOnly,
+ onFocus: () => this.setState({ focused: true, emptyState: valueToRender === '0' }),
+ onBlur: () => this.setState({ focused: false, emptyState: false }),
onChange: e => this.handleChange(e.target.value),
ref: inputRef,
})
diff --git a/ui/app/components/customize-gas-modal/index.js b/ui/app/components/customize-gas-modal/index.js
index 920dfeab6..d8384c19d 100644
--- a/ui/app/components/customize-gas-modal/index.js
+++ b/ui/app/components/customize-gas-modal/index.js
@@ -22,12 +22,14 @@ const {
conversionUtil,
multiplyCurrencies,
conversionGreaterThan,
+ conversionMax,
subtractCurrencies,
} = require('../../conversion-util')
const {
getGasPrice,
getGasLimit,
+ getForceGasMin,
conversionRateSelector,
getSendAmount,
getSelectedToken,
@@ -45,6 +47,7 @@ function mapStateToProps (state) {
return {
gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state),
+ forceGasMin: getForceGasMin(state),
conversionRate,
amount: getSendAmount(state),
maxModeOn: getSendMaxModeState(state),
@@ -115,9 +118,9 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
updateSendAmount(maxAmount)
}
- updateGasPrice(gasPrice)
- updateGasLimit(gasLimit)
- updateGasTotal(gasTotal)
+ updateGasPrice(ethUtil.addHexPrefix(gasPrice))
+ updateGasLimit(ethUtil.addHexPrefix(gasLimit))
+ updateGasTotal(ethUtil.addHexPrefix(gasTotal))
hideModal()
}
@@ -218,7 +221,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
}
CustomizeGasModal.prototype.render = function () {
- const { hideModal } = this.props
+ const { hideModal, forceGasMin } = this.props
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
let convertedGasPrice = conversionUtil(gasPrice, {
@@ -230,6 +233,22 @@ CustomizeGasModal.prototype.render = function () {
convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}`
+ let newGasPrice = gasPrice
+ if (forceGasMin) {
+ const convertedMinPrice = conversionUtil(forceGasMin, {
+ fromNumericBase: 'hex',
+ toNumericBase: 'dec',
+ })
+ convertedGasPrice = conversionMax(
+ { value: convertedMinPrice, fromNumericBase: 'dec' },
+ { value: convertedGasPrice, fromNumericBase: 'dec' }
+ )
+ newGasPrice = conversionMax(
+ { value: gasPrice, fromNumericBase: 'hex' },
+ { value: forceGasMin, fromNumericBase: 'hex' }
+ )
+ }
+
const convertedGasLimit = conversionUtil(gasLimit, {
fromNumericBase: 'hex',
toNumericBase: 'dec',
@@ -252,7 +271,7 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, {
value: convertedGasPrice,
- min: MIN_GAS_PRICE_GWEI,
+ min: forceGasMin || MIN_GAS_PRICE_GWEI,
// max: 1000,
step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10),
onChange: value => this.convertAndSetGasPrice(value),
@@ -288,7 +307,7 @@ CustomizeGasModal.prototype.render = function () {
}, [t('cancel')]),
h(`div.send-v2__customize-gas__save${error ? '__error' : ''}.allcaps`, {
- onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
+ onClick: () => !error && this.save(newGasPrice, gasLimit, gasTotal),
}, [t('save')]),
]),
diff --git a/ui/app/components/dropdowns/account-options-dropdown.js b/ui/app/components/dropdowns/account-options-dropdown.js
deleted file mode 100644
index f74c0a2d4..000000000
--- a/ui/app/components/dropdowns/account-options-dropdown.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const AccountDropdowns = require('./components/account-dropdowns')
-
-inherits(AccountOptionsDropdown, Component)
-function AccountOptionsDropdown () {
- Component.call(this)
-}
-
-module.exports = AccountOptionsDropdown
-
-// TODO: specify default props and proptypes
-// TODO: hook up to state, connect to redux to clean up API
-// TODO: selectedAddress is not defined... should we use selected?
-AccountOptionsDropdown.prototype.render = function () {
- const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
-
- return h(AccountDropdowns, {
- enableAccountOptions: true,
- enableAccountsSelector: false,
- selected,
- network,
- identities,
- style: style || {},
- dropdownWrapperStyle: dropdownWrapperStyle || {},
- menuItemStyles: menuItemStyles || {},
- }, [])
-}
diff --git a/ui/app/components/dropdowns/account-selection-dropdown.js b/ui/app/components/dropdowns/account-selection-dropdown.js
deleted file mode 100644
index 2f6452b15..000000000
--- a/ui/app/components/dropdowns/account-selection-dropdown.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const AccountDropdowns = require('./components/account-dropdowns')
-
-inherits(AccountSelectionDropdown, Component)
-function AccountSelectionDropdown () {
- Component.call(this)
-}
-
-module.exports = AccountSelectionDropdown
-
-// TODO: specify default props and proptypes
-// TODO: hook up to state, connect to redux to clean up API
-// TODO: selectedAddress is not defined... should we use selected?
-AccountSelectionDropdown.prototype.render = function () {
- const { selected, network, identities, style, dropdownWrapperStyle, menuItemStyles } = this.props
-
- return h(AccountDropdowns, {
- enableAccountOptions: false,
- enableAccountsSelector: true,
- selected,
- network,
- identities,
- style: style || {},
- dropdownWrapperStyle: dropdownWrapperStyle || {},
- menuItemStyles: menuItemStyles || {},
- }, [])
-}
diff --git a/ui/app/components/dropdowns/index.js b/ui/app/components/dropdowns/index.js
index fa66f5000..605507058 100644
--- a/ui/app/components/dropdowns/index.js
+++ b/ui/app/components/dropdowns/index.js
@@ -1,17 +1,11 @@
// Reusable Dropdown Components
// TODO: Refactor into separate components
const Dropdown = require('./components/dropdown').Dropdown
-const AccountDropdowns = require('./components/account-dropdowns')
// App-Specific Instances
-const AccountSelectionDropdown = require('./account-selection-dropdown')
-const AccountOptionsDropdown = require('./account-options-dropdown')
const NetworkDropdown = require('./network-dropdown').default
module.exports = {
- AccountSelectionDropdown,
- AccountOptionsDropdown,
NetworkDropdown,
Dropdown,
- AccountDropdowns,
}
diff --git a/ui/app/components/mini-account-panel.js b/ui/app/components/mini-account-panel.js
deleted file mode 100644
index c09cf5b7a..000000000
--- a/ui/app/components/mini-account-panel.js
+++ /dev/null
@@ -1,74 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const Identicon = require('./identicon')
-
-module.exports = AccountPanel
-
-
-inherits(AccountPanel, Component)
-function AccountPanel () {
- Component.call(this)
-}
-
-AccountPanel.prototype.render = function () {
- var props = this.props
- var picOrder = props.picOrder || 'left'
- const { imageSeed } = props
-
- return (
-
- h('.identity-panel.flex-row.flex-left', {
- style: {
- cursor: props.onClick ? 'pointer' : undefined,
- },
- onClick: props.onClick,
- }, [
-
- this.genIcon(imageSeed, picOrder),
-
- h('div.flex-column.flex-justify-center', {
- style: {
- lineHeight: '15px',
- order: 2,
- display: 'flex',
- alignItems: picOrder === 'left' ? 'flex-begin' : 'flex-end',
- },
- }, this.props.children),
- ])
- )
-}
-
-AccountPanel.prototype.genIcon = function (seed, picOrder) {
- const props = this.props
-
- // When there is no seed value, this is a contract creation.
- // We then show the contract icon.
- if (!seed) {
- return h('.identicon-wrapper.flex-column.select-none', {
- style: {
- order: picOrder === 'left' ? 1 : 3,
- },
- }, [
- h('i.fa.fa-file-text-o.fa-lg', {
- style: {
- fontSize: '42px',
- transform: 'translate(0px, -16px)',
- },
- }),
- ])
- }
-
- // If there was a seed, we return an identicon for that address.
- return h('.identicon-wrapper.flex-column.select-none', {
- style: {
- order: picOrder === 'left' ? 1 : 3,
- },
- }, [
- h(Identicon, {
- address: seed,
- imageify: props.imageifyIdenticons,
- }),
- ])
-}
-
diff --git a/ui/app/components/pending-personal-msg-details.js b/ui/app/components/pending-personal-msg-details.js
deleted file mode 100644
index b896e9a7e..000000000
--- a/ui/app/components/pending-personal-msg-details.js
+++ /dev/null
@@ -1,60 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const t = require('../../i18n')
-
-const AccountPanel = require('./account-panel')
-const BinaryRenderer = require('./binary-renderer')
-
-module.exports = PendingMsgDetails
-
-inherits(PendingMsgDetails, Component)
-function PendingMsgDetails () {
- Component.call(this)
-}
-
-PendingMsgDetails.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- var msgParams = msgData.msgParams || {}
- var address = msgParams.from || state.selectedAddress
- var identity = state.identities[address] || { address: address }
- var account = state.accounts[address] || { address: address }
-
- var { data } = msgParams
-
- return (
- h('div', {
- key: msgData.id,
- style: {
- margin: '10px 20px',
- },
- }, [
-
- // account that will sign
- h(AccountPanel, {
- showFullAddress: true,
- identity: identity,
- account: account,
- imageifyIdenticons: state.imageifyIdenticons,
- }),
-
- // message data
- h('div', {
- style: {
- height: '260px',
- },
- }, [
- h('label.font-small.allcaps', { style: { display: 'block' } }, t('message')),
- h(BinaryRenderer, {
- value: data,
- style: {
- height: '215px',
- },
- }),
- ]),
-
- ])
- )
-}
diff --git a/ui/app/components/pending-tx/confirm-send-ether.js b/ui/app/components/pending-tx/confirm-send-ether.js
index 908df3671..f36def9d5 100644
--- a/ui/app/components/pending-tx/confirm-send-ether.js
+++ b/ui/app/components/pending-tx/confirm-send-ether.js
@@ -8,7 +8,12 @@ const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-bn')
-const { conversionUtil, addCurrencies } = require('../../conversion-util')
+const {
+ conversionUtil,
+ addCurrencies,
+ multiplyCurrencies,
+} = require('../../conversion-util')
+const GasFeeDisplay = require('../send/gas-fee-display-v2')
const t = require('../../../i18n')
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
@@ -44,6 +49,7 @@ function mapDispatchToProps (dispatch) {
to,
value: amount,
} = txParams
+
dispatch(actions.updateSend({
gasLimit,
gasPrice,
@@ -56,6 +62,29 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.showSendPage())
},
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })),
+ showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => {
+ const { id, txParams, lastGasPrice } = txMeta
+ const { gas: txGasLimit, gasPrice: txGasPrice } = txParams
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ fromDenomination: 'WEI',
+ }))
+ }
+
+ dispatch(actions.updateSend({
+ gasLimit: sendGasLimit || txGasLimit,
+ gasPrice: sendGasPrice || txGasPrice,
+ editingTransactionId: id,
+ gasTotal: sendGasTotal,
+ forceGasMin,
+ }))
+ dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
+ },
}
}
@@ -140,6 +169,7 @@ ConfirmSendEther.prototype.getGasFee = function () {
return {
FIAT,
ETH,
+ gasFeeInHex: txFeeBn.toString(16),
}
}
@@ -147,7 +177,7 @@ ConfirmSendEther.prototype.getData = function () {
const { identities } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
- const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH } = this.getGasFee()
+ const { FIAT: gasFeeInFIAT, ETH: gasFeeInETH, gasFeeInHex } = this.getGasFee()
const { FIAT: amountInFIAT, ETH: amountInETH } = this.getAmount()
const totalInFIAT = addCurrencies(gasFeeInFIAT, amountInFIAT, {
@@ -175,11 +205,20 @@ ConfirmSendEther.prototype.getData = function () {
amountInETH,
totalInFIAT,
totalInETH,
+ gasFeeInHex,
}
}
ConfirmSendEther.prototype.render = function () {
- const { editTransaction, currentCurrency, clearSend } = this.props
+ const {
+ editTransaction,
+ currentCurrency,
+ clearSend,
+ conversionRate,
+ currentCurrency: convertedCurrency,
+ showCustomizeGasModal,
+ send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice },
+ } = this.props
const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {}
@@ -193,13 +232,17 @@ ConfirmSendEther.prototype.render = function () {
name: toName,
},
memo,
- gasFeeInFIAT,
- gasFeeInETH,
+ gasFeeInHex,
amountInFIAT,
totalInFIAT,
totalInETH,
} = this.getData()
+ const title = txMeta.lastGasPrice ? 'Reprice Transaction' : 'Confirm'
+ const subtitle = txMeta.lastGasPrice
+ ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
+ : 'Please review your transaction.'
+
// This is from the latest master
// It handles some of the errors that we are not currently handling
// Leaving as comments fo reference
@@ -218,11 +261,11 @@ ConfirmSendEther.prototype.render = function () {
// Main Send token Card
h('div.page-container', [
h('div.page-container__header', [
- h('button.confirm-screen-back-button', {
+ !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
onClick: () => editTransaction(txMeta),
}, 'Edit'),
- h('div.page-container__title', 'Confirm'),
- h('div.page-container__subtitle', `Please review your transaction.`),
+ h('div.page-container__title', title),
+ h('div.page-container__subtitle', subtitle),
]),
h('.page-container__content', [
h('div.flex-row.flex-center.confirm-screen-identicons', [
@@ -286,13 +329,15 @@ ConfirmSendEther.prototype.render = function () {
h('section.flex-row.flex-center.confirm-screen-row', [
h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${gasFeeInFIAT} ${currentCurrency.toUpperCase()}`),
-
- h('div.confirm-screen-row-detail', `${gasFeeInETH} ETH`),
+ h(GasFeeDisplay, {
+ gasTotal: gasTotal || gasFeeInHex,
+ conversionRate,
+ convertedCurrency,
+ onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
+ }),
]),
]),
-
h('section.flex-row.flex-center.confirm-screen-row.confirm-screen-total-box ', [
h('div.confirm-screen-section-column', [
h('span.confirm-screen-label', [ t('total') + ' ' ]),
@@ -450,6 +495,27 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
+ const { gasPrice: sendGasPrice, gas: sendGasLimit } = props.send
+ const {
+ lastGasPrice,
+ txParams: {
+ gasPrice: txGasPrice,
+ gas: txGasLimit,
+ },
+ } = txData
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ }))
+ }
+
+ txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice
+ txData.txParams.gas = sendGasLimit || txGasLimit
+
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/pending-tx/confirm-send-token.js b/ui/app/components/pending-tx/confirm-send-token.js
index 0a4182014..ccd87c0a4 100644
--- a/ui/app/components/pending-tx/confirm-send-token.js
+++ b/ui/app/components/pending-tx/confirm-send-token.js
@@ -9,6 +9,7 @@ const actions = require('../../actions')
const t = require('../../../i18n')
const clone = require('clone')
const Identicon = require('../identicon')
+const GasFeeDisplay = require('../send/gas-fee-display-v2.js')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const {
@@ -89,6 +90,39 @@ function mapDispatchToProps (dispatch, ownProps) {
}))
dispatch(actions.showSendTokenPage())
},
+ showCustomizeGasModal: (txMeta, sendGasLimit, sendGasPrice, sendGasTotal) => {
+ const { id, txParams, lastGasPrice } = txMeta
+ const { gas: txGasLimit, gasPrice: txGasPrice } = txParams
+ const tokenData = txParams.data && abiDecoder.decodeMethod(txParams.data)
+ const { params = [] } = tokenData
+ const { value: to } = params[0] || {}
+ const { value: tokenAmountInDec } = params[1] || {}
+ const tokenAmountInHex = conversionUtil(tokenAmountInDec, {
+ fromNumericBase: 'dec',
+ toNumericBase: 'hex',
+ })
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ fromDenomination: 'WEI',
+ }))
+ }
+
+ dispatch(actions.updateSend({
+ gasLimit: sendGasLimit || txGasLimit,
+ gasPrice: sendGasPrice || txGasPrice,
+ editingTransactionId: id,
+ gasTotal: sendGasTotal,
+ to,
+ amount: tokenAmountInHex,
+ forceGasMin,
+ }))
+ dispatch(actions.showModal({ name: 'CUSTOMIZE_GAS' }))
+ },
}
}
@@ -188,6 +222,7 @@ ConfirmSendToken.prototype.getGasFee = function () {
token: tokenExchangeRate
? tokenGas
: null,
+ gasFeeInHex: gasTotal.toString(16),
}
}
@@ -240,19 +275,25 @@ ConfirmSendToken.prototype.renderHeroAmount = function () {
}
ConfirmSendToken.prototype.renderGasFee = function () {
- const { token: { symbol }, currentCurrency } = this.props
- const { fiat: fiatGas, token: tokenGas, eth: ethGas } = this.getGasFee()
+ const {
+ currentCurrency: convertedCurrency,
+ conversionRate,
+ send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice },
+ showCustomizeGasModal,
+ } = this.props
+ const txMeta = this.gatherTxMeta()
+ const { gasFeeInHex } = this.getGasFee()
return (
h('section.flex-row.flex-center.confirm-screen-row', [
h('span.confirm-screen-label.confirm-screen-section-column', [ t('gasFee') ]),
h('div.confirm-screen-section-column', [
- h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency}`),
-
- h(
- 'div.confirm-screen-row-detail',
- tokenGas ? `${tokenGas} ${symbol}` : `${ethGas} ETH`
- ),
+ h(GasFeeDisplay, {
+ gasTotal: gasTotal || gasFeeInHex,
+ conversionRate,
+ convertedCurrency,
+ onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
+ }),
]),
])
)
@@ -308,16 +349,21 @@ ConfirmSendToken.prototype.render = function () {
this.inputs = []
+ const title = txMeta.lastGasPrice ? 'Reprice Transaction' : t('confirm')
+ const subtitle = txMeta.lastGasPrice
+ ? 'Increase your gas fee to attempt to overwrite and speed up your transaction'
+ : t('pleaseReviewTransaction')
+
return (
h('div.confirm-screen-container.confirm-send-token', [
// Main Send token Card
h('div.page-container', [
h('div.page-container__header', [
- h('button.confirm-screen-back-button', {
+ !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
onClick: () => editTransaction(txMeta),
}, t('edit')),
- h('div.page-container__title', t('confirm')),
- h('div.page-container__subtitle', t('pleaseReviewTransaction')),
+ h('div.page-container__title', title),
+ h('div.page-container__subtitle', subtitle),
]),
h('.page-container__content', [
h('div.flex-row.flex-center.confirm-screen-identicons', [
@@ -441,6 +487,27 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
const state = this.state
const txData = clone(state.txData) || clone(props.txData)
+ const { gasPrice: sendGasPrice, gas: sendGasLimit } = props.send
+ const {
+ lastGasPrice,
+ txParams: {
+ gasPrice: txGasPrice,
+ gas: txGasLimit,
+ },
+ } = txData
+
+ let forceGasMin
+ if (lastGasPrice) {
+ forceGasMin = ethUtil.addHexPrefix(multiplyCurrencies(lastGasPrice, 1.1, {
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ toNumericBase: 'hex',
+ }))
+ }
+
+ txData.txParams.gasPrice = sendGasPrice || forceGasMin || txGasPrice
+ txData.txParams.gas = sendGasLimit || txGasLimit
+
// log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData
}
diff --git a/ui/app/components/pending-typed-msg-details.js b/ui/app/components/pending-typed-msg-details.js
deleted file mode 100644
index ae0a1171e..000000000
--- a/ui/app/components/pending-typed-msg-details.js
+++ /dev/null
@@ -1,60 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const AccountPanel = require('./account-panel')
-const TypedMessageRenderer = require('./typed-message-renderer')
-const t = require('../../i18n')
-
-module.exports = PendingMsgDetails
-
-inherits(PendingMsgDetails, Component)
-function PendingMsgDetails () {
- Component.call(this)
-}
-
-PendingMsgDetails.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- var msgParams = msgData.msgParams || {}
- var address = msgParams.from || state.selectedAddress
- var identity = state.identities[address] || { address: address }
- var account = state.accounts[address] || { address: address }
-
- var { data } = msgParams
-
- return (
- h('div', {
- key: msgData.id,
- style: {
- margin: '10px 20px',
- },
- }, [
-
- // account that will sign
- h(AccountPanel, {
- showFullAddress: true,
- identity: identity,
- account: account,
- imageifyIdenticons: state.imageifyIdenticons,
- }),
-
- // message data
- h('div', {
- style: {
- height: '260px',
- },
- }, [
- h('label.font-small.allcaps', { style: { display: 'block' } }, t('youSign')),
- h(TypedMessageRenderer, {
- value: data,
- style: {
- height: '215px',
- },
- }),
- ]),
-
- ])
- )
-}
diff --git a/ui/app/components/pending-typed-msg.js b/ui/app/components/pending-typed-msg.js
deleted file mode 100644
index ccde5e8af..000000000
--- a/ui/app/components/pending-typed-msg.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const PendingTxDetails = require('./pending-typed-msg-details')
-const t = require('../../i18n')
-
-module.exports = PendingMsg
-
-inherits(PendingMsg, Component)
-function PendingMsg () {
- Component.call(this)
-}
-
-PendingMsg.prototype.render = function () {
- var state = this.props
- var msgData = state.txData
-
- return (
-
- h('div', {
- key: msgData.id,
- }, [
-
- // header
- h('h3', {
- style: {
- fontWeight: 'bold',
- textAlign: 'center',
- },
- }, t('signMessage')),
-
- // message details
- h(PendingTxDetails, state),
-
- // sign + cancel
- h('.flex-row.flex-space-around', [
- h('button.allcaps', {
- onClick: state.cancelTypedMessage,
- }, t('cancel')),
- h('button.allcaps', {
- onClick: state.signTypedMessage,
- }, t('sign')),
- ]),
- ])
-
- )
-}
diff --git a/ui/app/components/range-slider.js b/ui/app/components/range-slider.js
deleted file mode 100644
index 823f5eb01..000000000
--- a/ui/app/components/range-slider.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = RangeSlider
-
-inherits(RangeSlider, Component)
-function RangeSlider () {
- Component.call(this)
-}
-
-RangeSlider.prototype.render = function () {
- const state = this.state || {}
- const props = this.props
- const onInput = props.onInput || function () {}
- const name = props.name
- const {
- min = 0,
- max = 100,
- increment = 1,
- defaultValue = 50,
- mirrorInput = false,
- } = this.props.options
- const {container, input, range} = props.style
-
- return (
- h('.flex-row', {
- style: container,
- }, [
- h('input', {
- type: 'range',
- name: name,
- min: min,
- max: max,
- step: increment,
- style: range,
- value: state.value || defaultValue,
- onChange: mirrorInput ? this.mirrorInputs.bind(this, event) : onInput,
- }),
-
- // Mirrored input for range
- mirrorInput ? h('input.large-input', {
- type: 'number',
- name: `${name}Mirror`,
- min: min,
- max: max,
- value: state.value || defaultValue,
- step: increment,
- style: input,
- onChange: this.mirrorInputs.bind(this, event),
- }) : null,
- ])
- )
-}
-
-RangeSlider.prototype.mirrorInputs = function (event) {
- this.setState({value: event.target.value})
-}
diff --git a/ui/app/components/send-token/index.js b/ui/app/components/send-token/index.js
deleted file mode 100644
index 58743b641..000000000
--- a/ui/app/components/send-token/index.js
+++ /dev/null
@@ -1,440 +0,0 @@
-const Component = require('react').Component
-const connect = require('react-redux').connect
-const h = require('react-hyperscript')
-const classnames = require('classnames')
-const abi = require('ethereumjs-abi')
-const inherits = require('util').inherits
-const actions = require('../../actions')
-const selectors = require('../../selectors')
-const { isValidAddress, allNull } = require('../../util')
-const t = require('../../../i18n')
-
-// const BalanceComponent = require('./balance-component')
-const Identicon = require('../identicon')
-const TokenBalance = require('../token-balance')
-const CurrencyToggle = require('../send/currency-toggle')
-const GasTooltip = require('../send/gas-tooltip')
-const GasFeeDisplay = require('../send/gas-fee-display')
-
-module.exports = connect(mapStateToProps, mapDispatchToProps)(SendTokenScreen)
-
-function mapStateToProps (state) {
- // const sidebarOpen = state.appState.sidebarOpen
-
- const { warning } = state.appState
- const identities = state.metamask.identities
- const addressBook = state.metamask.addressBook
- const conversionRate = state.metamask.conversionRate
- const currentBlockGasLimit = state.metamask.currentBlockGasLimit
- const accounts = state.metamask.accounts
- const selectedTokenAddress = state.metamask.selectedTokenAddress
- const selectedAddress = state.metamask.selectedAddress || Object.keys(accounts)[0]
- const selectedToken = selectors.getSelectedToken(state)
- const tokenExchangeRates = state.metamask.tokenExchangeRates
- const pair = `${selectedToken.symbol.toLowerCase()}_eth`
- const { rate: tokenExchangeRate = 0 } = tokenExchangeRates[pair] || {}
-
- return {
- selectedAddress,
- selectedTokenAddress,
- identities,
- addressBook,
- conversionRate,
- tokenExchangeRate,
- currentBlockGasLimit,
- selectedToken,
- warning,
- }
-}
-
-function mapDispatchToProps (dispatch) {
- return {
- backToAccountDetail: address => dispatch(actions.backToAccountDetail(address)),
- hideWarning: () => dispatch(actions.hideWarning()),
- addToAddressBook: (recipient, nickname) => dispatch(
- actions.addToAddressBook(recipient, nickname)
- ),
- signTx: txParams => dispatch(actions.signTx(txParams)),
- signTokenTx: (tokenAddress, toAddress, amount, txData) => (
- dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData))
- ),
- updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)),
- estimateGas: params => dispatch(actions.estimateGas(params)),
- getGasPrice: () => dispatch(actions.getGasPrice()),
- }
-}
-
-inherits(SendTokenScreen, Component)
-function SendTokenScreen () {
- Component.call(this)
- this.state = {
- to: '',
- amount: '0x0',
- amountToSend: '0x0',
- selectedCurrency: 'USD',
- isGasTooltipOpen: false,
- gasPrice: null,
- gasLimit: null,
- errors: {},
- }
-}
-
-SendTokenScreen.prototype.componentWillMount = function () {
- const {
- updateTokenExchangeRate,
- selectedToken: { symbol },
- getGasPrice,
- estimateGas,
- selectedAddress,
- } = this.props
-
- updateTokenExchangeRate(symbol)
-
- const data = Array.prototype.map.call(
- abi.rawEncode(['address', 'uint256'], [selectedAddress, '0x0']),
- x => ('00' + x.toString(16)).slice(-2)
- ).join('')
-
- console.log(data)
- Promise.all([
- getGasPrice(),
- estimateGas({
- from: selectedAddress,
- value: '0x0',
- gas: '746a528800',
- data,
- }),
- ])
- .then(([blockGasPrice, estimatedGas]) => {
- console.log({ blockGasPrice, estimatedGas})
- this.setState({
- gasPrice: blockGasPrice,
- gasLimit: estimatedGas,
- })
- })
-}
-
-SendTokenScreen.prototype.validate = function () {
- const {
- to,
- amount: stringAmount,
- gasPrice: hexGasPrice,
- gasLimit: hexGasLimit,
- } = this.state
-
- const gasPrice = parseInt(hexGasPrice, 16)
- const gasLimit = parseInt(hexGasLimit, 16) / 1000000000
- const amount = Number(stringAmount)
-
- const errors = {
- to: !to ? t('required') : null,
- amount: !amount ? t('required') : null,
- gasPrice: !gasPrice ? t('gasPriceRequired') : null,
- gasLimit: !gasLimit ? t('gasLimitRequired') : null,
- }
-
- if (to && !isValidAddress(to)) {
- errors.to = t('invalidAddress')
- }
-
- const isValid = Object.entries(errors).every(([key, value]) => value === null)
- return {
- isValid,
- errors: isValid ? {} : errors,
- }
-}
-
-SendTokenScreen.prototype.setErrorsFor = function (field) {
- const { errors: previousErrors } = this.state
-
- const {
- isValid,
- errors: newErrors,
- } = this.validate()
-
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: newErrors[field] || null,
- })
-
- if (!isValid) {
- this.setState({
- errors: nextErrors,
- isValid,
- })
- }
-}
-
-SendTokenScreen.prototype.clearErrorsFor = function (field) {
- const { errors: previousErrors } = this.state
- const nextErrors = Object.assign({}, previousErrors, {
- [field]: null,
- })
-
- this.setState({
- errors: nextErrors,
- isValid: allNull(nextErrors),
- })
-}
-
-SendTokenScreen.prototype.getAmountToSend = function (amount, selectedToken) {
- const { decimals } = selectedToken || {}
- const multiplier = Math.pow(10, Number(decimals || 0))
- const sendAmount = '0x' + Number(amount * multiplier).toString(16)
- return sendAmount
-}
-
-SendTokenScreen.prototype.submit = function () {
- const {
- to,
- amount,
- gasPrice,
- gasLimit,
- } = this.state
-
- const {
- identities,
- selectedAddress,
- selectedTokenAddress,
- hideWarning,
- addToAddressBook,
- signTokenTx,
- selectedToken,
- } = this.props
-
- const { nickname = ' ' } = identities[to] || {}
-
- hideWarning()
- addToAddressBook(to, nickname)
-
- const txParams = {
- from: selectedAddress,
- value: '0',
- gas: gasLimit,
- gasPrice: gasPrice,
- }
-
- const sendAmount = this.getAmountToSend(amount, selectedToken)
-
- signTokenTx(selectedTokenAddress, to, sendAmount, txParams)
-}
-
-SendTokenScreen.prototype.renderToAddressInput = function () {
- const {
- identities,
- addressBook,
- } = this.props
-
- const {
- to,
- errors: { to: errorMessage },
- } = this.state
-
- return h('div', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': errorMessage,
- }),
- }, [
- h('div', [t('to') + ':']),
- h('input.large-input.send-screen-input', {
- name: 'address',
- list: 'addresses',
- placeholder: t('address'),
- value: to,
- onChange: e => this.setState({
- to: e.target.value,
- errors: {},
- }),
- onBlur: () => {
- this.setErrorsFor('to')
- },
- onFocus: event => {
- if (to) event.target.select()
- this.clearErrorsFor('to')
- },
- }),
- h('datalist#addresses', [
- // Corresponds to the addresses owned.
- Object.entries(identities).map(([key, { address, name }]) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- addressBook.map(({ address, name }) => {
- return h('option', {
- value: address,
- label: name,
- key: address,
- })
- }),
- ]),
- h('div.send-screen-input-wrapper__error-message', [ errorMessage ]),
- ])
-}
-
-SendTokenScreen.prototype.renderAmountInput = function () {
- const {
- selectedCurrency,
- amount,
- errors: { amount: errorMessage },
- } = this.state
-
- const {
- tokenExchangeRate,
- selectedToken: {symbol},
- } = this.props
-
- return h('div.send-screen-input-wrapper', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': errorMessage,
- }),
- }, [
- h('div.send-screen-amount-labels', [
- h('span', [t('amount')]),
- h(CurrencyToggle, {
- currentCurrency: tokenExchangeRate ? selectedCurrency : 'USD',
- currencies: tokenExchangeRate ? [ symbol, 'USD' ] : [],
- onClick: currency => this.setState({ selectedCurrency: currency }),
- }),
- ]),
- h('input.large-input.send-screen-input', {
- placeholder: `0 ${symbol}`,
- type: 'number',
- value: amount,
- onChange: e => this.setState({
- amount: e.target.value,
- }),
- onBlur: () => {
- this.setErrorsFor('amount')
- },
- onFocus: () => this.clearErrorsFor('amount'),
- }),
- h('div.send-screen-input-wrapper__error-message', [ errorMessage ]),
- ])
-}
-
-SendTokenScreen.prototype.renderGasInput = function () {
- const {
- isGasTooltipOpen,
- gasPrice,
- gasLimit,
- selectedCurrency,
- errors: {
- gasPrice: gasPriceErrorMessage,
- gasLimit: gasLimitErrorMessage,
- },
- } = this.state
-
- const {
- conversionRate,
- tokenExchangeRate,
- currentBlockGasLimit,
- } = this.props
-
- return h('div.send-screen-input-wrapper', {
- className: classnames('send-screen-input-wrapper', {
- 'send-screen-input-wrapper--error': gasPriceErrorMessage || gasLimitErrorMessage,
- }),
- }, [
- isGasTooltipOpen && h(GasTooltip, {
- className: 'send-tooltip',
- gasPrice: gasPrice || '0x0',
- gasLimit: gasLimit || '0x0',
- onClose: () => this.setState({ isGasTooltipOpen: false }),
- onFeeChange: ({ gasLimit, gasPrice }) => {
- this.setState({ gasLimit, gasPrice, errors: {} })
- },
- onBlur: () => {
- this.setErrorsFor('gasLimit')
- this.setErrorsFor('gasPrice')
- },
- onFocus: () => {
- this.clearErrorsFor('gasLimit')
- this.clearErrorsFor('gasPrice')
- },
- }),
-
- h('div.send-screen-gas-labels', {}, [
- h('span', [ h('i.fa.fa-bolt'), t('gasFee') + ':']),
- h('span', [t('whatsThis')]),
- ]),
- h('div.large-input.send-screen-gas-input', [
- h(GasFeeDisplay, {
- conversionRate,
- tokenExchangeRate,
- gasPrice: gasPrice || '0x0',
- activeCurrency: selectedCurrency,
- gas: gasLimit || '0x0',
- blockGasLimit: currentBlockGasLimit,
- }),
- h(
- 'div.send-screen-gas-input-customize',
- { onClick: () => this.setState({ isGasTooltipOpen: !isGasTooltipOpen }) },
- [t('customize')]
- ),
- ]),
- h('div.send-screen-input-wrapper__error-message', [
- gasPriceErrorMessage || gasLimitErrorMessage,
- ]),
- ])
-}
-
-SendTokenScreen.prototype.renderMemoInput = function () {
- return h('div.send-screen-input-wrapper', [
- h('div', {}, [t('transactionMemo')]),
- h(
- 'input.large-input.send-screen-input',
- { onChange: e => this.setState({ memo: e.target.value }) }
- ),
- ])
-}
-
-SendTokenScreen.prototype.renderButtons = function () {
- const { selectedAddress, backToAccountDetail } = this.props
- const { isValid } = this.validate()
-
- return h('div.send-token__button-group', [
- h('button.send-token__button-next.btn-secondary', {
- className: !isValid && 'send-screen__send-button__disabled',
- onClick: () => isValid && this.submit(),
- }, [t('next')]),
- h('button.send-token__button-cancel.btn-tertiary', {
- onClick: () => backToAccountDetail(selectedAddress),
- }, [t('cancel')]),
- ])
-}
-
-SendTokenScreen.prototype.render = function () {
- const {
- selectedTokenAddress,
- selectedToken,
- warning,
- } = this.props
-
- return h('div.send-token', [
- h('div.send-token__content', [
- h(Identicon, {
- diameter: 75,
- address: selectedTokenAddress,
- }),
- h('div.send-token__title', [t('sendTokens')]),
- h('div.send-token__description', [t('sendTokensAnywhere')]),
- h('div.send-token__balance-text', [t('tokenBalance')]),
- h('div.send-token__token-balance', [
- h(TokenBalance, { token: selectedToken, balanceOnly: true }),
- ]),
- h('div.send-token__token-symbol', [selectedToken.symbol]),
- this.renderToAddressInput(),
- this.renderAmountInput(),
- this.renderGasInput(),
- this.renderMemoInput(),
- warning && h('div.send-screen-input-wrapper--error', {},
- h('div.send-screen-input-wrapper__error-message', [
- warning,
- ])
- ),
- ]),
- this.renderButtons(),
- ])
-}
diff --git a/ui/app/components/send/currency-toggle.js b/ui/app/components/send/currency-toggle.js
deleted file mode 100644
index 7aaccd490..000000000
--- a/ui/app/components/send/currency-toggle.js
+++ /dev/null
@@ -1,44 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const classnames = require('classnames')
-
-module.exports = CurrencyToggle
-
-inherits(CurrencyToggle, Component)
-function CurrencyToggle () {
- Component.call(this)
-}
-
-const defaultCurrencies = [ 'ETH', 'USD' ]
-
-CurrencyToggle.prototype.renderToggles = function () {
- const { onClick, activeCurrency } = this.props
- const [currencyA, currencyB] = this.props.currencies || defaultCurrencies
-
- return [
- h('span', {
- className: classnames('currency-toggle__item', {
- 'currency-toggle__item--selected': currencyA === activeCurrency,
- }),
- onClick: () => onClick(currencyA),
- }, [ currencyA ]),
- '<>',
- h('span', {
- className: classnames('currency-toggle__item', {
- 'currency-toggle__item--selected': currencyB === activeCurrency,
- }),
- onClick: () => onClick(currencyB),
- }, [ currencyB ]),
- ]
-}
-
-CurrencyToggle.prototype.render = function () {
- const currencies = this.props.currencies || defaultCurrencies
-
- return h('span.currency-toggle', currencies.length
- ? this.renderToggles()
- : []
- )
-}
-
diff --git a/ui/app/components/send/eth-fee-display.js b/ui/app/components/send/eth-fee-display.js
deleted file mode 100644
index 9eda5ec62..000000000
--- a/ui/app/components/send/eth-fee-display.js
+++ /dev/null
@@ -1,37 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const EthBalance = require('../eth-balance')
-const { getTxFeeBn } = require('../../util')
-
-module.exports = EthFeeDisplay
-
-inherits(EthFeeDisplay, Component)
-function EthFeeDisplay () {
- Component.call(this)
-}
-
-EthFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- return h(EthBalance, {
- value: getTxFeeBn(gas, gasPrice, blockGasLimit),
- currentCurrency: activeCurrency,
- conversionRate,
- showFiat: false,
- hideTooltip: true,
- styleOveride: {
- color: '#5d5d5d',
- fontSize: '16px',
- fontFamily: 'DIN OT',
- lineHeight: '22.4px',
- },
- })
-}
-
diff --git a/ui/app/components/send/gas-fee-display-v2.js b/ui/app/components/send/gas-fee-display-v2.js
index 0c6f76303..f6af13454 100644
--- a/ui/app/components/send/gas-fee-display-v2.js
+++ b/ui/app/components/send/gas-fee-display-v2.js
@@ -18,6 +18,7 @@ GasFeeDisplay.prototype.render = function () {
onClick,
primaryCurrency = 'ETH',
convertedCurrency,
+ gasLoadingError,
} = this.props
return h('div.send-v2__gas-fee-display', [
@@ -31,13 +32,15 @@ GasFeeDisplay.prototype.render = function () {
convertedPrefix: '$',
readOnly: true,
})
- : h('div.currency-display', t('loading')),
+ : gasLoadingError
+ ? h('div..currency-display.currency-display--message', 'Set with the gas price customizer.')
+ : h('div.currency-display', t('loading')),
- h('button.send-v2__sliders-icon-container', {
+ h('button.sliders-icon-container', {
onClick,
- disabled: !gasTotal,
+ disabled: !gasTotal && !gasLoadingError,
}, [
- h('i.fa.fa-sliders.send-v2__sliders-icon'),
+ h('i.fa.fa-sliders.sliders-icon'),
]),
])
diff --git a/ui/app/components/send/gas-fee-display.js b/ui/app/components/send/gas-fee-display.js
deleted file mode 100644
index a9a3f3f49..000000000
--- a/ui/app/components/send/gas-fee-display.js
+++ /dev/null
@@ -1,62 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const USDFeeDisplay = require('./usd-fee-display')
-const EthFeeDisplay = require('./eth-fee-display')
-const { getTxFeeBn, formatBalance, shortenBalance } = require('../../util')
-
-module.exports = GasFeeDisplay
-
-inherits(GasFeeDisplay, Component)
-function GasFeeDisplay () {
- Component.call(this)
-}
-
-GasFeeDisplay.prototype.getTokenValue = function () {
- const {
- tokenExchangeRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- const value = formatBalance(getTxFeeBn(gas, gasPrice, blockGasLimit), 6, true)
- const [ethNumber] = value.split(' ')
-
- return shortenBalance(Number(ethNumber) / tokenExchangeRate, 6)
-}
-
-GasFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- switch (activeCurrency) {
- case 'USD':
- return h(USDFeeDisplay, {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- })
- case 'ETH':
- return h(EthFeeDisplay, {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- })
- default:
- return h('div.token-gas', [
- h('div.token-gas__amount', this.getTokenValue()),
- h('div.token-gas__symbol', activeCurrency),
- ])
- }
-}
-
diff --git a/ui/app/components/send/send-v2-container.js b/ui/app/components/send/send-v2-container.js
index 1106902b7..d1319b6dc 100644
--- a/ui/app/components/send/send-v2-container.js
+++ b/ui/app/components/send/send-v2-container.js
@@ -48,6 +48,7 @@ function mapStateToProps (state) {
primaryCurrency,
convertedCurrency: getCurrentCurrency(state),
data,
+ selectedAddress,
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
tokenContract: getSelectedTokenContract(state),
unapprovedTxs: state.metamask.unapprovedTxs,
diff --git a/ui/app/components/send/usd-fee-display.js b/ui/app/components/send/usd-fee-display.js
deleted file mode 100644
index 4cf31a493..000000000
--- a/ui/app/components/send/usd-fee-display.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const FiatValue = require('../fiat-value')
-const { getTxFeeBn } = require('../../util')
-
-module.exports = USDFeeDisplay
-
-inherits(USDFeeDisplay, Component)
-function USDFeeDisplay () {
- Component.call(this)
-}
-
-USDFeeDisplay.prototype.render = function () {
- const {
- activeCurrency,
- conversionRate,
- gas,
- gasPrice,
- blockGasLimit,
- } = this.props
-
- return h(FiatValue, {
- value: getTxFeeBn(gas, gasPrice, blockGasLimit),
- conversionRate,
- currentCurrency: activeCurrency,
- style: {
- color: '#5d5d5d',
- fontSize: '16px',
- fontFamily: 'DIN OT',
- lineHeight: '22.4px',
- },
- })
-}
-
diff --git a/ui/app/components/template.js b/ui/app/components/template.js
deleted file mode 100644
index b6ed8eaa0..000000000
--- a/ui/app/components/template.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = NewComponent
-
-inherits(NewComponent, Component)
-function NewComponent () {
- Component.call(this)
-}
-
-NewComponent.prototype.render = function () {
- const props = this.props
-
- return (
- h('span', props.message)
- )
-}
diff --git a/ui/app/components/transaction-list-item-icon.js b/ui/app/components/transaction-list-item-icon.js
deleted file mode 100644
index f442b05af..000000000
--- a/ui/app/components/transaction-list-item-icon.js
+++ /dev/null
@@ -1,68 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const Tooltip = require('./tooltip')
-
-const Identicon = require('./identicon')
-
-module.exports = TransactionIcon
-
-inherits(TransactionIcon, Component)
-function TransactionIcon () {
- Component.call(this)
-}
-
-TransactionIcon.prototype.render = function () {
- const { transaction, txParams, isMsg } = this.props
- switch (transaction.status) {
- case 'unapproved':
- return h(!isMsg ? '.unapproved-tx-icon' : 'i.fa.fa-certificate.fa-lg')
-
- case 'rejected':
- return h('i.fa.fa-exclamation-triangle.fa-lg.warning', {
- style: {
- width: '24px',
- },
- })
-
- case 'failed':
- return h('i.fa.fa-exclamation-triangle.fa-lg.error', {
- style: {
- width: '24px',
- },
- })
-
- case 'submitted':
- return h(Tooltip, {
- title: 'Pending',
- position: 'right',
- }, [
- h('i.fa.fa-ellipsis-h', {
- style: {
- fontSize: '27px',
- },
- }),
- ])
- }
-
- if (isMsg) {
- return h('i.fa.fa-certificate.fa-lg', {
- style: {
- width: '24px',
- },
- })
- }
-
- if (txParams.to) {
- return h(Identicon, {
- diameter: 24,
- address: txParams.to || transaction.hash,
- })
- } else {
- return h('i.fa.fa-file-text-o.fa-lg', {
- style: {
- width: '24px',
- },
- })
- }
-}
diff --git a/ui/app/components/transaction-list-item.js b/ui/app/components/transaction-list-item.js
deleted file mode 100644
index 6d6e79bd5..000000000
--- a/ui/app/components/transaction-list-item.js
+++ /dev/null
@@ -1,239 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const connect = require('react-redux').connect
-
-const EthBalance = require('./eth-balance')
-const addressSummary = require('../util').addressSummary
-const explorerLink = require('etherscan-link').createExplorerLink
-const CopyButton = require('./copyButton')
-const vreme = new (require('vreme'))()
-const Tooltip = require('./tooltip')
-const numberToBN = require('number-to-bn')
-const actions = require('../actions')
-const t = require('../../i18n')
-
-const TransactionIcon = require('./transaction-list-item-icon')
-const ShiftListItem = require('./shift-list-item')
-
-const mapDispatchToProps = dispatch => {
- return {
- retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
- }
-}
-
-module.exports = connect(null, mapDispatchToProps)(TransactionListItem)
-
-inherits(TransactionListItem, Component)
-function TransactionListItem () {
- Component.call(this)
-}
-
-TransactionListItem.prototype.showRetryButton = function () {
- const { transaction = {} } = this.props
- const { status, time } = transaction
- return status === 'submitted' && Date.now() - time > 30000
-}
-
-TransactionListItem.prototype.render = function () {
- const { transaction, network, conversionRate, currentCurrency } = this.props
- const { status } = transaction
- if (transaction.key === 'shapeshift') {
- if (network === '1') return h(ShiftListItem, transaction)
- }
- var date = formatDate(transaction.time)
-
- let isLinkable = false
- const numericNet = parseInt(network)
- isLinkable = numericNet === 1 || numericNet === 3 || numericNet === 4 || numericNet === 42
-
- var isMsg = ('msgParams' in transaction)
- var isTx = ('txParams' in transaction)
- var isPending = status === 'unapproved'
- let txParams
- if (isTx) {
- txParams = transaction.txParams
- } else if (isMsg) {
- txParams = transaction.msgParams
- }
-
- const nonce = txParams.nonce ? numberToBN(txParams.nonce).toString(10) : ''
-
- const isClickable = ('hash' in transaction && isLinkable) || isPending
- return (
- h('.transaction-list-item.flex-column', {
- onClick: (event) => {
- if (isPending) {
- this.props.showTx(transaction.id)
- }
- event.stopPropagation()
- if (!transaction.hash || !isLinkable) return
- var url = explorerLink(transaction.hash, parseInt(network))
- global.platform.openWindow({ url })
- },
- style: {
- padding: '20px 0',
- alignItems: 'center',
- },
- }, [
- h(`.flex-row.flex-space-between${isClickable ? '.pointer' : ''}`, {
- style: {
- width: '100%',
- },
- }, [
- h('.identicon-wrapper.flex-column.flex-center.select-none', [
- h(TransactionIcon, { txParams, transaction, isTx, isMsg }),
- ]),
-
- h(Tooltip, {
- title: t('transactionNumber'),
- position: 'right',
- }, [
- h('span', {
- style: {
- display: 'flex',
- cursor: 'normal',
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- padding: '10px',
- },
- }, nonce),
- ]),
-
- h('.flex-column', {style: {width: '200px', overflow: 'hidden'}}, [
- domainField(txParams),
- h('div', date),
- recipientField(txParams, transaction, isTx, isMsg),
- ]),
-
- // Places a copy button if tx is successful, else places a placeholder empty div.
- transaction.hash ? h(CopyButton, { value: transaction.hash }) : h('div', {style: { display: 'flex', alignItems: 'center', width: '26px' }}),
-
- isTx ? h(EthBalance, {
- value: txParams.value,
- conversionRate,
- currentCurrency,
- width: '55px',
- shorten: true,
- showFiat: false,
- style: {fontSize: '15px'},
- }) : h('.flex-column'),
- ]),
-
- this.showRetryButton() && h('.transition-list-item__retry.grow-on-hover', {
- onClick: event => {
- event.stopPropagation()
- this.resubmit()
- },
- style: {
- height: '22px',
- borderRadius: '22px',
- color: '#F9881B',
- padding: '0 20px',
- backgroundColor: '#FFE3C9',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- fontSize: '8px',
- cursor: 'pointer',
- },
- }, [
- h('div', {
- style: {
- paddingRight: '2px',
- },
- }, t('takesTooLong')),
- h('div', {
- style: {
- textDecoration: 'underline',
- },
- }, t('retryWithMoreGas')),
- ]),
- ])
- )
-}
-
-TransactionListItem.prototype.resubmit = function () {
- const { transaction } = this.props
- this.props.retryTransaction(transaction.id)
-}
-
-function domainField (txParams) {
- return h('div', {
- style: {
- fontSize: 'x-small',
- color: '#ABA9AA',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- width: '100%',
- },
- }, [
- txParams.origin,
- ])
-}
-
-function recipientField (txParams, transaction, isTx, isMsg) {
- let message
-
- if (isMsg) {
- message = t('sigRequested')
- } else if (txParams.to) {
- message = addressSummary(txParams.to)
- } else {
- message = t('contractDeployment')
- }
-
- return h('div', {
- style: {
- fontSize: 'x-small',
- color: '#ABA9AA',
- },
- }, [
- message,
- renderErrorOrWarning(transaction),
- ])
-}
-
-function formatDate (date) {
- return vreme.format(new Date(date), 'March 16 2014 14:30')
-}
-
-function renderErrorOrWarning (transaction) {
- const { status } = transaction
-
- // show rejected
- if (status === 'rejected') {
- return h('span.error', ' (' + t('rejected') + ')')
- }
- if (transaction.err || transaction.warning) {
- const { err, warning = {} } = transaction
- const errFirst = !!((err && warning) || err)
-
- errFirst ? err.message : warning.message
-
- // show error
- if (err) {
- const message = err.message || ''
- return (
- h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.error`, ` (` + t('failed') + `)`),
- ])
- )
- }
-
- // show warning
- if (warning) {
- const message = warning.message
- return h(Tooltip, {
- title: message,
- position: 'bottom',
- }, [
- h(`span.warning`, ` (` + t('warning') + `)`),
- ])
- }
- }
-}
diff --git a/ui/app/components/transaction-list.js b/ui/app/components/transaction-list.js
deleted file mode 100644
index 07f7a06ae..000000000
--- a/ui/app/components/transaction-list.js
+++ /dev/null
@@ -1,87 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-const TransactionListItem = require('./transaction-list-item')
-const t = require('../../i18n')
-
-module.exports = TransactionList
-
-
-inherits(TransactionList, Component)
-function TransactionList () {
- Component.call(this)
-}
-
-TransactionList.prototype.render = function () {
- const { transactions, network, unapprovedMsgs, conversionRate } = this.props
-
- var shapeShiftTxList
- if (network === '1') {
- shapeShiftTxList = this.props.shapeShiftTxList
- }
- const txsToRender = !shapeShiftTxList ? transactions.concat(unapprovedMsgs) : transactions.concat(unapprovedMsgs, shapeShiftTxList)
- .sort((a, b) => b.time - a.time)
-
- return (
-
- h('section.transaction-list.full-flex-height', {
- style: {
- justifyContent: 'center',
- },
- }, [
-
- h('style', `
- .transaction-list .transaction-list-item:not(:last-of-type) {
- border-bottom: 1px solid #D4D4D4;
- }
- .transaction-list .transaction-list-item .ether-balance-label {
- display: block !important;
- font-size: small;
- }
- `),
-
- h('.tx-list', {
- style: {
- overflowY: 'auto',
- height: '100%',
- padding: '0 20px',
- textAlign: 'center',
- },
- }, [
-
- txsToRender.length
- ? txsToRender.map((transaction, i) => {
- let key
- switch (transaction.key) {
- case 'shapeshift':
- const { depositAddress, time } = transaction
- key = `shift-tx-${depositAddress}-${time}-${i}`
- break
- default:
- key = `tx-${transaction.id}-${i}`
- }
- return h(TransactionListItem, {
- transaction, i, network, key,
- conversionRate,
- showTx: (txId) => {
- this.props.viewPendingTx(txId)
- },
- })
- })
- : h('.flex-center.full-flex-height', {
- style: {
- flexDirection: 'column',
- justifyContent: 'center',
- },
- }, [
- h('p', {
- style: {
- marginTop: '50px',
- },
- }, t('noTransactionHistory')),
- ]),
- ]),
- ])
- )
-}
diff --git a/ui/app/components/tx-list-item.js b/ui/app/components/tx-list-item.js
index 849d70489..5ff1820a6 100644
--- a/ui/app/components/tx-list-item.js
+++ b/ui/app/components/tx-list-item.js
@@ -9,19 +9,28 @@ abiDecoder.addABI(abi)
const Identicon = require('./identicon')
const contractMap = require('eth-contract-metadata')
+const actions = require('../actions')
const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const { calcTokenAmount } = require('../token-util')
const { getCurrentCurrency } = require('../selectors')
const t = require('../../i18n')
-module.exports = connect(mapStateToProps)(TxListItem)
+module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem)
function mapStateToProps (state) {
return {
tokens: state.metamask.tokens,
currentCurrency: getCurrentCurrency(state),
tokenExchangeRates: state.metamask.tokenExchangeRates,
+ selectedAddressTxList: state.metamask.selectedAddressTxList,
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ setSelectedToken: tokenAddress => dispatch(actions.setSelectedToken(tokenAddress)),
+ retryTransaction: transactionId => dispatch(actions.retryTransaction(transactionId)),
}
}
@@ -32,6 +41,7 @@ function TxListItem () {
this.state = {
total: null,
fiatTotal: null,
+ isTokenTx: null,
}
}
@@ -40,12 +50,13 @@ TxListItem.prototype.componentDidMount = async function () {
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { name: txDataName } = decodedData || {}
+ const isTokenTx = txDataName === 'transfer'
- const { total, fiatTotal } = txDataName === 'transfer'
+ const { total, fiatTotal } = isTokenTx
? await this.getSendTokenTotal()
: this.getSendEtherTotal()
- this.setState({ total, fiatTotal })
+ this.setState({ total, fiatTotal, isTokenTx })
}
TxListItem.prototype.getAddressText = function () {
@@ -168,22 +179,49 @@ TxListItem.prototype.getSendTokenTotal = async function () {
}
}
+TxListItem.prototype.showRetryButton = function () {
+ const {
+ transactionSubmittedTime,
+ selectedAddressTxList,
+ transactionId,
+ txParams,
+ } = this.props
+ const currentNonce = txParams.nonce
+ const currentNonceTxs = selectedAddressTxList.filter(tx => tx.txParams.nonce === currentNonce)
+ const currentNonceSubmittedTxs = currentNonceTxs.filter(tx => tx.status === 'submitted')
+ const lastSubmittedTxWithCurrentNonce = currentNonceSubmittedTxs[currentNonceSubmittedTxs.length - 1]
+ const currentTxIsLatestWithNonce = lastSubmittedTxWithCurrentNonce
+ && lastSubmittedTxWithCurrentNonce.id === transactionId
+
+ return currentTxIsLatestWithNonce && Date.now() - transactionSubmittedTime > 30000
+}
+
+TxListItem.prototype.setSelectedToken = function (tokenAddress) {
+ this.props.setSelectedToken(tokenAddress)
+}
+
+TxListItem.prototype.resubmit = function () {
+ const { transactionId } = this.props
+ this.props.retryTransaction(transactionId)
+}
+
TxListItem.prototype.render = function () {
const {
transactionStatus,
transactionAmount,
onClick,
- transActionId,
+ transactionId,
dateString,
address,
className,
+ txParams,
} = this.props
- const { total, fiatTotal } = this.state
+ const { total, fiatTotal, isTokenTx } = this.state
const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
return h(`div${className || ''}`, {
- key: transActionId,
- onClick: () => onClick && onClick(transActionId),
+ key: transactionId,
+ onClick: () => onClick && onClick(transactionId),
}, [
h(`div.flex-column.tx-list-item-wrapper`, {}, [
@@ -224,6 +262,7 @@ TxListItem.prototype.render = function () {
className: classnames('tx-list-status', {
'tx-list-status--rejected': transactionStatus === 'rejected',
'tx-list-status--failed': transactionStatus === 'failed',
+ 'tx-list-status--dropped': transactionStatus === 'dropped',
}),
},
transactionStatus,
@@ -241,6 +280,23 @@ TxListItem.prototype.render = function () {
]),
]),
+
+ this.showRetryButton() && h('div.tx-list-item-retry-container', [
+
+ h('span.tx-list-item-retry-copy', 'Taking too long?'),
+
+ h('span.tx-list-item-retry-link', {
+ onClick: (event) => {
+ event.stopPropagation()
+ if (isTokenTx) {
+ this.setSelectedToken(txParams.to)
+ }
+ this.resubmit()
+ },
+ }, 'Increase the gas price on your transaction'),
+
+ ]),
+
]), // holding on icon from design
])
}
diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js
index 34dc837ae..08e37ebc8 100644
--- a/ui/app/components/tx-list.js
+++ b/ui/app/components/tx-list.js
@@ -75,9 +75,10 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
address: transaction.txParams.to,
transactionStatus: transaction.status,
transactionAmount: transaction.txParams.value,
- transActionId: transaction.id,
+ transactionId: transaction.id,
transactionHash: transaction.hash,
transactionNetworkId: transaction.metamaskNetworkId,
+ transactionSubmittedTime: transaction.submittedTime,
}
const {
@@ -85,29 +86,31 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
transactionStatus,
transactionAmount,
dateString,
- transActionId,
+ transactionId,
transactionHash,
transactionNetworkId,
+ transactionSubmittedTime,
} = props
const { showConfTxPage } = this.props
const opts = {
- key: transActionId || transactionHash,
+ key: transactionId || transactionHash,
txParams: transaction.txParams,
transactionStatus,
- transActionId,
+ transactionId,
dateString,
address,
transactionAmount,
transactionHash,
conversionRate,
tokenInfoGetter: this.tokenInfoGetter,
+ transactionSubmittedTime,
}
const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) {
- opts.onClick = () => showConfTxPage({id: transActionId})
+ opts.onClick = () => showConfTxPage({id: transactionId})
opts.transactionStatus = t('Not Started')
} else if (transactionHash) {
opts.onClick = () => this.view(transactionHash, transactionNetworkId)
diff --git a/ui/app/components/typed-message-renderer.js b/ui/app/components/typed-message-renderer.js
deleted file mode 100644
index d170d63b7..000000000
--- a/ui/app/components/typed-message-renderer.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-const extend = require('xtend')
-
-module.exports = TypedMessageRenderer
-
-inherits(TypedMessageRenderer, Component)
-function TypedMessageRenderer () {
- Component.call(this)
-}
-
-TypedMessageRenderer.prototype.render = function () {
- const props = this.props
- const { value, style } = props
- const text = renderTypedData(value)
-
- const defaultStyle = extend({
- width: '315px',
- maxHeight: '210px',
- resize: 'none',
- border: 'none',
- background: 'white',
- padding: '3px',
- overflow: 'scroll',
- }, style)
-
- return (
- h('div.font-small', {
- style: defaultStyle,
- }, text)
- )
-}
-
-function renderTypedData (values) {
- return values.map(function (value) {
- return h('div', {}, [
- h('strong', {style: {display: 'block', fontWeight: 'bold'}}, String(value.name) + ':'),
- h('div', {}, value.value),
- ])
- })
-}
diff --git a/ui/app/components/wallet-content-display.js b/ui/app/components/wallet-content-display.js
deleted file mode 100644
index bfa061be4..000000000
--- a/ui/app/components/wallet-content-display.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const inherits = require('util').inherits
-
-module.exports = WalletContentDisplay
-
-inherits(WalletContentDisplay, Component)
-function WalletContentDisplay () {
- Component.call(this)
-}
-
-WalletContentDisplay.prototype.render = function () {
- const { title, amount, fiatValue, active, style } = this.props
-
- // TODO: Separate component: wallet-content-account
- return h('div.flex-column', {
- style: {
- marginLeft: '1.3em',
- alignItems: 'flex-start',
- ...style,
- },
- }, [
-
- h('span', {
- style: {
- fontSize: '1.1em',
- },
- }, title),
-
- h('span', {
- style: {
- fontSize: '1.8em',
- margin: '0.4em 0em',
- },
- }, amount),
-
- h('span', {
- style: {
- fontSize: '1.3em',
- },
- }, fiatValue),
-
- active && h('div', {
- style: {
- position: 'absolute',
- marginLeft: '-1.3em',
- height: '6em',
- width: '0.3em',
- background: '#D8D8D8', // $alto
- },
- }, [
- ]),
- ])
-
-}
-
diff --git a/ui/app/conversion-util.js b/ui/app/conversion-util.js
index ee42ebea1..d484ed16d 100644
--- a/ui/app/conversion-util.js
+++ b/ui/app/conversion-util.js
@@ -187,6 +187,18 @@ const conversionGreaterThan = (
return firstValue.gt(secondValue)
}
+const conversionMax = (
+ { ...firstProps },
+ { ...secondProps },
+) => {
+ const firstIsGreater = conversionGreaterThan(
+ { ...firstProps },
+ { ...secondProps }
+ )
+
+ return firstIsGreater ? firstProps.value : secondProps.value
+}
+
const conversionGTE = (
{ ...firstProps },
{ ...secondProps },
@@ -216,6 +228,7 @@ module.exports = {
conversionGreaterThan,
conversionGTE,
conversionLTE,
+ conversionMax,
toNegative,
subtractCurrencies,
}
diff --git a/ui/app/css/itcss/components/account-menu.scss b/ui/app/css/itcss/components/account-menu.scss
index 4752741aa..c4037d862 100644
--- a/ui/app/css/itcss/components/account-menu.scss
+++ b/ui/app/css/itcss/components/account-menu.scss
@@ -87,7 +87,6 @@
flex: 1 0 auto;
display: flex;
flex-flow: column nowrap;
- padding-top: 4px;
}
&__check-mark {
@@ -115,13 +114,11 @@
color: $white;
font-size: 18px;
font-weight: 300;
- line-height: 16px;
}
&__balance {
color: $dusty-gray;
font-size: 14px;
- line-height: 19px;
}
&__action {
diff --git a/ui/app/css/itcss/components/network.scss b/ui/app/css/itcss/components/network.scss
index c32d1de5e..374cb71b6 100644
--- a/ui/app/css/itcss/components/network.scss
+++ b/ui/app/css/itcss/components/network.scss
@@ -10,8 +10,9 @@
.network-component.pointer {
border: 2px solid $silver;
border-radius: 82px;
- padding: 3px;
+ padding: 7px 3px;
flex: 0 0 auto;
+ display: flex;
&.ethereum-network .menu-icon-circle div {
background-color: rgba(3, 135, 137, .7) !important;
diff --git a/ui/app/css/itcss/components/newui-sections.scss b/ui/app/css/itcss/components/newui-sections.scss
index 5cdda5e6c..777a82318 100644
--- a/ui/app/css/itcss/components/newui-sections.scss
+++ b/ui/app/css/itcss/components/newui-sections.scss
@@ -265,7 +265,6 @@ $wallet-view-bg: $alabaster;
.account-name {
font-size: 24px;
font-weight: 300;
- line-height: 20px;
color: $black;
margin-top: 8px;
margin-bottom: .9rem;
diff --git a/ui/app/css/itcss/components/send.scss b/ui/app/css/itcss/components/send.scss
index bb17e53cd..bdea1b008 100644
--- a/ui/app/css/itcss/components/send.scss
+++ b/ui/app/css/itcss/components/send.scss
@@ -660,6 +660,13 @@
&__gas-fee-display {
width: 100%;
+ position: relative;
+
+ .currency-display--message {
+ padding: 8px 38px 8px 10px;
+ display: flex;
+ align-items: center;
+ }
}
&__sliders-icon-container {
@@ -885,3 +892,23 @@
}
}
}
+
+.sliders-icon-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 24px;
+ width: 24px;
+ border: 1px solid $curious-blue;
+ border-radius: 4px;
+ background-color: $white;
+ position: absolute;
+ right: 15px;
+ top: 14px;
+ cursor: pointer;
+ font-size: 1em;
+}
+
+.sliders-icon {
+ color: $curious-blue;
+} \ No newline at end of file
diff --git a/ui/app/css/itcss/components/transaction-list.scss b/ui/app/css/itcss/components/transaction-list.scss
index c3df493df..c13f24953 100644
--- a/ui/app/css/itcss/components/transaction-list.scss
+++ b/ui/app/css/itcss/components/transaction-list.scss
@@ -97,7 +97,7 @@
.tx-list-content-wrapper {
align-items: stretch;
- margin-bottom: 4px;
+ margin: 4px 0;
flex: 1 0 auto;
width: 100%;
display: flex;
@@ -126,6 +126,53 @@
}
}
+.tx-list-item-retry-container {
+ background: #d1edff;
+ width: 100%;
+ border-radius: 4px;
+ font-size: 0.8em;
+ display: flex;
+ justify-content: center;
+ margin-left: 44px;
+ width: calc(100% - 44px);
+
+ @media screen and (min-width: 576px) and (max-width: 679px) {
+ flex-flow: column;
+ align-items: center;
+ }
+
+ @media screen and (min-width: 380px) and (max-width: 575px) {
+ flex-flow: row;
+ }
+
+ @media screen and (max-width: 379px) {
+ flex-flow: column;
+ align-items: center;
+ }
+}
+
+.tx-list-item-retry-copy {
+ font-family: Roboto;
+}
+
+.tx-list-item-retry-link {
+ text-decoration: underline;
+ margin-left: 6px;
+ cursor: pointer;
+
+ @media screen and (min-width: 576px) and (max-width: 679px) {
+ margin-left: 0px;
+ }
+
+ @media screen and (min-width: 380px) and (max-width: 575px) {
+ margin-left: 6px;
+ }
+
+ @media screen and (max-width: 379px) {
+ margin-left: 0px;
+ }
+}
+
.tx-list-date {
color: $dusty-gray;
font-size: 12px;
@@ -136,6 +183,7 @@
align-self: center;
flex: 0 0 auto;
margin-right: 16px;
+ display: flex;
}
.tx-list-account-and-status-wrapper {
@@ -189,9 +237,14 @@
.tx-list-status--failed {
color: $monzo;
}
+
+ .tx-list-status--dropped {
+ opacity: 0.5;
+ }
}
.tx-list-item {
+ height: 80px;
border-top: 1px solid rgb(231, 231, 231);
flex: 0 0 auto;
display: flex;
diff --git a/ui/app/css/itcss/generic/index.scss b/ui/app/css/itcss/generic/index.scss
index 1fbd9896f..1e226b93e 100644
--- a/ui/app/css/itcss/generic/index.scss
+++ b/ui/app/css/itcss/generic/index.scss
@@ -13,7 +13,6 @@ body {
font-family: Roboto, Arial;
color: #4d4d4d;
font-weight: 300;
- line-height: 1.4em;
background: #f7f7f7;
width: 100%;
height: 100%;
@@ -103,6 +102,7 @@ input.large-input {
&::after {
content: '\00D7';
font-size: 40px;
+ line-height: 20px;
}
}
diff --git a/ui/app/css/itcss/generic/reset.scss b/ui/app/css/itcss/generic/reset.scss
index e054d533e..a417a0453 100644
--- a/ui/app/css/itcss/generic/reset.scss
+++ b/ui/app/css/itcss/generic/reset.scss
@@ -112,10 +112,6 @@ section {
display: block;
}
-body {
- line-height: 1;
-}
-
ol,
ul {
list-style: none;
diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss
index d96c1ae43..640fd95b8 100644
--- a/ui/app/css/itcss/settings/variables.scss
+++ b/ui/app/css/itcss/settings/variables.scss
@@ -46,6 +46,7 @@ $manatee: #93949d;
$spindle: #c7ddec;
$mid-gray: #5b5d67;
$cape-cod: #38393a;
+$onahau: #d1edff;
$java: #29b6af;
$wild-strawberry: #ff4a8d;
$cornflower-blue: #7057ff;
diff --git a/ui/app/info.js b/ui/app/info.js
deleted file mode 100644
index 49ff9f24a..000000000
--- a/ui/app/info.js
+++ /dev/null
@@ -1,154 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-const actions = require('./actions')
-
-module.exports = connect(mapStateToProps)(InfoScreen)
-
-function mapStateToProps (state) {
- return {}
-}
-
-inherits(InfoScreen, Component)
-function InfoScreen () {
- Component.call(this)
-}
-
-InfoScreen.prototype.render = function () {
- const state = this.props
- const version = global.platform.getVersion()
-
- return (
- h('.flex-column.flex-grow', {
- style: {
- maxWidth: '400px',
- },
- }, [
-
- // subtitle and nav
- h('.section-title.flex-row.flex-center', [
- h('i.fa.fa-arrow-left.fa-lg.cursor-pointer', {
- onClick: (event) => {
- state.dispatch(actions.goHome())
- },
- }),
- h('h2.page-subtitle', 'Info'),
- ]),
-
- // main view
- h('.flex-column.flex-justify-center.flex-grow.select-none', [
- h('.flex-space-around', {
- style: {
- padding: '20px',
- },
- }, [
- // current version number
-
- h('.info.info-gray', [
- h('div', 'Metamask'),
- h('div', {
- style: {
- marginBottom: '10px',
- },
- }, `Version: ${version}`),
- ]),
-
- h('div', {
- style: {
- marginBottom: '5px',
- }},
- [
- h('div', [
- h('a', {
- href: 'https://metamask.io/privacy.html',
- target: '_blank',
- onClick (event) { this.navigateTo(event.target.href) },
- }, [
- h('div.info', 'Privacy Policy'),
- ]),
- ]),
- h('div', [
- h('a', {
- href: 'https://metamask.io/terms.html',
- target: '_blank',
- onClick (event) { this.navigateTo(event.target.href) },
- }, [
- h('div.info', 'Terms of Use'),
- ]),
- ]),
- h('div', [
- h('a', {
- href: 'https://metamask.io/attributions.html',
- target: '_blank',
- onClick (event) { this.navigateTo(event.target.href) },
- }, [
- h('div.info', 'Attributions'),
- ]),
- ]),
- ]
- ),
-
- h('hr', {
- style: {
- margin: '10px 0 ',
- width: '7em',
- },
- }),
-
- h('div', {
- style: {
- paddingLeft: '30px',
- }},
- [
- h('div.fa.fa-support', [
- h('a.info', {
- href: 'https://metamask.helpscoutdocs.com/',
- target: '_blank',
- }, 'Visit our Knowledge Base'),
- ]),
-
- h('div', [
- h('a', {
- href: 'https://metamask.io/',
- target: '_blank',
- }, [
- h('img.icon-size', {
- src: 'images/icon-128.png',
- style: {
- // IE6-9
- filter: 'grayscale(100%)',
- // Microsoft Edge and Firefox 35+
- WebkitFilter: 'grayscale(100%)',
- },
- }),
- h('div.info', 'Visit our web site'),
- ]),
- ]),
-
- h('div', [
- h('.fa.fa-twitter', [
- h('a.info', {
- href: 'https://twitter.com/metamask_io',
- target: '_blank',
- }, 'Follow us on Twitter'),
- ]),
- ]),
-
- h('div.fa.fa-envelope', [
- h('a.info', {
- target: '_blank',
- href: 'mailto:support@metamask.io?subject=MetaMask Support',
- }, 'Email us!'),
- ]),
- ]),
- ]),
- ]),
- ])
- )
-}
-
-InfoScreen.prototype.navigateTo = function (url) {
- global.platform.openWindow({ url })
-}
-
diff --git a/ui/app/reducers/metamask.js b/ui/app/reducers/metamask.js
index 4ca7d221e..e6e02d057 100644
--- a/ui/app/reducers/metamask.js
+++ b/ui/app/reducers/metamask.js
@@ -38,6 +38,7 @@ function reduceMetamask (state, action) {
errors: {},
maxModeOn: false,
editingTransactionId: null,
+ forceGasMin: null,
},
coinOptions: {},
useBlockie: false,
@@ -297,6 +298,7 @@ function reduceMetamask (state, action) {
memo: '',
errors: {},
editingTransactionId: null,
+ forceGasMin: null,
},
})
diff --git a/ui/app/selectors.js b/ui/app/selectors.js
index 5d2635775..d37c26f7e 100644
--- a/ui/app/selectors.js
+++ b/ui/app/selectors.js
@@ -18,6 +18,7 @@ const selectors = {
getCurrentAccountWithSendEtherInfo,
getGasPrice,
getGasLimit,
+ getForceGasMin,
getAddressBook,
getSendFrom,
getCurrentCurrency,
@@ -130,6 +131,10 @@ function getGasLimit (state) {
return state.metamask.send.gasLimit
}
+function getForceGasMin (state) {
+ return state.metamask.send.forceGasMin
+}
+
function getSendFrom (state) {
return state.metamask.send.from
}
diff --git a/ui/app/send-v2.js b/ui/app/send-v2.js
index fc1df1f51..6133eadab 100644
--- a/ui/app/send-v2.js
+++ b/ui/app/send-v2.js
@@ -42,6 +42,7 @@ function SendTransactionScreen () {
to: null,
amount: null,
},
+ gasLoadingError: false,
}
this.handleToChange = this.handleToChange.bind(this)
@@ -128,6 +129,10 @@ SendTransactionScreen.prototype.updateGas = function () {
.then(([gasPrice, gas]) => {
const newGasTotal = this.getGasTotal(gas, gasPrice)
updateGasTotal(newGasTotal)
+ this.setState({ gasLoadingError: false })
+ })
+ .catch(err => {
+ this.setState({ gasLoadingError: true })
})
} else {
const newGasTotal = this.getGasTotal(gasLimit, gasPrice)
@@ -436,6 +441,7 @@ SendTransactionScreen.prototype.renderGasRow = function () {
showCustomizeGasModal,
gasTotal,
} = this.props
+ const { gasLoadingError } = this.state
return h('div.send-v2__form-row', [
@@ -448,6 +454,7 @@ SendTransactionScreen.prototype.renderGasRow = function () {
conversionRate,
convertedCurrency,
onClick: showCustomizeGasModal,
+ gasLoadingError,
}),
]),
@@ -571,9 +578,11 @@ SendTransactionScreen.prototype.getEditedTx = function () {
data,
})
} else {
+ const data = unapprovedTxs[editingTransactionId].txParams.data
Object.assign(editingTx.txParams, {
value: ethUtil.addHexPrefix(amount),
to: ethUtil.addHexPrefix(to),
+ data,
})
}
diff --git a/ui/app/send.js b/ui/app/send.js
deleted file mode 100644
index 517b7690d..000000000
--- a/ui/app/send.js
+++ /dev/null
@@ -1,547 +0,0 @@
-// const { inherits } = require('util')
-// const PersistentForm = require('../lib/persistent-form')
-// const h = require('react-hyperscript')
-// const connect = require('react-redux').connect
-// const Identicon = require('./components/identicon')
-// const EnsInput = require('./components/ens-input')
-// const GasTooltip = require('./components/send/gas-tooltip')
-// const CurrencyToggle = require('./components/send/currency-toggle')
-// const GasFeeDisplay = require('./components/send/gas-fee-display')
-// const { getSelectedIdentity } = require('./selectors')
-
-// const {
-// showAccountsPage,
-// backToAccountDetail,
-// displayWarning,
-// hideWarning,
-// addToAddressBook,
-// signTx,
-// estimateGas,
-// getGasPrice,
-// } = require('./actions')
-// const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util')
-// const { isHex, numericBalance, isValidAddress, allNull } = require('./util')
-// const { conversionUtil, conversionGreaterThan } = require('./conversion-util')
-
-// module.exports = connect(mapStateToProps)(SendTransactionScreen)
-
-// function mapStateToProps (state) {
-// const {
-// selectedAddress: address,
-// accounts,
-// identities,
-// network,
-// addressBook,
-// conversionRate,
-// currentBlockGasLimit: blockGasLimit,
-// } = state.metamask
-// const { warning } = state.appState
-// const selectedIdentity = getSelectedIdentity(state)
-// const account = accounts[address]
-
-// return {
-// address,
-// accounts,
-// identities,
-// network,
-// addressBook,
-// conversionRate,
-// blockGasLimit,
-// warning,
-// selectedIdentity,
-// error: warning && warning.split('.')[0],
-// account,
-// identity: identities[address],
-// balance: account ? account.balance : null,
-// }
-// }
-
-// inherits(SendTransactionScreen, PersistentForm)
-// function SendTransactionScreen () {
-// PersistentForm.call(this)
-
-// // [WIP] These are the bare minimum of tx props needed to sign a transaction
-// // We will need a few more for contract-related interactions
-// this.state = {
-// newTx: {
-// from: '',
-// to: '',
-// amountToSend: '0x0',
-// gasPrice: null,
-// gas: null,
-// amount: '0x0',
-// txData: null,
-// memo: '',
-// },
-// activeCurrency: 'USD',
-// tooltipIsOpen: false,
-// errors: {},
-// isValid: false,
-// }
-
-// this.back = this.back.bind(this)
-// this.closeTooltip = this.closeTooltip.bind(this)
-// this.onSubmit = this.onSubmit.bind(this)
-// this.setActiveCurrency = this.setActiveCurrency.bind(this)
-// this.toggleTooltip = this.toggleTooltip.bind(this)
-// this.validate = this.validate.bind(this)
-// this.getAmountToSend = this.getAmountToSend.bind(this)
-// this.setErrorsFor = this.setErrorsFor.bind(this)
-// this.clearErrorsFor = this.clearErrorsFor.bind(this)
-
-// this.renderFromInput = this.renderFromInput.bind(this)
-// this.renderToInput = this.renderToInput.bind(this)
-// this.renderAmountInput = this.renderAmountInput.bind(this)
-// this.renderGasInput = this.renderGasInput.bind(this)
-// this.renderMemoInput = this.renderMemoInput.bind(this)
-// this.renderErrorMessage = this.renderErrorMessage.bind(this)
-// }
-
-// SendTransactionScreen.prototype.componentWillMount = function () {
-// const { newTx } = this.state
-// const { address } = this.props
-
-// Promise.all([
-// this.props.dispatch(getGasPrice()),
-// this.props.dispatch(estimateGas({
-// from: address,
-// gas: '746a528800',
-// })),
-// ])
-// .then(([blockGasPrice, estimatedGas]) => {
-// console.log({ blockGasPrice, estimatedGas})
-// this.setState({
-// newTx: {
-// ...newTx,
-// gasPrice: blockGasPrice,
-// gas: estimatedGas,
-// },
-// })
-// })
-// }
-
-// SendTransactionScreen.prototype.renderErrorMessage = function(errorType, warning) {
-// const { errors } = this.state
-// const errorMessage = errors[errorType];
-
-// return errorMessage || warning
-// ? h('div.send-screen-input-wrapper__error-message', [ errorMessage || warning ])
-// : null
-// }
-
-// SendTransactionScreen.prototype.renderFromInput = function (from, identities) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div', 'From:'),
-
-// h('input.large-input.send-screen-input', {
-// list: 'accounts',
-// placeholder: 'Account',
-// value: from,
-// onChange: (event) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// from: event.target.value,
-// },
-// })
-// },
-// onBlur: () => this.setErrorsFor('from'),
-// onFocus: event => {
-// this.clearErrorsFor('from')
-// this.state.newTx.from && event.target.select()
-// },
-// }),
-
-// h('datalist#accounts', [
-// Object.entries(identities).map(([key, { address, name }]) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// ]),
-
-// this.renderErrorMessage('from'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderToInput = function (to, identities, addressBook) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div', 'To:'),
-
-// h('input.large-input.send-screen-input', {
-// name: 'address',
-// list: 'addresses',
-// placeholder: 'Address',
-// value: to,
-// onChange: (event) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// to: event.target.value,
-// },
-// })
-// },
-// onBlur: () => {
-// this.setErrorsFor('to')
-// },
-// onFocus: event => {
-// this.clearErrorsFor('to')
-// this.state.newTx.to && event.target.select()
-// },
-// }),
-
-// h('datalist#addresses', [
-// // Corresponds to the addresses owned.
-// ...Object.entries(identities).map(([key, { address, name }]) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// // Corresponds to previously sent-to addresses.
-// ...addressBook.map(({ address, name }) => {
-// return h('option', {
-// value: address,
-// label: name,
-// key: address,
-// })
-// }),
-// ]),
-
-// this.renderErrorMessage('to'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) {
-// return h('div.send-screen-input-wrapper', [
-
-// h('div.send-screen-amount-labels', [
-// h('span', 'Amount'),
-// h(CurrencyToggle, {
-// activeCurrency,
-// onClick: (newCurrency) => this.setActiveCurrency(newCurrency),
-// }), // holding on icon from design
-// ]),
-
-// h('input.large-input.send-screen-input', {
-// placeholder: `0 ${activeCurrency}`,
-// type: 'number',
-// onChange: (event) => {
-// const amountToSend = event.target.value
-// ? this.getAmountToSend(event.target.value)
-// : '0x0'
-
-// this.setState({
-// newTx: Object.assign(
-// this.state.newTx,
-// {
-// amount: event.target.value,
-// amountToSend: amountToSend,
-// }
-// ),
-// })
-// },
-// onBlur: () => {
-// this.setErrorsFor('amount')
-// },
-// onFocus: () => this.clearErrorsFor('amount'),
-// }),
-
-// this.renderErrorMessage('amount'),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderGasInput = function (gasPrice, gas, activeCurrency, conversionRate, blockGasLimit) {
-// return h('div.send-screen-input-wrapper', [
-// this.state.tooltipIsOpen && h(GasTooltip, {
-// className: 'send-tooltip',
-// gasPrice,
-// gasLimit: gas,
-// onClose: this.closeTooltip,
-// onFeeChange: ({gasLimit, gasPrice}) => {
-// this.setState({
-// newTx: {
-// ...this.state.newTx,
-// gas: gasLimit,
-// gasPrice,
-// },
-// })
-// },
-// }),
-
-// h('div.send-screen-gas-labels', [
-// h('span', [
-// h('i.fa.fa-bolt'),
-// 'Gas fee:',
-// ]),
-// h('span', 'What\'s this?'),
-// ]),
-
-// // TODO: handle loading time when switching to USD
-// h('div.large-input.send-screen-gas-input', {}, [
-// h(GasFeeDisplay, {
-// activeCurrency,
-// conversionRate,
-// gas,
-// gasPrice,
-// blockGasLimit,
-// }),
-// h('div.send-screen-gas-input-customize', {
-// onClick: this.toggleTooltip,
-// }, [
-// 'Customize',
-// ]),
-// ]),
-
-// ])
-// }
-
-// SendTransactionScreen.prototype.renderMemoInput = function () {
-// return h('div.send-screen-input-wrapper', [
-// h('div', 'Transaction memo (optional)'),
-// h('input.large-input.send-screen-input', {
-// onChange: () => {
-// this.setState({
-// newTx: Object.assign(
-// this.state.newTx,
-// {
-// memo: event.target.value,
-// }
-// ),
-// })
-// },
-// }),
-// ])
-// }
-
-// SendTransactionScreen.prototype.render = function () {
-// this.persistentFormParentId = 'send-tx-form'
-
-// const props = this.props
-// const {
-// warning,
-// identities,
-// addressBook,
-// conversionRate,
-// } = props
-
-// const {
-// blockGasLimit,
-// newTx,
-// activeCurrency,
-// isValid,
-// } = this.state
-// const { gas, gasPrice } = newTx
-
-// return (
-
-// h('div.send-screen-wrapper', [
-// // Main Send token Card
-// h('div.send-screen-card', [
-
-// h('img.send-eth-icon', { src: '../images/eth_logo.svg' }),
-
-// h('div.send-screen__title', 'Send'),
-
-// h('div.send-screen__subtitle', 'Send Ethereum to anyone with an Ethereum account'),
-
-// this.renderFromInput(this.state.newTx.from, identities),
-
-// this.renderToInput(this.state.newTx.to, identities, addressBook),
-
-// this.renderAmountInput(activeCurrency),
-
-// this.renderGasInput(
-// gasPrice || '0x0',
-// gas || '0x0',
-// activeCurrency,
-// conversionRate,
-// blockGasLimit
-// ),
-
-// this.renderMemoInput(),
-
-// this.renderErrorMessage(null, warning),
-
-// ]),
-
-// // Buttons underneath card
-// h('section.flex-column.flex-center', [
-// h('button.btn-secondary.send-screen__send-button', {
-// className: !isValid && 'send-screen__send-button__disabled',
-// onClick: (event) => isValid && this.onSubmit(event),
-// }, 'Next'),
-// h('button.btn-tertiary.send-screen__cancel-button', {
-// onClick: this.back,
-// }, 'Cancel'),
-// ]),
-// ])
-
-// )
-// }
-
-// SendTransactionScreen.prototype.toggleTooltip = function () {
-// this.setState({ tooltipIsOpen: !this.state.tooltipIsOpen })
-// }
-
-// SendTransactionScreen.prototype.closeTooltip = function () {
-// this.setState({ tooltipIsOpen: false })
-// }
-
-// SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) {
-// this.setState({ activeCurrency: newCurrency })
-// }
-
-// SendTransactionScreen.prototype.back = function () {
-// var address = this.props.address
-// this.props.dispatch(backToAccountDetail(address))
-// }
-
-// SendTransactionScreen.prototype.validate = function (balance, amountToSend, { to, from }) {
-// const sufficientBalance = conversionGreaterThan(
-// {
-// value: balance,
-// fromNumericBase: 'hex',
-// },
-// {
-// value: amountToSend,
-// fromNumericBase: 'hex',
-// },
-// )
-
-// const amountLessThanZero = conversionGreaterThan(
-// {
-// value: 0,
-// fromNumericBase: 'dec',
-// },
-// {
-// value: amountToSend,
-// fromNumericBase: 'hex',
-// },
-// )
-
-// const errors = {}
-
-// if (!sufficientBalance) {
-// errors.amount = 'Insufficient funds.'
-// }
-
-// if (amountLessThanZero) {
-// errors.amount = 'Can not send negative amounts of ETH.'
-// }
-
-// if (!from) {
-// errors.from = 'Required'
-// }
-
-// if (from && !isValidAddress(from)) {
-// errors.from = 'Sender address is invalid.'
-// }
-
-// if (!to) {
-// errors.to = 'Required'
-// }
-
-// if (to && !isValidAddress(to)) {
-// errors.to = 'Recipient address is invalid.'
-// }
-
-// // if (txData && !isHex(stripHexPrefix(txData))) {
-// // message = 'Transaction data must be hex string.'
-// // return this.props.dispatch(displayWarning(message))
-// // }
-
-// return {
-// isValid: allNull(errors),
-// errors,
-// }
-// }
-
-// SendTransactionScreen.prototype.getAmountToSend = function (amount) {
-// const { activeCurrency } = this.state
-// const { conversionRate } = this.props
-
-// return conversionUtil(amount, {
-// fromNumericBase: 'dec',
-// toNumericBase: 'hex',
-// fromCurrency: activeCurrency,
-// toCurrency: 'ETH',
-// toDenomination: 'WEI',
-// conversionRate,
-// invertConversionRate: activeCurrency !== 'ETH',
-// })
-// }
-
-// SendTransactionScreen.prototype.setErrorsFor = function (field) {
-// const { balance } = this.props
-// const { newTx, errors: previousErrors } = this.state
-// const { amountToSend } = newTx
-
-// const {
-// isValid,
-// errors: newErrors
-// } = this.validate(balance, amountToSend, newTx)
-
-// const nextErrors = Object.assign({}, previousErrors, {
-// [field]: newErrors[field] || null
-// })
-
-// if (!isValid) {
-// this.setState({
-// errors: nextErrors,
-// isValid,
-// })
-// }
-// }
-
-// SendTransactionScreen.prototype.clearErrorsFor = function (field) {
-// const { errors: previousErrors } = this.state
-// const nextErrors = Object.assign({}, previousErrors, {
-// [field]: null
-// })
-
-// this.setState({
-// errors: nextErrors,
-// isValid: allNull(nextErrors),
-// })
-// }
-
-// SendTransactionScreen.prototype.onSubmit = function (event) {
-// event.preventDefault()
-// const { warning, balance } = this.props
-// const state = this.state || {}
-
-// const recipient = state.newTx.to
-// const sender = state.newTx.from
-// const nickname = state.nickname || ' '
-
-// // TODO: convert this to hex when created and include it in send
-// const txData = state.newTx.memo
-
-// this.props.dispatch(hideWarning())
-
-// this.props.dispatch(addToAddressBook(recipient, nickname))
-
-// var txParams = {
-// from: this.state.newTx.from,
-// to: this.state.newTx.to,
-
-// value: this.state.newTx.amountToSend,
-
-// gas: this.state.newTx.gas,
-// gasPrice: this.state.newTx.gasPrice,
-// }
-
-// if (recipient) txParams.to = addHexPrefix(recipient)
-// if (txData) txParams.data = txData
-
-// this.props.dispatch(signTx(txParams))
-// }
diff --git a/ui/app/template.js b/ui/app/template.js
deleted file mode 100644
index d15b30fd2..000000000
--- a/ui/app/template.js
+++ /dev/null
@@ -1,30 +0,0 @@
-const inherits = require('util').inherits
-const Component = require('react').Component
-const h = require('react-hyperscript')
-const connect = require('react-redux').connect
-
-module.exports = connect(mapStateToProps)(COMPONENTNAME)
-
-function mapStateToProps (state) {
- return {}
-}
-
-inherits(COMPONENTNAME, Component)
-function COMPONENTNAME () {
- Component.call(this)
-}
-
-COMPONENTNAME.prototype.render = function () {
- const props = this.props
-
- return (
- h('div', {
- style: {
- background: 'blue',
- },
- }, [
- `Hello, ${props.sender}`,
- ])
- )
-}
-
diff --git a/ui/app/token-tracker.js b/ui/app/token-tracker.js
deleted file mode 100644
index e69de29bb..000000000
--- a/ui/app/token-tracker.js
+++ /dev/null
diff --git a/ui/lib/contract-namer.js b/ui/lib/contract-namer.js
deleted file mode 100644
index f05e770cc..000000000
--- a/ui/lib/contract-namer.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* CONTRACT NAMER
- *
- * Takes an address,
- * Returns a nicname if we have one stored,
- * otherwise returns null.
- */
-
-const contractMap = require('eth-contract-metadata')
-const ethUtil = require('ethereumjs-util')
-
-module.exports = function (addr, identities = {}) {
- const checksummed = ethUtil.toChecksumAddress(addr)
- if (contractMap[checksummed] && contractMap[checksummed].name) {
- return contractMap[checksummed].name
- }
-
- const address = addr.toLowerCase()
- const ids = hashFromIdentities(identities)
- return addrFromHash(address, ids)
-}
-
-function hashFromIdentities (identities) {
- const result = {}
- for (const key in identities) {
- result[key] = identities[key].name
- }
- return result
-}
-
-function addrFromHash (addr, hash) {
- const address = addr.toLowerCase()
- return hash[address] || null
-}