Merge branch 'master' of github.com:MetaMask/metamask-extension into i18n-translator-redux

feature/default_network_editable
kumavis 7 years ago
commit 29cc2f8ab9
  1. 1
      CHANGELOG.md
  2. 77
      app/_locales/en/messages.json
  3. 838
      app/_locales/es/messages.json
  4. 819
      app/_locales/it/messages.json
  5. 609
      app/_locales/ph/messages.json
  6. 819
      app/_locales/pt/messages.json
  7. 53
      app/scripts/controllers/transactions.js
  8. 42
      app/scripts/lib/tx-state-manager.js
  9. 2
      development/states/confirm-new-ui.json
  10. 2
      development/states/send-edit.json
  11. 96
      development/verify-locale-strings.js
  12. 10
      docs/translating-guide.md
  13. 6
      gulpfile.js
  14. 18
      old-ui/app/components/transaction-list-item.js
  15. 2
      old-ui/app/components/transaction-list.js
  16. 4
      old-ui/app/css/index.css
  17. 6
      package.json
  18. 8
      test/integration/lib/send-new-ui.js
  19. 45
      test/unit/tx-controller-test.js
  20. 6
      ui/app/accounts/import/index.js
  21. 2
      ui/app/accounts/import/json.js
  22. 4
      ui/app/actions.js
  23. 43
      ui/app/add-token.js
  24. 33
      ui/app/app.js
  25. 31
      ui/app/components/customize-gas-modal/index.js
  26. 89
      ui/app/components/pending-tx/confirm-send-ether.js
  27. 90
      ui/app/components/pending-tx/confirm-send-token.js
  28. 11
      ui/app/components/send/gas-fee-display-v2.js
  29. 1
      ui/app/components/send/send-v2-container.js
  30. 97
      ui/app/components/tx-list-item.js
  31. 15
      ui/app/components/tx-list.js
  32. 18
      ui/app/conf-tx.js
  33. 13
      ui/app/conversion-util.js
  34. 3
      ui/app/css/itcss/components/account-menu.scss
  35. 16
      ui/app/css/itcss/components/confirm.scss
  36. 3
      ui/app/css/itcss/components/network.scss
  37. 1
      ui/app/css/itcss/components/newui-sections.scss
  38. 27
      ui/app/css/itcss/components/send.scss
  39. 55
      ui/app/css/itcss/components/transaction-list.scss
  40. 2
      ui/app/css/itcss/generic/index.scss
  41. 4
      ui/app/css/itcss/generic/reset.scss
  42. 1
      ui/app/css/itcss/settings/variables.scss
  43. 7
      ui/app/keychains/hd/recover-seed/confirmation.js
  44. 35
      ui/app/keychains/hd/restore-vault.js
  45. 2
      ui/app/reducers/metamask.js
  46. 5
      ui/app/selectors.js
  47. 39
      ui/app/send-v2.js
  48. 76
      ui/app/settings.js
  49. 6
      ui/app/unlock.js

@ -2,6 +2,7 @@
## Current Master ## Current Master
- MetaMask will no longer allow nonces to be specified by the dapp
- Add ability for internationalization. - Add ability for internationalization.
- Will now throw an error if the `to` field in txParams is not valid. - Will now throw an error if the `to` field in txParams is not valid.
- Will strip null values from the `to` field. - Will strip null values from the `to` field.

@ -37,6 +37,9 @@
"message": "MetaMask", "message": "MetaMask",
"description": "The name of the application" "description": "The name of the application"
}, },
"approved": {
"message": "Approved"
},
"attemptingConnect": { "attemptingConnect": {
"message": "Attempting to connect to blockchain." "message": "Attempting to connect to blockchain."
}, },
@ -83,6 +86,9 @@
"buyCoinbaseExplainer": { "buyCoinbaseExplainer": {
"message": "Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin." "message": "Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin."
}, },
"ok": {
"message": "Ok"
},
"cancel": { "cancel": {
"message": "Cancel" "message": "Cancel"
}, },
@ -95,6 +101,9 @@
"confirm": { "confirm": {
"message": "Confirm" "message": "Confirm"
}, },
"confirmed": {
"message": "Confirmed"
},
"confirmContract": { "confirmContract": {
"message": "Confirm Contract" "message": "Confirm Contract"
}, },
@ -226,6 +235,9 @@
"downloadStatelogs": { "downloadStatelogs": {
"message": "Download State Logs" "message": "Download State Logs"
}, },
"dropped": {
"message": "Dropped"
},
"edit": { "edit": {
"message": "Edit" "message": "Edit"
}, },
@ -244,6 +256,12 @@
"enterPasswordConfirm": { "enterPasswordConfirm": {
"message": "Enter your password to confirm" "message": "Enter your password to confirm"
}, },
"passwordNotLongEnough": {
"message": "Password not long enough"
},
"passwordsDontMatch": {
"message": "Passwords Don't Match"
},
"etherscanView": { "etherscanView": {
"message": "View account on Etherscan" "message": "View account on Etherscan"
}, },
@ -403,6 +421,9 @@
"knowledgeDataBase": { "knowledgeDataBase": {
"message": "Visit our Knowledge Base" "message": "Visit our Knowledge Base"
}, },
"max": {
"message": "Max"
},
"lessThanMax": { "lessThanMax": {
"message": "must be less than or equal to $1.", "message": "must be less than or equal to $1.",
"description": "helper for inputting hex as decimal input" "description": "helper for inputting hex as decimal input"
@ -410,6 +431,9 @@
"likeToAddTokens": { "likeToAddTokens": {
"message": "Would you like to add these tokens?" "message": "Would you like to add these tokens?"
}, },
"links": {
"message": "Links"
},
"limit": { "limit": {
"message": "Limit" "message": "Limit"
}, },
@ -583,12 +607,18 @@
"restoreFromSeed": { "restoreFromSeed": {
"message": "Restore from seed phrase" "message": "Restore from seed phrase"
}, },
"restoreVault": {
"message": "Restore Vault"
},
"required": { "required": {
"message": "Required" "message": "Required"
}, },
"retryWithMoreGas": { "retryWithMoreGas": {
"message": "Retry with a higher gas price here" "message": "Retry with a higher gas price here"
}, },
"walletSeed": {
"message": "Wallet Seed"
},
"revealSeedWords": { "revealSeedWords": {
"message": "Reveal Seed Words" "message": "Reveal Seed Words"
}, },
@ -604,6 +634,24 @@
"ropsten": { "ropsten": {
"message": "Ropsten Test Network" "message": "Ropsten Test Network"
}, },
"currentRpc": {
"message": "Current RPC"
},
"connectingToMainnet": {
"message": "Connecting to Main Ethereum Network"
},
"connectingToRopsten": {
"message": "Connecting to Ropsten Test Network"
},
"connectingToKovan": {
"message": "Connecting to Kovan Test Network"
},
"connectingToRinkeby": {
"message": "Connecting to Rinkeby Test Network"
},
"connectingToUnknown": {
"message": "Connecting to Unknown Network"
},
"sampleAccountName": { "sampleAccountName": {
"message": "E.g. My new account", "message": "E.g. My new account",
"description": "Help user understand concept of adding a human-readable name to their account" "description": "Help user understand concept of adding a human-readable name to their account"
@ -611,6 +659,12 @@
"save": { "save": {
"message": "Save" "message": "Save"
}, },
"reprice:title": {
"message": "Reprice Transaction"
},
"reprice:subtitle": {
"message": "Increase your gas price to attempt to overwrite and speed up your transaction"
},
"saveAsFile": { "saveAsFile": {
"message": "Save as File", "message": "Save as File",
"description": "Account export process" "description": "Account export process"
@ -624,6 +678,9 @@
"secretPhrase": { "secretPhrase": {
"message": "Enter your secret twelve word phrase here to restore your vault." "message": "Enter your secret twelve word phrase here to restore your vault."
}, },
"newPassword8Chars": {
"message": "New Password (min 8 chars)"
},
"seedPhraseReq": { "seedPhraseReq": {
"message": "seed phrases are 12 words long" "message": "seed phrases are 12 words long"
}, },
@ -648,12 +705,18 @@
"sendTokens": { "sendTokens": {
"message": "Send Tokens" "message": "Send Tokens"
}, },
"onlySendToEtherAddress": {
"message": "Only send ETH to an Ethereum address."
},
"sendTokensAnywhere": { "sendTokensAnywhere": {
"message": "Send Tokens to anyone with an Ethereum account" "message": "Send Tokens to anyone with an Ethereum account"
}, },
"settings": { "settings": {
"message": "Settings" "message": "Settings"
}, },
"info": {
"message": "Info"
},
"shapeshiftBuy": { "shapeshiftBuy": {
"message": "Buy with Shapeshift" "message": "Buy with Shapeshift"
}, },
@ -666,6 +729,9 @@
"sign": { "sign": {
"message": "Sign" "message": "Sign"
}, },
"signed": {
"message": "Signed"
},
"signMessage": { "signMessage": {
"message": "Sign Message" "message": "Sign Message"
}, },
@ -690,9 +756,15 @@
"stateLogsDescription": { "stateLogsDescription": {
"message": "State logs contain your public account addresses and sent transactions." "message": "State logs contain your public account addresses and sent transactions."
}, },
"stateLogError": {
"message": "Error in retrieving state logs."
},
"submit": { "submit": {
"message": "Submit" "message": "Submit"
}, },
"submitted": {
"message": "Submitted"
},
"supportCenter": { "supportCenter": {
"message": "Visit our Support Center" "message": "Visit our Support Center"
}, },
@ -709,7 +781,7 @@
"message": "Test Faucet" "message": "Test Faucet"
}, },
"to": { "to": {
"message": "To" "message": "To: "
}, },
"toETHviaShapeShift": { "toETHviaShapeShift": {
"message": "$1 to ETH via ShapeShift", "message": "$1 to ETH via ShapeShift",
@ -764,6 +836,9 @@
"uiWelcomeMessage": { "uiWelcomeMessage": {
"message": "You are now using the new Metamask UI. Take a look around, try out new features like sending tokens, and let us know if you have any issues." "message": "You are now using the new Metamask UI. Take a look around, try out new features like sending tokens, and let us know if you have any issues."
}, },
"unapproved": {
"message": "Unapproved"
},
"unavailable": { "unavailable": {
"message": "Unavailable" "message": "Unavailable"
}, },

@ -1,10 +1,840 @@
{ {
"accept": {
"message": "Aceptar"
},
"account": {
"message": "Cuenta"
},
"accountDetails": {
"message": "Detalles de la cuenta"
},
"accountName": {
"message": "Nombre de la cuenta"
},
"address": {
"message": "Dirección"
},
"addToken": {
"message": "Agregar Token"
},
"amount": {
"message": "Cantidad"
},
"amountPlusGas": {
"message": "Cantidad + Gas"
},
"appDescription": {
"message": "Extensión del explorador usar Ethereum",
"description": "La descripción de la aplicación"
},
"appName": { "appName": {
"message": "MetaMask", "message": "MetaMask",
"description": "The name of the application" "description": "El nombre de la aplicación"
}, },
"appDescription": { "attemptingConnect": {
"message": "Administración de identidad en Ethereum", "message": "Intentando conectar a la Blockchain"
"description": "The description of the application" },
"available": {
"message": "Disponible"
},
"back": {
"message": "Atrás"
},
"balance": {
"message": "Saldo"
},
"balances": {
"message": "Tus saldos"
},
"balanceIsInsufficientGas": {
"message": "Saldo de gas insuficiente"
},
"beta": {
"message": "BETA"
},
"betweenMinAndMax": {
"message": "Debe ser mayor o igual a $1 y menor o igual a $2",
"description": "helper para ingresar hex como un ingreso decimal"
},
"borrowDharma": {
"message": "Pedir prestado con Dharma (Beta)"
},
"buy": {
"message": "Comprar"
},
"buyCoinbase": {
"message": "Comprar en Coinbase"
},
"buyCoinbaseExplainer": {
"message": "Coinbase es la manera más popular en el mundo para comprar y vender bitcoin, ethereum y litecoin"
},
"cancel": {
"message": "Cancelar"
},
"clickCopy": {
"message": "Click para copiar"
},
"confirm": {
"message": "Confirmar"
},
"continue": {
"message": "Continuar"
},
"confirmContract": {
"message": "Confirmar contrato"
},
"confirmPassword": {
"message": "Confirmar contraseña"
},
"enterPasswordConfirm": {
"message": "Ingresa tu contraseña para confirmar"
},
"confirmTransaction": {
"message": "Confirmar transacción "
},
"continueToCoinbase": {
"message": "Continuar a Coinbase"
},
"contractDeployment": {
"message": "Deployar contrato"
},
"currentConversion": {
"message": "Conversión Actual"
},
"conversionProgress": {
"message": "Conversión en progreso"
},
"copiedButton": {
"message": "Copiado"
},
"copiedClipboard": {
"message": "Copiado al portapapeles"
},
"copiedExclamation": {
"message": "Copiado!"
},
"copy": {
"message": "Copiar"
},
"copyToClipboard": {
"message": "Copiar al portapapeles"
},
"copyButton": {
"message": " Copiar "
},
"copyPrivateKey": {
"message": "Esta es tu llave privada (Click para copiar)"
},
"create": {
"message": "Crear"
},
"createAccount": {
"message": "Crear Cuenta"
},
"createDen": {
"message": "Crear"
},
"crypto": {
"message": "Crypto",
"description": "Tipo de Cambio (criptomonedas)"
},
"customGas": {
"message": "Personalizar Gas"
},
"customize": {
"message": "Personalizar"
},
"currentRPC": {
"message": "RPC actual"
},
"customRPC": {
"message": "RPC Personalizado"
},
"newRPC": {
"message": "Nueva URL del RPC"
},
"defaultNetwork": {
"message": "La red por defecto para las transacciones de Ether es MainNet (red principal)"
},
"denExplainer": {
"message": "Tu DEN es tu contraseña encriptada guardada dentro de MetaMask"
},
"deposit": {
"message": "Depositar"
},
"depositBTC": {
"message": "Deposita tus BTC a la dirección de abajo:"
},
"depositCoin": {
"message": "Deposita tu $1 a la dirección de abajo",
"description": "Informa al usuario que moneda ha elegido para depositar en shapeshift"
},
"depositEth": {
"message": "Depositar Eth"
},
"depositEther": {
"message": "Depositar Ether"
},
"depositFiat": {
"message": "Depositar con Fiat (divisa nacional)"
},
"depositFromAccount": {
"message": "Depositar con otra cuenta"
},
"depositShapeShift": {
"message": "Depositar con ShapeShift"
},
"depositShapeShiftExplainer": {
"message": "Si tu tienes otras criptomonedas, puedes intercambiar y depositar Ether directamente en tu billetera de MetaMask. No necesitas tener una cuenta."
},
"details": {
"message": "Detalles"
},
"directDeposit": {
"message": "Deposito directo"
},
"directDepositEther": {
"message": "Depositar Ether directamente"
},
"directDepositEtherExplainer": {
"message": "Si tu tienes algo de Ether, la forma rapida para tener Ether en tu nueva billetera es depositando directamente"
},
"done": {
"message": "Completo"
},
"edit": {
"message": "Editar"
},
"editAccountName": {
"message": "Editar el nombre de la cuenta"
},
"encryptNewDen": {
"message": "Encriptar tu nuevo DEN"
},
"enterPassword": {
"message": "Ingresa contraseña"
},
"passwordCorrect": {
"message": "Asegurate que tu contraseña es correcta"
},
"etherscanView": {
"message": "Ver la cuenta en Etherscan"
},
"exchangeRate": {
"message": "Tipo de cambio"
},
"exportPrivateKey": {
"message": "Exportar llave privada"
},
"exportPrivateKeyWarning": {
"message": "Exportar llaves privadas bajo TU PROPIO riesgo"
},
"failed": {
"message": "Fallo"
},
"fiat": {
"message": "FIAT",
"description": "Exchange type"
},
"fileImportFail": {
"message": "No funciona importar el archivo? Haz Click Aquí!",
"description": "Ayuda al usuario a importar su cuenta desde un archivo JSON"
},
"from": {
"message": "De:"
},
"fromShapeShift": {
"message": "De ShapeShift"
},
"gas": {
"message": "Gas",
"description": "Indicación pequeña del costo de gas"
},
"gasFee": {
"message": "Comisión de gas"
},
"gasLimit": {
"message": "Límite de gas"
},
"gasLimitCalculation": {
"message": "Calculamos el límite de gas sugerido en función de las tasas de éxito de la red"
},
"gasLimitRequired": {
"message": "Límite de Gas requerido"
},
"gasLimitTooLow": {
"message": "El límite de gas debe ser de al menos 21000"
},
"gasPrice": {
"message": "Precio del Gas (GWEI)"
},
"gasPriceCalculation": {
"message": "Calculamos los precios sugeridos del gas en función de las tasas de éxito de la red"
},
"gasPriceRequired": {
"message": "Precio del gas requerido"
},
"getEther": {
"message": "Conseguir Ether"
},
"getEtherFromFaucet": {
"message": "Obtenga Ether de un faucet (grifo) por $1",
"description": "Muestra el nombre de la red para el faucet (grifo) de Ether"
},
"greaterThanMin": {
"message": "Debe ser mayor o igual a $1",
"description": "helper para ingresar hex como entrada decimal"
},
"here": {
"message": "Aqui",
"description": "como en -haz click aquí- para más información"
},
"hide": {
"message": "Ocultar"
},
"hideToken": {
"message": "Ocultar Token"
},
"hideTokenPrompt": {
"message": "Ocultar Token?"
},
"howToDeposit": {
"message": "Cómo te gustaria depositar Ether?"
},
"import": {
"message": "Importar",
"description": "Botón para importar una cuenta desde un archivo seleccionado"
},
"importAccount": {
"message": "Importar Cuenta"
},
"importAnAccount": {
"message": "Importar una cuenta"
},
"importDen": {
"message": "Importar DEN existente"
},
"imported": {
"message": "Importado",
"description": "estado que muestra que una cuenta ha sido completamente cargada en el llavero"
},
"infoHelp": {
"message": "Informacion y Ayuda"
},
"invalidAddress": {
"message": "Dirección Inválida"
},
"invalidGasParams": {
"message": "Parametros de Gas Inválidos"
},
"invalidInput": {
"message": "Entrada inválida"
},
"invalidRequest": {
"message": "Peticion inválida"
},
"invalidAddressRecipient": {
"message": "Dirección del recipiente invalida"
},
"fromToSame": {
"message": "La dirección de origen y destino no pueden ser la misma"
},
"jsonFile": {
"message": "Archivo JSON",
"description": "formato para importar una cuenta"
},
"jsonFail": {
"message": "Algo malo pasó. Asegurate que tu JSON tiene el formato correcto"
},
"kovan": {
"message": "Red de pruebas Kovan"
},
"lessThanMax": {
"message": "Debe ser menor o igual a $1",
"description": "helper para ingresar hex como decimal"
},
"limit": {
"message": "Límite"
},
"loading": {
"message": "Cargando..."
},
"loadingTokens": {
"message": "Cargando Tokens..."
},
"localhost": {
"message": "Localhost 8545"
},
"login": {
"message": "Ingresar"
},
"logout": {
"message": "Cerrar sesion"
},
"loose": {
"message": "Loose"
},
"mainnet": {
"message": "Red principal de Ethereum (MainNet)"
},
"message": {
"message": "Mensaje"
},
"min": {
"message": "Minimo"
},
"myAccounts": {
"message": "Mis cuentas"
},
"needEtherInWallet": {
"message": "Para interactuar con una aplicación descentralizada usando MetaMask, vas a necesitar tener Ether en tu billetera"
},
"needImportFile": {
"message": "Debes seleccionar un archivo para importar",
"description": "El usuario está importando una cuenta y necesita agregar un archivo para continuar"
},
"needImportPassword": {
"message": "Debes ingresar una contraseña para el archivo seleccionado",
"description": "Contraseña y archivo necesarios para importar una cuenta"
},
"validFileImport": {
"message": "Debes selecionar un archivo valido para importar"
},
"networks": {
"message": "Redes"
},
"newAccount": {
"message": "Nueva cuenta"
},
"newAccountNumberName": {
"message": "Cuenta $1",
"description": "Nombre por defecto de la próxima cuenta a ser creada o pantalla de creación de cuenta"
},
"newContract": {
"message": "Nuevo contrato"
},
"newPassword": {
"message": "Nueva contraseña (mínimo [8] caracteres)"
},
"newRecipient": {
"message": "Nuevo destinatario"
},
"next": {
"message": "Siguiente"
},
"noAddressForName": {
"message": "No se ha establecido ninguna dirección para este nombre"
},
"noDeposits": {
"message": "No hay depósitos recibidos"
},
"noTransactionHistory": {
"message": "Sin historial de transacciones"
},
"transactions": {
"message": "Transacciones"
},
"noTransactions": {
"message": "Sin transacciones"
},
"notStarted": {
"message": "Sin iniciar"
},
"oldUI": {
"message": "Antigua UI"
},
"useOldUI": {
"message": "Usar UI antigua"
},
"oldUIMessage": {
"message": "Regresaste a la antigua UI. Puedes regresar a la nueva UI mediante la opcion en la barra desplegable del menu de arriba a la derecha."
},
"or": {
"message": "o",
"description": "opción entre crear o importar una cuenta"
},
"passwordMismatch": {
"message": "Contraseña no concide",
"description": "en el proceso de creación de contraseña, los dos campos de contraseña no coincidieron"
},
"passwordShort": {
"message": "Contraseña no es lo suficientemente larga",
"description": "in password creation process, the password is not long enough to be secure"
},
"pastePrivateKey": {
"message": "Pega tu llave privada aqui",
"description": "Para importar una cuenta desde una llave privada"
},
"pasteSeed": {
"message": "Pegue su frase semilla aquí!"
},
"generatingSeed": {
"message": "Generando semilla..."
},
"pleaseReviewTransaction": {
"message": "Por favor revisa tu transaccion"
},
"privateKey": {
"message": "Llave privada",
"description": "select this type of file to use to import an account"
},
"privateKeyWarning": {
"message": "Advertencia: Nunca revele esta clave. Cualquier persona con sus claves privadas puede robar cualquier activo retenido en su cuenta."
},
"privateNetwork": {
"message": "Red Privada"
},
"qrCode": {
"message": "Mostrar codigo QR"
},
"readdToken": {
"message": "Puede volver a agregar este token en el futuro yendo a 'Agregar token' en el menú de opciones de su cuenta.."
},
"readMore": {
"message": "Leer más aquí"
},
"receive": {
"message": "Recibir"
},
"recipientAddress": {
"message": "Dirección del receptor"
},
"refundAddress": {
"message": "Su dirección de reembolso"
},
"rejected": {
"message": "Rechazado"
},
"required": {
"message": "Requerido"
},
"retryWithMoreGas": {
"message": "Vuelva a intentar con un precio de Gas más alto aquí"
},
"revert": {
"message": "Revertir"
},
"rinkeby": {
"message": "Red privada Rinkeby"
},
"ropsten": {
"message": "Red privada Ropsten"
},
"sampleAccountName": {
"message": "Ej. Mi nueva cuenta",
"description": "Ayuda al usuario a entender el concepto de agregar un nombre, leíble por humanos, a su cuenta"
},
"save": {
"message": "Guardar"
},
"saveAsFile": {
"message": "Guardar como archivo",
"description": "Proceso de exportación de cuenta"
},
"selectService": {
"message": "Seleccionar servicio"
},
"send": {
"message": "Enviar"
},
"sendTokens": {
"message": "Enviar Tokens"
},
"sendETH": {
"message": "Enviar ETH"
},
"sendTokensAnywhere": {
"message": "Enviar Tokens a cualquiera con una cuenta de Ethereum"
},
"settings": {
"message": "Configuración"
},
"info": {
"message": "Información"
},
"shapeshiftBuy": {
"message": "Comprar con ShapeShift"
},
"showPrivateKeys": {
"message": "Mostrar llaves privadas"
},
"showQRCode": {
"message": "Mostrar codigo QR"
},
"sign": {
"message": "Firmar"
},
"signMessage": {
"message": "Firmar Mensaje"
},
"signNotice": {
"message": "Firmar este mensaje puede tener\n efectos secundarios peligrosos. Firma sólo\nmensajes desde sitios a los cuales tú estés dispuesto a confiar completamente tu cuenta.\nEste método peligroso va a ser \nremovido en una version futura."
},
"sigRequest": {
"message": "Solicitud de firma"
},
"sigRequested": {
"message": "Firma solicitada"
},
"status": {
"message": "Estado"
},
"submit": {
"message": "Enviar"
},
"takesTooLong": {
"message": "¿Está tomando demasiado?"
},
"testFaucet": {
"message": "Testear Faucet"
},
"to": {
"message": "Para:"
},
"toETHviaShapeShift": {
"message": "$1 a ETH via ShapeShift",
"description": "el sistema llenará el tipo de depósito al principio del mensaje"
},
"tokenBalance": {
"message": "Tu balance de Tokens es:"
},
"total": {
"message": "Total"
},
"transactionMemo": {
"message": "Memo de transaccion (opcional)"
},
"transactionNumber": {
"message": "Número de transacción"
},
"transfers": {
"message": "Transferencias"
},
"troubleTokenBalances": {
"message": "Tuvimos problemas para cargar sus balances de tokens. Puedes verlos ",
"description": "Followed by a link (here) to view token balances"
},
"typePassword": {
"message": "Escribe tu contraseña"
},
"uiWelcome": {
"message": "Bienvenido a la nueva UI (Beta)"
},
"uiWelcomeMessage": {
"message": "Estás usando la nueva UI de MetaMask. Echa un vistazo alrededor, prueba las nuevas características, tales como mandar tokens, y déjanos saber si tienes algún problema"
},
"unavailable": {
"message": "No disponible"
},
"unknown": {
"message": "Desconocido (a)"
},
"unknownNetwork": {
"message": "Red privada desconocida"
},
"unknownNetworkId": {
"message": "ID (identidad) de Red desconocida"
},
"currentNetwork": {
"message": "Red actual"
},
"usaOnly": {
"message": "Sólo USA (Estados Unidos)",
"description": "El uso de este exchange (casa de cambio) estaá limitado a las personas dentro de los Estados Unidos de America"
},
"usedByClients": {
"message": "Utilizado por una variedad de clientes diferentes"
},
"viewAccount": {
"message": "Mirar cuenta"
},
"warning": {
"message": "Advertencia"
},
"whatsThis": {
"message": "Qué es esto?"
},
"yourSigRequested": {
"message": "Tu firma ya fue solicidada"
},
"youSign": {
"message": "Usted está firmando"
},
"importedAccountMsg": {
"message": "Cuentas importadas no serán asociadas con tu cuenta original creada con tu MetaMask. Aprende más acerca de importar cuentas. "
},
"selectType": {
"message": "Seleccionar tipo"
},
"selectCurrency": {
"message": "Seleccionar moneda"
},
"password": {
"message": "Contraseña"
},
"select": {
"message": "Seleccionar"
},
"readMore2": {
"message": "Leer más."
},
"secretPhrase": {
"message": "Ingresa tu frase de 12 palabras para restaurar tu bóveda"
},
"spaceBetween": {
"message": "Sólo puede haber un espacio entre las palabras"
},
"loweCaseWords": {
"message": "las frases semilla sólo pueden tener minúsculas"
},
"seedPhraseReq": {
"message": "las frases semilla tienen doce (12) palabras de largo"
},
"addTokens": {
"message": "Agregar tokens"
},
"addCustomTokens": {
"message": "Agregar token personalizados"
},
"up": {
"message": "arriba"
},
"down": {
"message": "abajo"
},
"tokenWarning1": {
"message": "Mantenga un registro de los tokens que ha comprado con su cuenta de MetaMask. Si compraste tokens usando una cuenta diferente, esos tokens no aparecerán aquí."
},
"tokenSelection": {
"message": "Busca tokens o selecciónalo de nuestra lista de tokens populares"
},
"search": {
"message": "Buscar"
},
"privacyMsg": {
"message": "Política de privacidad"
},
"terms": {
"message": "Terminos de Uso"
},
"attributions": {
"message": "Atribuciones"
},
"supportCenter": {
"message": "Visita nuestro centro de atención"
},
"knowledgeDataBase": {
"message": "Visita nuestra base de conocimiento"
},
"visitWebSite": {
"message": "Visita nuestro sitio web"
},
"followTwitter": {
"message": "Síguenos en Twitter"
},
"emailUs": {
"message": "Envíanos un correo!"
},
"hereList": {
"message": "Aquí está una lista!!!"
},
"insufficientFounds": {
"message": "Fondos insuficientes"
},
"insufficientTokens": {
"message": "Tokens insuficientes"
},
"negativeETH": {
"message": "No se pueden mandar cantidades negativas de ETH"
},
"urlErrorMsg": {
"message": "URI necesita el prefijo HTTP/HTTPS apropiado"
},
"invalidRPC": {
"message": "Invalida URL del RPC"
},
"saveLogs": {
"message": "Logs de estado contienen tus direcciones publicas y transacciones enviadas"
},
"stateLogs": {
"message": "Logs de estado"
},
"downloadStatelogs": {
"message": "Descargar logs de estados"
},
"revealSeedWords": {
"message": "Revelar palabras de semilla"
},
"revealSeedWordsWarning": {
"message": "No recuperes tu semilla en un lugar publico! Esas palabras pueden ser usadas para robarte todas tus cuentas"
},
"resetAccount": {
"message": "Reiniciar cuenta"
},
"builtInCalifornia": {
"message": "Metamask fue diseñado y construido en California "
},
"classicInterface": {
"message": "Usar interfaz clasica "
},
"welcomeBeta": {
"message": "Bienvenido a Metamask Beta"
},
"metamaskDescription": {
"message": "Metamask es una identidad segura en Ethereum"
},
"holdEther": {
"message": "Te permite mantener tus ether y tokens, así como puente para aplicaciones descentralizadas"
},
"decimalsMustZerotoTen": {
"message": "Los decimales deben ser al menos 0 y no más de 36"
},
"symbolBetweenZeroTen": {
"message": "Símbolo debe ser entre 0 y 10 caracteres"
},
"personalAddressDetected": {
"message": "Dirección personal detectada. Ingresa la dirección del contrato del token"
},
"tokenAlreadyAdded": {
"message": "El token esta actualmente agregado"
},
"mustSelectOne": {
"message": "Debe seleccionar al menos un (1) token"
},
"tokenAddress": {
"message": "Dirección del token"
},
"tokenSymbol": {
"message": "Símbolo del token"
},
"decimalsPrecision": {
"message": "Decimales de precisión"
},
"likeToAddTokens": {
"message": "¿Te gustaría agregar estos tokens?"
},
"msgCompose1": {
"message": "Solo manda "
},
"msgCompose2": {
"message": " a una dirección de Ethereum"
},
"blockiesIdenticon": {
"message": "Usar Blockies Identicon (Iconos)"
},
"vaultCreated": {
"message": "Bóveda creada"
},
"twelveWords": {
"message": "Estas 12 palabras son la única forma de restablecer sus cuentas de MetaMask. \nGuardalas en un lugar seguro y secreto."
},
"copiedSafe": {
"message": "Ya lo guardé en un lugar seguro"
},
"saveSeedAsFile": {
"message": "Guardar la semilla como archivo"
},
"restoreFromSeed": {
"message": "Restaurar desde semilla"
} }
} }

@ -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"
}
}

@ -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"
}
}

@ -0,0 +1,819 @@
{
"accept": {
"message": "Aceitar"
},
"account": {
"message": "Conta"
},
"accountDetails": {
"message": "Detalhes da Conta"
},
"accountName": {
"message": "Nome da Conta"
},
"address": {
"message": "Endereço"
},
"addCustomToken": {
"message": "Adicionar token customizada"
},
"addToken": {
"message": "Adicionar Token"
},
"addTokens": {
"message": "Adicionar Tokens"
},
"amount": {
"message": "Valor"
},
"amountPlusGas": {
"message": "Valor + Gas"
},
"appDescription": {
"message": "Extensão para o browser de Ethereum",
"description": "A descrição da aplicação"
},
"appName": {
"message": "MetaMask",
"description": "Nome da aplicação"
},
"attemptingConnect": {
"message": "A tentar ligar à blockchain."
},
"attributions": {
"message": "Atribuições"
},
"available": {
"message": "Disponível"
},
"back": {
"message": "Voltar"
},
"balance": {
"message": "Saldo:"
},
"balances": {
"message": "O meu saldo"
},
"balanceIsInsufficientGas": {
"message": "Saldo insuficiente para a quantidade de gas total"
},
"beta": {
"message": "BETA"
},
"betweenMinAndMax": {
"message": "tem de ser maior ou igual a $1 e menor ou igual a $2.",
"description": "ajuda para introduzir hexadecimal como decimal"
},
"blockiesIdenticon": {
"message": "Usar Blockies Identicon"
},
"borrowDharma": {
"message": "Pedir Empréstimo Com Dharma (Beta)"
},
"builtInCalifornia": {
"message": "MetaMask é desenhada e construída na California."
},
"buy": {
"message": "Comprar"
},
"buyCoinbase": {
"message": "Comprar no Coinbase"
},
"buyCoinbaseExplainer": {
"message": "Coinbase é a forma mais conhecida para comprar e vender bitcoin, ethereum, e litecoin."
},
"cancel": {
"message": "Cancelar"
},
"classicInterface": {
"message": "Utilizar interface clássico"
},
"clickCopy": {
"message": "Carregue para copiar"
},
"confirm": {
"message": "Confirmar"
},
"confirmContract": {
"message": "Confirmar Contrato"
},
"confirmPassword": {
"message": "Confirmar Palavra-passe"
},
"confirmTransaction": {
"message": "Confirmar Transação"
},
"continue": {
"message": "Continuar"
},
"continueToCoinbase": {
"message": "Continuar para o Coinbase"
},
"contractDeployment": {
"message": "Distribuição do Contrato"
},
"conversionProgress": {
"message": "Conversão em progresso"
},
"copiedButton": {
"message": "Copiado"
},
"copiedClipboard": {
"message": "Copiado para a Área de Transferência"
},
"copiedExclamation": {
"message": "Copiado!"
},
"copiedSafe": {
"message": "Já copiei para um lugar seguro"
},
"copy": {
"message": "Copiar"
},
"copyToClipboard": {
"message": "Copiar para o clipboard"
},
"copyButton": {
"message": " Copiar "
},
"copyPrivateKey": {
"message": "Esta é a sua chave privada (carregue para copiar)"
},
"create": {
"message": "Criar"
},
"createAccount": {
"message": "Criar Conta"
},
"createDen": {
"message": "Criar"
},
"crypto": {
"message": "Cripto",
"description": "Tipo de câmbio (criptomoedas)"
},
"currentConversion": {
"message": "Taxa de Conversão Atual"
},
"currentNetwork": {
"message": "Rede Atual"
},
"customGas": {
"message": "Customizar Gas"
},
"customize": {
"message": "Customizar"
},
"customRPC": {
"message": "Customizar RPC"
},
"decimalsMustZerotoTen": {
"message": "Decimais devem ser no mínimo 0 e não passar de 36."
},
"decimal": {
"message": "Precisão em Decimais"
},
"defaultNetwork": {
"message": "A rede pré definida para transações em Ether é a Main Net."
},
"denExplainer": {
"message": " DEN é o armazenamento encriptado da sua palavra-passe no MetaMask."
},
"deposit": {
"message": "Depósito"
},
"depositBTC": {
"message": "Deposite as suas BTC no endereço abaixo:"
},
"depositCoin": {
"message": "Deposite $1 no endereço abaixo",
"description": "Diz ao usuário que moeda selecionou para depositar com shapeshift"
},
"depositEth": {
"message": "Depositar Eth"
},
"depositEther": {
"message": "Depositar Ether"
},
"depositFiat": {
"message": "Depositar moeda fiduciária"
},
"depositFromAccount": {
"message": "Depositar de outra conta"
},
"depositShapeShift": {
"message": "Depositar com ShapeShift"
},
"depositShapeShiftExplainer": {
"message": "Se tem criptomoedas, pode trocar e depositar Ether diretamente na sua carteira MetaMask. Não precisa de conta."
},
"details": {
"message": "Detalhes"
},
"directDeposit": {
"message": "Depósito Direto"
},
"directDepositEther": {
"message": "Depositar Diretamente Ether"
},
"directDepositEtherExplainer": {
"message": "Se já tem Ether, a forma mais rápida de ficar com Ether na sua carteira é através de depósito direto."
},
"done": {
"message": "Finalizado"
},
"downloadStatelogs": {
"message": "Descarregar Registos de Estado"
},
"edit": {
"message": "Editar"
},
"editAccountName": {
"message": "Editar Nome da Conta"
},
"emailUs": {
"message": "Fale connosco!"
},
"encryptNewDen": {
"message": "Encripte o seu novo DEN"
},
"enterPassword": {
"message": "Introduza palavra-passe"
},
"enterPasswordConfirm": {
"message": "Introduza a sua palavra-passe para confirmar"
},
"etherscanView": {
"message": "Ver conta no Etherscan"
},
"exchangeRate": {
"message": "Taxa de Câmbio"
},
"exportPrivateKey": {
"message": "Exportar Chave Privada"
},
"exportPrivateKeyWarning": {
"message": "Exportar chaves privadas por sua conta e risco."
},
"failed": {
"message": "Falhou"
},
"fiat": {
"message": "FIAT",
"description": "Tipo de câmbio"
},
"fileImportFail": {
"message": "A importação de ficheiro não está a funcionar? Carregue aqui!",
"description": "Ajuda usuários a importar as suas contas a partir de um ficheiro JSON"
},
"followTwitter": {
"message": "Siga-nos no Twitter"
},
"from": {
"message": "De"
},
"fromToSame": {
"message": "Endereços De e Para não podem ser iguais"
},
"fromShapeShift": {
"message": "De ShapeShift"
},
"gas": {
"message": "Gas",
"description": "Indicação breve do custo de gas"
},
"gasFee": {
"message": "Taxa de Gas"
},
"gasLimit": {
"message": "Limite de Gas"
},
"gasLimitCalculation": {
"message": "Calculamos o limite sugerido do gas com base nas taxas de sucesso da rede."
},
"gasLimitRequired": {
"message": "Limite de Gas Necessário"
},
"gasLimitTooLow": {
"message": "Limite de Gas deve ser no mínimo 21000"
},
"generatingSeed": {
"message": "A gerar Seed..."
},
"gasPrice": {
"message": "Preço Gas (GWEI)"
},
"gasPriceCalculation": {
"message": "Calculamos o gas sugerido com base nas taxas de sucesso da rede."
},
"gasPriceRequired": {
"message": "Preço Gas Necessário"
},
"getEther": {
"message": "Obter Ether"
},
"getEtherFromFaucet": {
"message": "Obter Ether de um faucet por $1",
"description": "Mostra nome da rede para faucet de Ether"
},
"greaterThanMin": {
"message": "tem de ser maior ou igual a $1.",
"description": "ajuda para introduzir hexadecimal como decimal"
},
"here": {
"message": "aqui",
"description": "como -clicar aqui- para mais informações (associado a troubleTokenBalances)"
},
"hereList": {
"message": "Aqui está uma lista!!!!"
},
"hide": {
"message": "Ocultar"
},
"hideToken": {
"message": "Ocultar Token"
},
"hideTokenPrompt": {
"message": "Ocultar Token?"
},
"howToDeposit": {
"message": "Como gostaria de depositar Ether?"
},
"holdEther": {
"message": "Permite ter ether & tokens, e serve como uma ponte para aplicações descentralizadas."
},
"import": {
"message": "Importar",
"description": "Botão para importar uma conta de um ficheiro selecionado"
},
"importAccount": {
"message": "Importar Conta"
},
"importAccountMsg": {
"message":"Contas importadas não irão ser associadas com a frase seed da conta criada originalmente pelo MetaMask. Saiba mais sobre contas importadas."
},
"importAnAccount": {
"message": "Importar uma conta"
},
"importDen": {
"message": "Importar DEN Existente"
},
"imported": {
"message": "Importado",
"description": "estado para mostrar que uma conta foi totalmente carregada para o keyring"
},
"infoHelp": {
"message": "Informação & Ajuda"
},
"insufficientFunds": {
"message": "Fundos insuficientes."
},
"insufficientTokens": {
"message": "Tokens insuficientes."
},
"invalidAddress": {
"message": "Endereço inválido"
},
"invalidAddressRecipient": {
"message": "O endereço do destinatário é inválido "
},
"invalidGasParams": {
"message": "Parâmetros para o Gas Inválidos"
},
"invalidInput": {
"message": "Campo inválido."
},
"invalidRequest": {
"message": "Pedido Inválido"
},
"invalidRPC": {
"message": "RPC URI Inválido"
},
"jsonFail": {
"message": "Ocorreu um erro. Por favor confirme que o seu ficheiro JSON está devidamente formatado."
},
"jsonFile": {
"message": "Ficheiro JSON",
"description": "Formatar para importar uma conta"
},
"kovan": {
"message": "Rede de Teste Kovan"
},
"knowledgeDataBase": {
"message": "Visite o nosso Centro de Conhecimento"
},
"lessThanMax": {
"message": "tem de ser menor ou igual a $1.",
"description": "ajuda para introduzir hexadecimal como decimal"
},
"likeToAddTokens": {
"message": "Gostaria de adicionar estes tokens?"
},
"limit": {
"message": "Limite"
},
"loading": {
"message": "A carregar..."
},
"loadingTokens": {
"message": "A carregar Tokens..."
},
"localhost": {
"message": "Localhost 8545"
},
"login": {
"message": "Entrar"
},
"logout": {
"message": "Sair"
},
"loose": {
"message": "Vago"
},
"loweCaseWords": {
"message": "palavras da seed apenas têm caracteres minúsculos"
},
"mainnet": {
"message": "Rede Principal de Ethereum"
},
"message": {
"message": "Mensagem"
},
"metamaskDescription": {
"message": "O MetaMask é um lugar seguro para guardar a sua identidade em em Ethereum."
},
"min": {
"message": "Mínimo"
},
"myAccounts": {
"message": "As minhas contas"
},
"mustSelectOne": {
"message": "Deve escolher no mínimo 1 token."
},
"needEtherInWallet": {
"message": "Para interagir com applicações descentralizadas usando MetaMask tem de ter Ether na sua carteira."
},
"needImportFile": {
"message": "Deve selecionar um ficheiro para importar.",
"description": "O utilizador deve adicionar um ficheiro para continuar"
},
"needImportPassword": {
"message": "Deve introduzir uma palavra-passe para o ficheiro selecionado.",
"description": "Palavra-passe e ficheiro necessários para importar uma conta"
},
"negativeETH": {
"message": "Não é possível enviar valores negativos de ETH."
},
"networks": {
"message": "Redes"
},
"newAccount": {
"message": "Conta Nova"
},
"newAccountNumberName": {
"message": "Conta $1",
"description": "Nome padrão da próxima conta a ser criado em Criar Conta"
},
"newContract": {
"message": "Contrato Novo"
},
"newPassword": {
"message": "Nova Palavra-passe (min 8 caracteres)"
},
"newRecipient": {
"message": "Recipiente Novo"
},
"newRPC": {
"message": "Novo RPC URL"
},
"next": {
"message": "Próximo"
},
"noAddressForName": {
"message": "Nenhum endereço foi estabelecido para este nome."
},
"noDeposits": {
"message": "Sem depósitos recebidos"
},
"noTransactionHistory": {
"message": "Sem histórico de transações."
},
"noTransactions": {
"message": "Sem Transações"
},
"notStarted": {
"message": "Não Iniciado"
},
"oldUI": {
"message": "UI Antigo"
},
"oldUIMessage": {
"message": "Voltou para o UI antigo. Pode reverter para o Novo UI através da opção no menu do topo direito."
},
"or": {
"message": "ou",
"description": "opção entre criar ou importar uma nova conta"
},
"passwordCorrect": {
"message": "Por favor confirme que a sua palavra-passe esteja correta."
},
"passwordMismatch": {
"message": "as palavras-passe não coincidem",
"description": "no processo de criação da palavra-passe, as duas palavras-passe não coincidiram"
},
"passwordShort": {
"message": "palavra-passe deve ser mais comprida",
"description": "no processo de criação da palavra-passe, a palavra-apasse não é longa o suficiente para ser segura"
},
"pastePrivateKey": {
"message": "Cole aqui a sua chave privada:",
"description": "Para importar uma conta através da chave privada"
},
"pasteSeed": {
"message": "Cole aqui a sua frase seed!"
},
"personalAddressDetected": {
"message": "Endereço pessoal detectado. Introduza o endereço do contrato do token."
},
"pleaseReviewTransaction": {
"message": "Por favor reveja a sua transação."
},
"privacyMsg": {
"message": "Política de Privacidade"
},
"privateKey": {
"message": "Chave Privada",
"description": "Selecione este tipo de ficheiro para importar uma conta"
},
"privateKeyWarning": {
"message": "Atenção: Nunca revele esta chave. Qualquer pessoa com acesso à sua chave privada pode roubar os bens que esta contém."
},
"privateNetwork": {
"message": "Rede Privada"
},
"qrCode": {
"message": "Mostrar Código QR"
},
"readdToken": {
"message": "Pode adicionar este token de novo clicando na opção “Adicionar token” no menu de opções da sua conta."
},
"readMore": {
"message": "Ler mais aqui."
},
"readMore2": {
"message": "Ler mais."
},
"receive": {
"message": "Receber"
},
"recipientAddress": {
"message": "Endereço do Destinatário"
},
"refundAddress": {
"message": "O seu endereço de reembolso"
},
"rejected": {
"message": "Rejeitado"
},
"resetAccount": {
"message": "Reinicializar Conta"
},
"restoreFromSeed": {
"message": "Restaurar a partir da frase seed"
},
"required": {
"message": "Necessário"
},
"retryWithMoreGas": {
"message": "Tentar novamente com um preço mais elevado aqui"
},
"revealSeedWords": {
"message": "Revelar Palavras Seed"
},
"revealSeedWordsWarning": {
"message": "Não revele as palavras seed num espaço público! Estas palavras podem ser usadas para roubar todas as suas contas."
},
"revert": {
"message": "Reverter"
},
"rinkeby": {
"message": "Rede de Teste Rinkeby"
},
"ropsten": {
"message": "Rede de Teste Ropsten"
},
"sampleAccountName": {
"message": "Ex. A minha conta nova",
"description": "Ajuda o utilizador a perceber o conceito de adicionar um nome legível à sua conta"
},
"save": {
"message": "Guardar"
},
"saveAsFile": {
"message": "Guardar como Ficheiro",
"description": "Processo de exportação de conta"
},
"saveSeedAsFile": {
"message": "Guardar Palavras Seed como um Ficheiro"
},
"search": {
"message": "Procurar"
},
"secretPhrase": {
"message": "Introduza a sua frase secreta de 12 palavras para recuperar o seu ."
},
"seedPhraseReq": {
"message": "seed phrases are 12 words long"
},
"select": {
"message": "Selecionar"
},
"selectCurrency": {
"message": "Selecionar Moeda"
},
"selectService": {
"message": "Selecionar Serviço"
},
"selectType": {
"message": "Selecionar Tipo"
},
"send": {
"message": "Enviar"
},
"sendETH": {
"message": "Enviar ETH"
},
"sendTokens": {
"message": "Enviar Tokens"
},
"sendTokensAnywhere": {
"message": "Enviar Tokens para qualquer pessoa com uma conta Ethereum"
},
"settings": {
"message": "Definições"
},
"shapeshiftBuy": {
"message": "Comprar com Shapeshift"
},
"showPrivateKeys": {
"message": "Mostrar Chaves Privadas"
},
"showQRCode": {
"message": "Mostrar Código QR"
},
"sign": {
"message": "Assinar"
},
"signMessage": {
"message": "Assinar Mensagem"
},
"signNotice": {
"message": "Assinar esta mensagem pode ter \nefeitos laterais perigosos. Apenas assine mensagens de sites que \ntotalmente confia com a sua conta total.\n Este método perigoso será removido numa versão posterior."
},
"sigRequest": {
"message": "Pedido de Assinatura"
},
"sigRequested": {
"message": "Assinatura Pedida"
},
"spaceBetween": {
"message": "só pode haver um espaço entre palavras"
},
"status": {
"message": "Estado"
},
"stateLogs": {
"message": "Registos de Estado"
},
"stateLogsDescription": {
"message": "Registo de estado podem conter o seu endereço e transações enviadas da sua conta pública."
},
"submit": {
"message": "Submeter"
},
"supportCenter": {
"message": "Visitar o nosso Centro de Suporte"
},
"symbolBetweenZeroTen": {
"message": "Símbolo deve conter entre 0 e 10 characters."
},
"takesTooLong": {
"message": "A demorar muito?"
},
"terms": {
"message": "Termos de Uso"
},
"testFaucet": {
"message": "Faucet de Teste"
},
"to": {
"message": "Para"
},
"toETHviaShapeShift": {
"message": "$1 para ETH via ShapeShift",
"description": "o sistema irá preencher o tipo de depósito no início da mensagem"
},
"tokenAddress": {
"message": "Endereço do Token"
},
"tokenAlreadyAdded": {
"message": "Token já foi adicionado."
},
"tokenBalance": {
"message": "O seu balanço é:"
},
"tokenSelection": {
"message": "Procure por tokens ou seleccione da nossa lista de tokens populares."
},
"tokenSymbol": {
"message": "Símbolo do Token"
},
"tokenWarning1": {
"message": "Registe os tokens que comprou com a sua conta MetaMask. Se comprou tokens utilizando uma conta diferente, esses tokens não irão aparecer aqui."
},
"total": {
"message": "Total"
},
"transactions": {
"message": "transações"
},
"transactionMemo": {
"message": "Notas da transação (opcional)"
},
"transactionNumber": {
"message": "Número da Transação"
},
"transfers": {
"message": "Transferências"
},
"troubleTokenBalances": {
"message": "Tivemos um problema a carregar o balanço dos seus tokens. Pode vê-los em ",
"description": "Seguido de um link (aqui) para ver o balanço dos seus tokens"
},
"twelveWords": {
"message": "Estas 12 palavras são a única forma de recuperar as suas contas na MetaMask.\nGuarde-as num local seguro e secreto."
},
"typePassword": {
"message": "Digite a sua Palavra-passe"
},
"uiWelcome": {
"message": "Bem-vindo ao seu Novo UI (Beta)"
},
"uiWelcomeMessage": {
"message": "Está agora a usar o novo UI da MetaMask. Dê uma vista de olhos, experimenta as novas funcionalidades como enviar tokens e diga-nos se tiver algum problema."
},
"unavailable": {
"message": "Indisponível"
},
"unknown": {
"message": "Desconhecido"
},
"unknownNetwork": {
"message": "Rede Privada Desconhecida"
},
"unknownNetworkId": {
"message": "Identificador da rede desconhecido"
},
"uriErrorMsg": {
"message": "Links requerem o prefixo HTTP/HTTPS apropriado."
},
"usaOnly": {
"message": "Só nos EUA",
"description": "Usar esta taxa de câmbio está limitado a pessoas residentes nos EUA"
},
"usedByClients": {
"message": "Utilizado por vários tipos de clientes"
},
"useOldUI": {
"message": "Utilizar UI antigo"
},
"validFileImport": {
"message": "Deve selecionar um ficheiro válido para importar."
},
"vaultCreated": {
"message": "Cofre Criado"
},
"viewAccount": {
"message": "Ver Conta"
},
"visitWebSite": {
"message": "Visite o nosso site"
},
"warning": {
"message": "Aviso"
},
"welcomeBeta": {
"message": "Bem-vindo ao MetaMask Beta"
},
"whatsThis": {
"message": "O que é isto?"
},
"yourSigRequested": {
"message": "A sua assinatura está a ser pedida"
},
"youSign": {
"message": "Está a assinar"
}
}

@ -6,7 +6,6 @@ const EthQuery = require('ethjs-query')
const TransactionStateManager = require('../lib/tx-state-manager') const TransactionStateManager = require('../lib/tx-state-manager')
const TxGasUtil = require('../lib/tx-gas-utils') const TxGasUtil = require('../lib/tx-gas-utils')
const PendingTransactionTracker = require('../lib/pending-tx-tracker') const PendingTransactionTracker = require('../lib/pending-tx-tracker')
const createId = require('../lib/random-id')
const NonceTracker = require('../lib/nonce-tracker') const NonceTracker = require('../lib/nonce-tracker')
/* /*
@ -92,8 +91,8 @@ module.exports = class TransactionController extends EventEmitter {
this.pendingTxTracker.on('tx:warning', (txMeta) => { this.pendingTxTracker.on('tx:warning', (txMeta) => {
this.txStateManager.updateTx(txMeta, 'transactions/pending-tx-tracker#event: tx:warning') 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: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) => { this.pendingTxTracker.on('tx:block-update', (txMeta, latestBlockNumber) => {
if (!txMeta.firstRetryBlockNumber) { if (!txMeta.firstRetryBlockNumber) {
txMeta.firstRetryBlockNumber = latestBlockNumber txMeta.firstRetryBlockNumber = latestBlockNumber
@ -186,14 +185,7 @@ module.exports = class TransactionController extends EventEmitter {
// validate // validate
await this.txGasUtil.validateTxParams(txParams) await this.txGasUtil.validateTxParams(txParams)
// construct txMeta // construct txMeta
const txMeta = { const txMeta = this.txStateManager.generateTxMeta({txParams})
id: createId(),
time: (new Date()).getTime(),
status: 'unapproved',
metamaskNetworkId: this.getNetwork(),
txParams: txParams,
loadingDefaults: true,
}
this.addTx(txMeta) this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta) this.emit('newUnapprovedTx', txMeta)
// add default tx params // add default tx params
@ -215,7 +207,6 @@ module.exports = class TransactionController extends EventEmitter {
const txParams = txMeta.txParams const txParams = txMeta.txParams
// ensure value // ensure value
txMeta.gasPriceSpecified = Boolean(txParams.gasPrice) txMeta.gasPriceSpecified = Boolean(txParams.gasPrice)
txMeta.nonceSpecified = Boolean(txParams.nonce)
let gasPrice = txParams.gasPrice let gasPrice = txParams.gasPrice
if (!gasPrice) { if (!gasPrice) {
gasPrice = this.getGasPrice ? this.getGasPrice() : await this.query.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) return await this.txGasUtil.analyzeGasUsage(txMeta)
} }
async retryTransaction (txId) { async retryTransaction (originalTxId) {
this.txStateManager.setTxStatusUnapproved(txId) const originalTxMeta = this.txStateManager.getTx(originalTxId)
const txMeta = this.txStateManager.getTx(txId) const lastGasPrice = originalTxMeta.txParams.gasPrice
txMeta.lastGasPrice = txMeta.txParams.gasPrice const txMeta = this.txStateManager.generateTxMeta({
this.txStateManager.updateTx(txMeta, 'retryTransaction: manual retry') txParams: originalTxMeta.txParams,
lastGasPrice,
loadingDefaults: false,
})
this.addTx(txMeta)
this.emit('newUnapprovedTx', txMeta)
return txMeta
} }
async updateTransaction (txMeta) { async updateTransaction (txMeta) {
@ -253,11 +250,9 @@ module.exports = class TransactionController extends EventEmitter {
// wait for a nonce // wait for a nonce
nonceLock = await this.nonceTracker.getNonceLock(fromAddress) nonceLock = await this.nonceTracker.getNonceLock(fromAddress)
// add nonce to txParams // add nonce to txParams
const nonce = txMeta.nonceSpecified ? txMeta.txParams.nonce : nonceLock.nextNonce // if txMeta has lastGasPrice then it is a retry at same nonce with higher
if (nonce > nonceLock.nextNonce) { // gas price transaction and their for the nonce should not be calculated
const message = `Specified nonce may not be larger than account's next valid nonce.` const nonce = txMeta.lastGasPrice ? txMeta.txParams.nonce : nonceLock.nextNonce
throw new Error(message)
}
txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16)) txMeta.txParams.nonce = ethUtil.addHexPrefix(nonce.toString(16))
// add nonce debugging information to txMeta // add nonce debugging information to txMeta
txMeta.nonceDetails = nonceLock.nonceDetails txMeta.nonceDetails = nonceLock.nonceDetails
@ -314,6 +309,22 @@ module.exports = class TransactionController extends EventEmitter {
// PRIVATE METHODS // 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 () { _updateMemstore () {
const unapprovedTxs = this.txStateManager.getUnapprovedTxList() const unapprovedTxs = this.txStateManager.getUnapprovedTxList()
const selectedAddressTxList = this.txStateManager.getFilteredTxList({ const selectedAddressTxList = this.txStateManager.getFilteredTxList({

@ -1,9 +1,21 @@
const extend = require('xtend') const extend = require('xtend')
const EventEmitter = require('events') const EventEmitter = require('events')
const ObservableStore = require('obs-store') const ObservableStore = require('obs-store')
const createId = require('./random-id')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const txStateHistoryHelper = require('./tx-state-history-helper') 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 { module.exports = class TransactionStateManager extends EventEmitter {
constructor ({ initState, txHistoryLimit, getNetwork }) { constructor ({ initState, txHistoryLimit, getNetwork }) {
super() super()
@ -16,6 +28,16 @@ module.exports = class TransactionStateManager extends EventEmitter {
this.getNetwork = getNetwork 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. // Returns the number of txs for the current network.
getTxCount () { getTxCount () {
return this.getTxList().length 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 // get::set status
// should return the status of the tx. // 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'. // should update the status of the tx to 'submitted'.
// and add a time stamp for when it was called
setTxStatusSubmitted (txId) { setTxStatusSubmitted (txId) {
const txMeta = this.getTx(txId)
txMeta.submittedTime = (new Date()).getTime()
this.updateTx(txMeta, 'txStateManager - add submitted time stamp')
this._setTxStatus(txId, 'submitted') this._setTxStatus(txId, 'submitted')
} }
@ -211,6 +227,12 @@ module.exports = class TransactionStateManager extends EventEmitter {
this._setTxStatus(txId, 'confirmed') this._setTxStatus(txId, 'confirmed')
} }
// should update the status dropped
setTxStatusDropped (txId) {
this._setTxStatus(txId, 'dropped')
}
setTxStatusFailed (txId, err) { setTxStatusFailed (txId, err) {
const txMeta = this.getTx(txId) const txMeta = this.getTx(txId)
txMeta.err = { txMeta.err = {

@ -116,7 +116,7 @@
"send": { "send": {
"gasLimit": "0xea60", "gasLimit": "0xea60",
"gasPrice": "0xba43b7400", "gasPrice": "0xba43b7400",
"gasTotal": "0xb451dc41b578", "gasTotal": "0xaa87bee538000",
"tokenBalance": null, "tokenBalance": null,
"from": { "from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",

@ -116,7 +116,7 @@
"send": { "send": {
"gasLimit": "0xea60", "gasLimit": "0xea60",
"gasPrice": "0xba43b7400", "gasPrice": "0xba43b7400",
"gasTotal": "0xb451dc41b578", "gasTotal": "0xaa87bee538000",
"tokenBalance": null, "tokenBalance": null,
"from": { "from": {
"address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb", "address": "0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb",

@ -0,0 +1,96 @@
////////////////////////////////////////////////////////////////////////////////
//
// Locale verification script
//
// usage:
//
// node app/scripts/verify-locale-strings.js <locale>
//
// will check the given locale against the strings in english
//
////////////////////////////////////////////////////////////////////////////////
var fs = require('fs')
var path = require('path')
console.log('Locale Verification')
var locale = process.argv[2]
if (!locale || locale == '') {
console.log('Must enter a locale as argument. exitting')
process.exit(1)
}
console.log("verifying for locale " + locale)
localeFilePath = path.join(process.cwd(), 'app', '_locales', locale, 'messages.json')
try {
localeObj = JSON.parse(fs.readFileSync(localeFilePath, 'utf8'));
} catch (e) {
if(e.code == 'ENOENT') {
console.log('Locale file not found')
} else {
console.log('Error opening your locale file: ', e)
}
process.exit(1)
}
englishFilePath = path.join(process.cwd(), 'app', '_locales', 'en', 'messages.json')
try {
englishObj = JSON.parse(fs.readFileSync(englishFilePath, 'utf8'));
} catch (e) {
if(e.code == 'ENOENT') {
console.log("English File not found")
} else {
console.log("Error opening english locale file: ", e)
}
process.exit(1)
}
console.log('\tverifying whether all your locale strings are contained in the english one')
var counter = 0
var foundErrorA = false
var notFound = [];
Object.keys(localeObj).forEach(function(key){
if (!englishObj[key]) {
foundErrorA = true
notFound.push(key)
}
counter++
})
if (foundErrorA) {
console.log('\nThe following string(s) is(are) not found in the english locale:')
notFound.forEach(function(key) {
console.log(key)
})
} else {
console.log('\tall ' + counter +' strings declared in your locale were found in the english one')
}
console.log('\n\tverifying whether your locale contains all english strings')
var counter = 0
var foundErrorB = false
var notFound = [];
Object.keys(englishObj).forEach(function(key){
if (!localeObj[key]) {
foundErrorB = true
notFound.push(key)
}
counter++
})
if (foundErrorB) {
console.log('\nThe following string(s) is(are) not found in the your locale:')
notFound.forEach(function(key) {
console.log(key)
})
} else {
console.log('\tall ' + counter +' english strings were found in your locale!')
}
if (!foundErrorA && !foundErrorB) {
console.log('You are good to go')
}

@ -14,5 +14,13 @@ That's it! When MetaMask is loaded on a computer with that language set as the s
## Testing ## Testing
To verify that your translation works, you will need to [build a local copy](https://github.com/MetaMask/metamask-extension#building-locally) of MetaMask. To automatically see if you are missing any phrases to translate, we have a script you can run (if you know how to use the command line). The script is:
```
node development/verify-locale-strings.js $YOUR_LOCALE
```
Where `$YOUR_LOCALE` is your [locale string](https://r12a.github.io/app-subtags/), i.e. the name of your language folder.
To verify that your translation works in the app, you will need to [build a local copy](https://github.com/MetaMask/metamask-extension#building-locally) of MetaMask. You will need to change your browser language, your operating system language, and restart your browser (sorry it's so much work!).

@ -408,7 +408,11 @@ function bundleTask(opts) {
.pipe(gulpif(debug, sourcemaps.init({ loadMaps: true }))) .pipe(gulpif(debug, sourcemaps.init({ loadMaps: true })))
// Minification // Minification
.pipe(gulpif(opts.isBuild, uglify({ .pipe(gulpif(opts.isBuild, uglify({
mangle: { reserved: [ 'MetamaskInpageProvider' ] }, mangle: { reserved: [ 'MetamaskInpageProvider' ] },
})))
// Transpile to ES5
.pipe(gulpif(opts.isBuild, babel({
presets: ['env']
}))) })))
// writes .map file // writes .map file
.pipe(gulpif(debug, sourcemaps.write('./'))) .pipe(gulpif(debug, sourcemaps.write('./')))

@ -29,9 +29,16 @@ function TransactionListItem () {
} }
TransactionListItem.prototype.showRetryButton = function () { TransactionListItem.prototype.showRetryButton = function () {
const { transaction = {} } = this.props const { transaction = {}, transactions } = this.props
const { status, time } = transaction const { status, submittedTime, txParams } = transaction
return status === 'submitted' && Date.now() - time > 30000 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 () { TransactionListItem.prototype.render = function () {
@ -201,6 +208,11 @@ function formatDate (date) {
function renderErrorOrWarning (transaction) { function renderErrorOrWarning (transaction) {
const { status, err, warning } = transaction const { status, err, warning } = transaction
// show dropped
if (status === 'dropped') {
return h('span.dropped', ' (Dropped)')
}
// show rejected // show rejected
if (status === 'rejected') { if (status === 'rejected') {
return h('span.error', ' (Rejected)') return h('span.error', ' (Rejected)')

@ -62,7 +62,7 @@ TransactionList.prototype.render = function () {
} }
return h(TransactionListItem, { return h(TransactionListItem, {
transaction, i, network, key, transaction, i, network, key,
conversionRate, conversionRate, transactions,
showTx: (txId) => { showTx: (txId) => {
this.props.viewPendingTx(txId) this.props.viewPendingTx(txId)
}, },

@ -247,6 +247,10 @@ app sections
color: #FFAE00; color: #FFAE00;
} }
.dropped {
color: #6195ED;
}
.lock { .lock {
width: 50px; width: 50px;
height: 50px; height: 50px;

@ -10,9 +10,10 @@
"mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./", "mock": "beefy development/mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
"watch": "mocha watch --recursive \"test/unit/**/*.js\"", "watch": "mocha watch --recursive \"test/unit/**/*.js\"",
"mascara": "gulp build && cross-env METAMASK_DEBUG=true node ./mascara/example/server", "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", "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": "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: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: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", "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: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: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": "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: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: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", "test:mascara:build:tests": "browserify test/integration/lib/first-time.js -o dist/mascara/tests.js",
@ -200,6 +201,7 @@
"envify": "^4.0.0", "envify": "^4.0.0",
"enzyme": "^3.3.0", "enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.5", "enzyme-adapter-react-15": "^1.0.5",
"es-check": "^2.0.2",
"eslint-plugin-chai": "0.0.1", "eslint-plugin-chai": "0.0.1",
"eslint-plugin-mocha": "^4.9.0", "eslint-plugin-mocha": "^4.9.0",
"eslint-plugin-react": "^7.4.0", "eslint-plugin-react": "^7.4.0",

@ -93,7 +93,7 @@ async function runSendFlowTest(assert, done) {
'send gas field should show estimated gas total converted to USD' '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() sendGasOpenCustomizeModalButton[0].click()
const customizeGasModal = await queryAsync($, '.send-v2__customize-gas') 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') assert.equal(confirmToName[0].textContent, 'Send Account 3', 'confirm screen should show correct to name')
const confirmScreenRows = await queryAsync($, '.confirm-screen-rows') const confirmScreenRows = await queryAsync($, '.confirm-screen-rows')
const confirmScreenGas = confirmScreenRows.find('.confirm-screen-row-info')[2] const confirmScreenGas = confirmScreenRows.find('.currency-display__converted-value')[0]
assert.equal(confirmScreenGas.textContent, '3.6 USD', 'confirm screen should show correct gas') assert.equal(confirmScreenGas.textContent, '3.60 USD', 'confirm screen should show correct gas')
const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[3] const confirmScreenTotal = confirmScreenRows.find('.confirm-screen-row-info')[2]
assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total') assert.equal(confirmScreenTotal.textContent, '2405.36 USD', 'confirm screen should show correct total')
const confirmScreenBackButton = await queryAsync($, '.confirm-screen-back-button') const confirmScreenBackButton = await queryAsync($, '.confirm-screen-back-button')

@ -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 () { describe('#getPendingTransactions', function () {
beforeEach(function () { beforeEach(function () {
@ -401,7 +444,7 @@ describe('Transaction Controller', function () {
{ id: 3, status: 'approved', metamaskNetworkId: currentNetworkId, txParams: {} }, { id: 3, status: 'approved', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 4, status: 'signed', metamaskNetworkId: currentNetworkId, txParams: {} }, { id: 4, status: 'signed', metamaskNetworkId: currentNetworkId, txParams: {} },
{ id: 5, status: 'submitted', 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: {} }, { id: 7, status: 'failed', metamaskNetworkId: currentNetworkId, txParams: {} },
]) ])
}) })

@ -36,7 +36,7 @@ AccountImportSubview.prototype.render = function () {
h('div.new-account-import-form', [ h('div.new-account-import-form', [
h('.new-account-import-disclaimer', [ h('.new-account-import-disclaimer', [
h('span', 'Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts '), h('span', t('importAccountMsg')),
h('span', { h('span', {
style: { style: {
cursor: 'pointer', cursor: 'pointer',
@ -47,12 +47,12 @@ AccountImportSubview.prototype.render = function () {
url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts', url: 'https://metamask.helpscoutdocs.com/article/17-what-are-loose-accounts',
}) })
}, },
}, 'here'), }, t('here')),
]), ]),
h('div.new-account-import-form__select-section', [ h('div.new-account-import-form__select-section', [
h('div.new-account-import-form__select-label', 'Select Type'), h('div.new-account-import-form__select-label', t('selectType')),
h(Select, { h(Select, {
className: 'new-account-import-form__select', className: 'new-account-import-form__select',

@ -85,7 +85,7 @@ class JsonImportSubview extends Component {
const state = this.state const state = this.state
if (!state) { if (!state) {
const message = 'You must select a valid file to import.' const message = t('validFileImport')
return this.props.displayWarning(message) return this.props.displayWarning(message)
} }

@ -1286,8 +1286,10 @@ function retryTransaction (txId) {
if (err) { if (err) {
return dispatch(actions.displayWarning(err.message)) return dispatch(actions.displayWarning(err.message))
} }
const { selectedAddressTxList } = newState
const { id: newTxId } = selectedAddressTxList[selectedAddressTxList.length - 1]
dispatch(actions.updateMetamaskState(newState)) dispatch(actions.updateMetamaskState(newState))
dispatch(actions.viewPendingTx(txId)) dispatch(actions.viewPendingTx(newTxId))
}) })
} }
} }

@ -26,6 +26,7 @@ const fuse = new Fuse(contractList, {
const actions = require('./actions') const actions = require('./actions')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const { tokenInfoGetter } = require('./token-util') const { tokenInfoGetter } = require('./token-util')
const t = require('../i18n')
const emptyAddr = '0x0000000000000000000000000000000000000000' const emptyAddr = '0x0000000000000000000000000000000000000000'
@ -139,28 +140,28 @@ AddTokenScreen.prototype.validate = function () {
if (customAddress) { if (customAddress) {
const validAddress = ethUtil.isValidAddress(customAddress) const validAddress = ethUtil.isValidAddress(customAddress)
if (!validAddress) { if (!validAddress) {
errors.customAddress = 'Address is invalid. ' errors.customAddress = t('invalidAddress')
} }
const validDecimals = customDecimals !== null && customDecimals >= 0 && customDecimals < 36 const validDecimals = customDecimals !== null && customDecimals >= 0 && customDecimals < 36
if (!validDecimals) { if (!validDecimals) {
errors.customDecimals = 'Decimals must be at least 0, and not over 36.' errors.customDecimals = t('decimalsMustZerotoTen')
} }
const symbolLen = customSymbol.trim().length const symbolLen = customSymbol.trim().length
const validSymbol = symbolLen > 0 && symbolLen < 10 const validSymbol = symbolLen > 0 && symbolLen < 10
if (!validSymbol) { if (!validSymbol) {
errors.customSymbol = 'Symbol must be between 0 and 10 characters.' errors.customSymbol = t('symbolBetweenZeroTen')
} }
const ownAddress = identitiesList.includes(standardAddress) const ownAddress = identitiesList.includes(standardAddress)
if (ownAddress) { if (ownAddress) {
errors.customAddress = 'Personal address detected. Input the token contract address.' errors.customAddress = t('personalAddressDetected')
} }
const tokenAlreadyAdded = this.checkExistingAddresses(customAddress) const tokenAlreadyAdded = this.checkExistingAddresses(customAddress)
if (tokenAlreadyAdded) { if (tokenAlreadyAdded) {
errors.customAddress = 'Token has already been added.' errors.customAddress = t('tokenAlreadyAdded')
} }
} else if ( } else if (
Object.entries(selectedTokens) Object.entries(selectedTokens)
@ -168,7 +169,7 @@ AddTokenScreen.prototype.validate = function () {
isEmpty && !isSelected isEmpty && !isSelected
), true) ), true)
) { ) {
errors.tokenSelector = 'Must select at least 1 token.' errors.tokenSelector = t('mustSelectOne')
} }
return { return {
@ -198,7 +199,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customAddress, 'add-token__add-custom-field--error': errors.customAddress,
}), }),
}, [ }, [
h('div.add-token__add-custom-label', 'Token Address'), h('div.add-token__add-custom-label', t('tokenAddress')),
h('input.add-token__add-custom-input', { h('input.add-token__add-custom-input', {
type: 'text', type: 'text',
onChange: this.tokenAddressDidChange, onChange: this.tokenAddressDidChange,
@ -211,7 +212,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customSymbol, 'add-token__add-custom-field--error': errors.customSymbol,
}), }),
}, [ }, [
h('div.add-token__add-custom-label', 'Token Symbol'), h('div.add-token__add-custom-label', t('tokenSymbol')),
h('input.add-token__add-custom-input', { h('input.add-token__add-custom-input', {
type: 'text', type: 'text',
onChange: this.tokenSymbolDidChange, onChange: this.tokenSymbolDidChange,
@ -225,7 +226,7 @@ AddTokenScreen.prototype.renderCustomForm = function () {
'add-token__add-custom-field--error': errors.customDecimals, 'add-token__add-custom-field--error': errors.customDecimals,
}), }),
}, [ }, [
h('div.add-token__add-custom-label', 'Decimals of Precision'), h('div.add-token__add-custom-label', t('decimal')),
h('input.add-token__add-custom-input', { h('input.add-token__add-custom-input', {
type: 'number', type: 'number',
onChange: this.tokenDecimalsDidChange, onChange: this.tokenDecimalsDidChange,
@ -299,11 +300,11 @@ AddTokenScreen.prototype.renderConfirmation = function () {
h('div.add-token', [ h('div.add-token', [
h('div.add-token__wrapper', [ h('div.add-token__wrapper', [
h('div.add-token__title-container.add-token__confirmation-title', [ h('div.add-token__title-container.add-token__confirmation-title', [
h('div.add-token__title', 'Add Token'), h('div.add-token__title', t('addToken')),
h('div.add-token__description', 'Would you like to add these tokens?'), h('div.add-token__description', t('likeToAddTokens')),
]), ]),
h('div.add-token__content-container.add-token__confirmation-content', [ h('div.add-token__content-container.add-token__confirmation-content', [
h('div.add-token__description.add-token__confirmation-description', 'Your balances'), h('div.add-token__description.add-token__confirmation-description', t('balances')),
h('div.add-token__confirmation-token-list', h('div.add-token__confirmation-token-list',
Object.entries(tokens) Object.entries(tokens)
.map(([ address, token ]) => ( .map(([ address, token ]) => (
@ -322,10 +323,10 @@ AddTokenScreen.prototype.renderConfirmation = function () {
h('div.add-token__buttons', [ h('div.add-token__buttons', [
h('button.btn-cancel.add-token__button', { h('button.btn-cancel.add-token__button', {
onClick: () => this.setState({ isShowingConfirmation: false }), onClick: () => this.setState({ isShowingConfirmation: false }),
}, 'Back'), }, t('back')),
h('button.btn-clear.add-token__button', { h('button.btn-clear.add-token__button', {
onClick: () => addTokens(tokens).then(goHome), onClick: () => addTokens(tokens).then(goHome),
}, 'Add Tokens'), }, t('addTokens')),
]), ]),
]) ])
) )
@ -341,15 +342,15 @@ AddTokenScreen.prototype.render = function () {
h('div.add-token', [ h('div.add-token', [
h('div.add-token__wrapper', [ h('div.add-token__wrapper', [
h('div.add-token__title-container', [ h('div.add-token__title-container', [
h('div.add-token__title', 'Add Token'), h('div.add-token__title', t('addToken')),
h('div.add-token__description', 'Keep track of the tokens you’ve bought with your MetaMask account. If you bought tokens using a different account, those tokens will not appear here.'), h('div.add-token__description', t('tokenWarning1')),
h('div.add-token__description', 'Search for tokens or select from our list of popular tokens.'), h('div.add-token__description', t('tokenSelection')),
]), ]),
h('div.add-token__content-container', [ h('div.add-token__content-container', [
h('div.add-token__input-container', [ h('div.add-token__input-container', [
h('input.add-token__input', { h('input.add-token__input', {
type: 'text', type: 'text',
placeholder: 'Search', placeholder: t('search'),
onChange: e => this.setState({ searchQuery: e.target.value }), onChange: e => this.setState({ searchQuery: e.target.value }),
}), }),
h('div.add-token__search-input-error-message', errors.tokenSelector), h('div.add-token__search-input-error-message', errors.tokenSelector),
@ -363,7 +364,7 @@ AddTokenScreen.prototype.render = function () {
h('div.add-token__add-custom', { h('div.add-token__add-custom', {
onClick: () => this.setState({ isCollapsed: !isCollapsed }), onClick: () => this.setState({ isCollapsed: !isCollapsed }),
}, [ }, [
'Add custom token', t('addCustomToken'),
h(`i.fa.fa-angle-${isCollapsed ? 'down' : 'up'}`), h(`i.fa.fa-angle-${isCollapsed ? 'down' : 'up'}`),
]), ]),
this.renderCustomForm(), this.renderCustomForm(),
@ -372,10 +373,10 @@ AddTokenScreen.prototype.render = function () {
h('div.add-token__buttons', [ h('div.add-token__buttons', [
h('button.btn-cancel.add-token__button', { h('button.btn-cancel.add-token__button', {
onClick: goHome, onClick: goHome,
}, 'Cancel'), }, t('cancel')),
h('button.btn-clear.add-token__button', { h('button.btn-clear.add-token__button', {
onClick: this.onNext, onClick: this.onNext,
}, 'Next'), }, t('next')),
]), ]),
]) ])
) )

@ -132,7 +132,7 @@ App.prototype.render = function () {
} = props } = props
const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config' const isLoadingNetwork = network === 'loading' && props.currentView.name !== 'config'
const loadMessage = loadingMessage || isLoadingNetwork ? const loadMessage = loadingMessage || isLoadingNetwork ?
`Connecting to ${this.getNetworkName()}` : null this.getConnectingLabel() : null
log.debug('Main ui render function') log.debug('Main ui render function')
return ( return (
@ -550,6 +550,27 @@ App.prototype.toggleMetamaskActive = function () {
} }
} }
App.prototype.getConnectingLabel = function () {
const { provider } = this.props
const providerName = provider.type
let name
if (providerName === 'mainnet') {
name = t('connectingToMainnet')
} else if (providerName === 'ropsten') {
name = t('connectingToRopsten')
} else if (providerName === 'kovan') {
name = t('connectingToRopsten')
} else if (providerName === 'rinkeby') {
name = t('connectingToRinkeby')
} else {
name = t('connectingToUnknown')
}
return name
}
App.prototype.getNetworkName = function () { App.prototype.getNetworkName = function () {
const { provider } = this.props const { provider } = this.props
const providerName = provider.type const providerName = provider.type
@ -557,15 +578,15 @@ App.prototype.getNetworkName = function () {
let name let name
if (providerName === 'mainnet') { if (providerName === 'mainnet') {
name = 'Main Ethereum Network' name = t('mainnet')
} else if (providerName === 'ropsten') { } else if (providerName === 'ropsten') {
name = 'Ropsten Test Network' name = t('ropsten')
} else if (providerName === 'kovan') { } else if (providerName === 'kovan') {
name = 'Kovan Test Network' name = t('kovan')
} else if (providerName === 'rinkeby') { } else if (providerName === 'rinkeby') {
name = 'Rinkeby Test Network' name = t('rinkeby')
} else { } else {
name = 'Unknown Private Network' name = t('unknownNetwork')
} }
return name return name

@ -22,12 +22,14 @@ const {
conversionUtil, conversionUtil,
multiplyCurrencies, multiplyCurrencies,
conversionGreaterThan, conversionGreaterThan,
conversionMax,
subtractCurrencies, subtractCurrencies,
} = require('../../conversion-util') } = require('../../conversion-util')
const { const {
getGasPrice, getGasPrice,
getGasLimit, getGasLimit,
getForceGasMin,
conversionRateSelector, conversionRateSelector,
getSendAmount, getSendAmount,
getSelectedToken, getSelectedToken,
@ -45,6 +47,7 @@ function mapStateToProps (state) {
return { return {
gasPrice: getGasPrice(state), gasPrice: getGasPrice(state),
gasLimit: getGasLimit(state), gasLimit: getGasLimit(state),
forceGasMin: getForceGasMin(state),
conversionRate, conversionRate,
amount: getSendAmount(state), amount: getSendAmount(state),
maxModeOn: getSendMaxModeState(state), maxModeOn: getSendMaxModeState(state),
@ -115,9 +118,9 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
updateSendAmount(maxAmount) updateSendAmount(maxAmount)
} }
updateGasPrice(gasPrice) updateGasPrice(ethUtil.addHexPrefix(gasPrice))
updateGasLimit(gasLimit) updateGasLimit(ethUtil.addHexPrefix(gasLimit))
updateGasTotal(gasTotal) updateGasTotal(ethUtil.addHexPrefix(gasTotal))
hideModal() hideModal()
} }
@ -218,7 +221,7 @@ CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
} }
CustomizeGasModal.prototype.render = function () { CustomizeGasModal.prototype.render = function () {
const { hideModal } = this.props const { hideModal, forceGasMin } = this.props
const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state const { gasPrice, gasLimit, gasTotal, error, priceSigZeros, priceSigDec } = this.state
let convertedGasPrice = conversionUtil(gasPrice, { let convertedGasPrice = conversionUtil(gasPrice, {
@ -230,6 +233,22 @@ CustomizeGasModal.prototype.render = function () {
convertedGasPrice += convertedGasPrice.match(/[.]/) ? priceSigZeros : `${priceSigDec}${priceSigZeros}` 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, { const convertedGasLimit = conversionUtil(gasLimit, {
fromNumericBase: 'hex', fromNumericBase: 'hex',
toNumericBase: 'dec', toNumericBase: 'dec',
@ -252,7 +271,7 @@ CustomizeGasModal.prototype.render = function () {
h(GasModalCard, { h(GasModalCard, {
value: convertedGasPrice, value: convertedGasPrice,
min: MIN_GAS_PRICE_GWEI, min: forceGasMin || MIN_GAS_PRICE_GWEI,
// max: 1000, // max: 1000,
step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10), step: multiplyCurrencies(MIN_GAS_PRICE_GWEI, 10),
onChange: value => this.convertAndSetGasPrice(value), onChange: value => this.convertAndSetGasPrice(value),
@ -288,7 +307,7 @@ CustomizeGasModal.prototype.render = function () {
}, [t(this.props.localeMessages, 'cancel')]), }, [t(this.props.localeMessages, 'cancel')]),
h(`div.send-v2__customize-gas__save${error ? '__error' : ''}.allcaps`, { 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(this.props.localeMessages, 'save')]), }, [t(this.props.localeMessages, 'save')]),
]), ]),

@ -8,7 +8,12 @@ const Identicon = require('../identicon')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN const BN = ethUtil.BN
const hexToBn = require('../../../../app/scripts/lib/hex-to-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-helper').getMessage const t = require('../../../i18n-helper').getMessage
const { MIN_GAS_PRICE_HEX } = require('../send/send-constants') const { MIN_GAS_PRICE_HEX } = require('../send/send-constants')
@ -44,6 +49,7 @@ function mapDispatchToProps (dispatch) {
to, to,
value: amount, value: amount,
} = txParams } = txParams
dispatch(actions.updateSend({ dispatch(actions.updateSend({
gasLimit, gasLimit,
gasPrice, gasPrice,
@ -56,6 +62,29 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.showSendPage()) dispatch(actions.showSendPage())
}, },
cancelTransaction: ({ id }) => dispatch(actions.cancelTx({ id })), 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 { return {
FIAT, FIAT,
ETH, ETH,
gasFeeInHex: txFeeBn.toString(16),
} }
} }
@ -147,7 +177,7 @@ ConfirmSendEther.prototype.getData = function () {
const { identities } = this.props const { identities } = this.props
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {} 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 { FIAT: amountInFIAT, ETH: amountInETH } = this.getAmount()
const totalInFIAT = addCurrencies(gasFeeInFIAT, amountInFIAT, { const totalInFIAT = addCurrencies(gasFeeInFIAT, amountInFIAT, {
@ -175,11 +205,20 @@ ConfirmSendEther.prototype.getData = function () {
amountInETH, amountInETH,
totalInFIAT, totalInFIAT,
totalInETH, totalInETH,
gasFeeInHex,
} }
} }
ConfirmSendEther.prototype.render = function () { 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 txMeta = this.gatherTxMeta()
const txParams = txMeta.txParams || {} const txParams = txMeta.txParams || {}
@ -193,13 +232,17 @@ ConfirmSendEther.prototype.render = function () {
name: toName, name: toName,
}, },
memo, memo,
gasFeeInFIAT, gasFeeInHex,
gasFeeInETH,
amountInFIAT, amountInFIAT,
totalInFIAT, totalInFIAT,
totalInETH, totalInETH,
} = this.getData() } = 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 // This is from the latest master
// It handles some of the errors that we are not currently handling // It handles some of the errors that we are not currently handling
// Leaving as comments fo reference // Leaving as comments fo reference
@ -218,11 +261,11 @@ ConfirmSendEther.prototype.render = function () {
// Main Send token Card // Main Send token Card
h('div.page-container', [ h('div.page-container', [
h('div.page-container__header', [ h('div.page-container__header', [
h('button.confirm-screen-back-button', { !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
onClick: () => editTransaction(txMeta), onClick: () => editTransaction(txMeta),
}, 'Edit'), }, 'Edit'),
h('div.page-container__title', 'Confirm'), h('div.page-container__title', title),
h('div.page-container__subtitle', `Please review your transaction.`), h('div.page-container__subtitle', subtitle),
]), ]),
h('.page-container__content', [ h('.page-container__content', [
h('div.flex-row.flex-center.confirm-screen-identicons', [ h('div.flex-row.flex-center.confirm-screen-identicons', [
@ -286,9 +329,12 @@ ConfirmSendEther.prototype.render = function () {
h('section.flex-row.flex-center.confirm-screen-row', [ h('section.flex-row.flex-center.confirm-screen-row', [
h('span.confirm-screen-label.confirm-screen-section-column', [ t(this.props.localeMessages, 'gasFee') ]), h('span.confirm-screen-label.confirm-screen-section-column', [ t(this.props.localeMessages, 'gasFee') ]),
h('div.confirm-screen-section-column', [ h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${gasFeeInFIAT} ${currentCurrency.toUpperCase()}`), h(GasFeeDisplay, {
gasTotal: gasTotal || gasFeeInHex,
h('div.confirm-screen-row-detail', `${gasFeeInETH} ETH`), conversionRate,
convertedCurrency,
onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
}),
]), ]),
]), ]),
@ -449,6 +495,27 @@ ConfirmSendEther.prototype.gatherTxMeta = function () {
const state = this.state const state = this.state
const txData = clone(state.txData) || clone(props.txData) 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)}`) // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData return txData
} }

@ -9,6 +9,7 @@ const actions = require('../../actions')
const t = require('../../../i18n-helper').getMessage const t = require('../../../i18n-helper').getMessage
const clone = require('clone') const clone = require('clone')
const Identicon = require('../identicon') const Identicon = require('../identicon')
const GasFeeDisplay = require('../send/gas-fee-display-v2.js')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN const BN = ethUtil.BN
const { const {
@ -89,6 +90,39 @@ function mapDispatchToProps (dispatch, ownProps) {
})) }))
dispatch(actions.showSendTokenPage()) 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 token: tokenExchangeRate
? tokenGas ? tokenGas
: null, : null,
gasFeeInHex: gasTotal.toString(16),
} }
} }
@ -240,19 +275,25 @@ ConfirmSendToken.prototype.renderHeroAmount = function () {
} }
ConfirmSendToken.prototype.renderGasFee = function () { ConfirmSendToken.prototype.renderGasFee = function () {
const { token: { symbol }, currentCurrency } = this.props const {
const { fiat: fiatGas, token: tokenGas, eth: ethGas } = this.getGasFee() currentCurrency: convertedCurrency,
conversionRate,
send: { gasTotal, gasLimit: sendGasLimit, gasPrice: sendGasPrice },
showCustomizeGasModal,
} = this.props
const txMeta = this.gatherTxMeta()
const { gasFeeInHex } = this.getGasFee()
return ( return (
h('section.flex-row.flex-center.confirm-screen-row', [ h('section.flex-row.flex-center.confirm-screen-row', [
h('span.confirm-screen-label.confirm-screen-section-column', [ t(this.props.localeMessages, 'gasFee') ]), h('span.confirm-screen-label.confirm-screen-section-column', [ t(this.props.localeMessages, 'gasFee') ]),
h('div.confirm-screen-section-column', [ h('div.confirm-screen-section-column', [
h('div.confirm-screen-row-info', `${fiatGas} ${currentCurrency}`), h(GasFeeDisplay, {
gasTotal: gasTotal || gasFeeInHex,
h( conversionRate,
'div.confirm-screen-row-detail', convertedCurrency,
tokenGas ? `${tokenGas} ${symbol}` : `${ethGas} ETH` onClick: () => showCustomizeGasModal(txMeta, sendGasLimit, sendGasPrice, gasTotal),
), }),
]), ]),
]) ])
) )
@ -308,16 +349,22 @@ ConfirmSendToken.prototype.render = function () {
this.inputs = [] this.inputs = []
const isTxReprice = Boolean(txMeta.lastGasPrice)
const title = isTxReprice ? t(this.props.localeMessages, 'reprice:title') : t(this.props.localeMessages, 'confirm')
const subtitle = isTxReprice
? t(this.props.localeMessages, 'reprice:subtitle')
: t(this.props.localeMessages, 'pleaseReviewTransaction')
return ( return (
h('div.confirm-screen-container.confirm-send-token', [ h('div.confirm-screen-container.confirm-send-token', [
// Main Send token Card // Main Send token Card
h('div.page-container', [ h('div.page-container', [
h('div.page-container__header', [ h('div.page-container__header', [
h('button.confirm-screen-back-button', { !txMeta.lastGasPrice && h('button.confirm-screen-back-button', {
onClick: () => editTransaction(txMeta), onClick: () => editTransaction(txMeta),
}, t(this.props.localeMessages, 'edit')), }, t(this.props.localeMessages, 'edit')),
h('div.page-container__title', t(this.props.localeMessages, 'confirm')), h('div.page-container__title', title),
h('div.page-container__subtitle', t(this.props.localeMessages, 'pleaseReviewTransaction')), h('div.page-container__subtitle', subtitle),
]), ]),
h('.page-container__content', [ h('.page-container__content', [
h('div.flex-row.flex-center.confirm-screen-identicons', [ h('div.flex-row.flex-center.confirm-screen-identicons', [
@ -442,6 +489,27 @@ ConfirmSendToken.prototype.gatherTxMeta = function () {
const state = this.state const state = this.state
const txData = clone(state.txData) || clone(props.txData) 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)}`) // log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData return txData
} }

@ -18,6 +18,7 @@ GasFeeDisplay.prototype.render = function () {
onClick, onClick,
primaryCurrency = 'ETH', primaryCurrency = 'ETH',
convertedCurrency, convertedCurrency,
gasLoadingError,
} = this.props } = this.props
return h('div.send-v2__gas-fee-display', [ return h('div.send-v2__gas-fee-display', [
@ -31,13 +32,15 @@ GasFeeDisplay.prototype.render = function () {
convertedPrefix: '$', convertedPrefix: '$',
readOnly: true, readOnly: true,
}) })
: h('div.currency-display', t(this.props.localeMessages, 'loading')), : gasLoadingError
? h('div.currency-display.currency-display--message', t(this.props.localeMessages, 'setGasPrice'))
: h('div.currency-display', t(this.props.localeMessages, 'loading')),
h('button.send-v2__sliders-icon-container', { h('button.sliders-icon-container', {
onClick, onClick,
disabled: !gasTotal, disabled: !gasTotal && !gasLoadingError,
}, [ }, [
h('i.fa.fa-sliders.send-v2__sliders-icon'), h('i.fa.fa-sliders.sliders-icon'),
]), ]),
]) ])

@ -48,6 +48,7 @@ function mapStateToProps (state) {
primaryCurrency, primaryCurrency,
convertedCurrency: getCurrentCurrency(state), convertedCurrency: getCurrentCurrency(state),
data, data,
selectedAddress,
amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate, amountConversionRate: selectedToken ? tokenToFiatRate : conversionRate,
tokenContract: getSelectedTokenContract(state), tokenContract: getSelectedTokenContract(state),
unapprovedTxs: state.metamask.unapprovedTxs, unapprovedTxs: state.metamask.unapprovedTxs,

@ -9,19 +9,28 @@ abiDecoder.addABI(abi)
const Identicon = require('./identicon') const Identicon = require('./identicon')
const contractMap = require('eth-contract-metadata') const contractMap = require('eth-contract-metadata')
const actions = require('../actions')
const { conversionUtil, multiplyCurrencies } = require('../conversion-util') const { conversionUtil, multiplyCurrencies } = require('../conversion-util')
const { calcTokenAmount } = require('../token-util') const { calcTokenAmount } = require('../token-util')
const { getCurrentCurrency } = require('../selectors') const { getCurrentCurrency } = require('../selectors')
const t = require('../../i18n-helper').getMessage const t = require('../../i18n-helper').getMessage
module.exports = connect(mapStateToProps)(TxListItem) module.exports = connect(mapStateToProps, mapDispatchToProps)(TxListItem)
function mapStateToProps (state) { function mapStateToProps (state) {
return { return {
tokens: state.metamask.tokens, tokens: state.metamask.tokens,
currentCurrency: getCurrentCurrency(state), currentCurrency: getCurrentCurrency(state),
tokenExchangeRates: state.metamask.tokenExchangeRates, 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 = { this.state = {
total: null, total: null,
fiatTotal: null, fiatTotal: null,
isTokenTx: null,
} }
} }
@ -40,12 +50,13 @@ TxListItem.prototype.componentDidMount = async function () {
const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data) const decodedData = txParams.data && abiDecoder.decodeMethod(txParams.data)
const { name: txDataName } = decodedData || {} const { name: txDataName } = decodedData || {}
const isTokenTx = txDataName === 'transfer'
const { total, fiatTotal } = txDataName === 'transfer' const { total, fiatTotal } = isTokenTx
? await this.getSendTokenTotal() ? await this.getSendTokenTotal()
: this.getSendEtherTotal() : this.getSendEtherTotal()
this.setState({ total, fiatTotal }) this.setState({ total, fiatTotal, isTokenTx })
} }
TxListItem.prototype.getAddressText = function () { 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 () { TxListItem.prototype.render = function () {
const { const {
transactionStatus, transactionStatus,
transactionAmount, transactionAmount,
onClick, onClick,
transActionId, transactionId,
dateString, dateString,
address, address,
className, className,
txParams,
} = this.props } = this.props
const { total, fiatTotal } = this.state const { total, fiatTotal, isTokenTx } = this.state
const showFiatTotal = transactionAmount !== '0x0' && fiatTotal const showFiatTotal = transactionAmount !== '0x0' && fiatTotal
return h(`div${className || ''}`, { return h(`div${className || ''}`, {
key: transActionId, key: transactionId,
onClick: () => onClick && onClick(transActionId), onClick: () => onClick && onClick(transactionId),
}, [ }, [
h(`div.flex-column.tx-list-item-wrapper`, {}, [ h(`div.flex-column.tx-list-item-wrapper`, {}, [
@ -224,9 +262,10 @@ TxListItem.prototype.render = function () {
className: classnames('tx-list-status', { className: classnames('tx-list-status', {
'tx-list-status--rejected': transactionStatus === 'rejected', 'tx-list-status--rejected': transactionStatus === 'rejected',
'tx-list-status--failed': transactionStatus === 'failed', 'tx-list-status--failed': transactionStatus === 'failed',
'tx-list-status--dropped': transactionStatus === 'dropped',
}), }),
}, },
transactionStatus, this.txStatusIndicator(),
), ),
]), ]),
]), ]),
@ -241,6 +280,48 @@ 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 ]), // holding on icon from design
]) ])
} }
TxListItem.prototype.txStatusIndicator = function () {
const { transactionStatus } = this.props
let name
if (transactionStatus === 'unapproved') {
name = t('unapproved')
} else if (transactionStatus === 'rejected') {
name = t('rejected')
} else if (transactionStatus === 'approved') {
name = t('approved')
} else if (transactionStatus === 'signed') {
name = t('signed')
} else if (transactionStatus === 'submitted') {
name = t('submitted')
} else if (transactionStatus === 'confirmed') {
name = t('confirmed')
} else if (transactionStatus === 'failed') {
name = t('failed')
} else if (transactionStatus === 'dropped') {
name = t('dropped')
}
return name
}

@ -40,7 +40,7 @@ TxList.prototype.render = function () {
return h('div.flex-column', [ return h('div.flex-column', [
h('div.flex-row.tx-list-header-wrapper', [ h('div.flex-row.tx-list-header-wrapper', [
h('div.flex-row.tx-list-header', [ h('div.flex-row.tx-list-header', [
h('div', 'transactions'), h('div', t('transactions')),
]), ]),
]), ]),
h('div.flex-column.tx-list-container', {}, [ h('div.flex-column.tx-list-container', {}, [
@ -75,9 +75,10 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
address: transaction.txParams.to, address: transaction.txParams.to,
transactionStatus: transaction.status, transactionStatus: transaction.status,
transactionAmount: transaction.txParams.value, transactionAmount: transaction.txParams.value,
transActionId: transaction.id, transactionId: transaction.id,
transactionHash: transaction.hash, transactionHash: transaction.hash,
transactionNetworkId: transaction.metamaskNetworkId, transactionNetworkId: transaction.metamaskNetworkId,
transactionSubmittedTime: transaction.submittedTime,
} }
const { const {
@ -85,29 +86,31 @@ TxList.prototype.renderTransactionListItem = function (transaction, conversionRa
transactionStatus, transactionStatus,
transactionAmount, transactionAmount,
dateString, dateString,
transActionId, transactionId,
transactionHash, transactionHash,
transactionNetworkId, transactionNetworkId,
transactionSubmittedTime,
} = props } = props
const { showConfTxPage } = this.props const { showConfTxPage } = this.props
const opts = { const opts = {
key: transActionId || transactionHash, key: transactionId || transactionHash,
txParams: transaction.txParams, txParams: transaction.txParams,
transactionStatus, transactionStatus,
transActionId, transactionId,
dateString, dateString,
address, address,
transactionAmount, transactionAmount,
transactionHash, transactionHash,
conversionRate, conversionRate,
tokenInfoGetter: this.tokenInfoGetter, tokenInfoGetter: this.tokenInfoGetter,
transactionSubmittedTime,
} }
const isUnapproved = transactionStatus === 'unapproved' const isUnapproved = transactionStatus === 'unapproved'
if (isUnapproved) { if (isUnapproved) {
opts.onClick = () => showConfTxPage({id: transActionId}) opts.onClick = () => showConfTxPage({ id: transactionId })
opts.transactionStatus = t(this.props.localeMessages, 'Not Started') opts.transactionStatus = t(this.props.localeMessages, 'Not Started')
} else if (transactionHash) { } else if (transactionHash) {
opts.onClick = () => this.view(transactionHash, transactionNetworkId) opts.onClick = () => this.view(transactionHash, transactionNetworkId)

@ -40,6 +40,7 @@ function mapStateToProps (state) {
currentCurrency: state.metamask.currentCurrency, currentCurrency: state.metamask.currentCurrency,
blockGasLimit: state.metamask.currentBlockGasLimit, blockGasLimit: state.metamask.currentBlockGasLimit,
computedBalances: state.metamask.computedBalances, computedBalances: state.metamask.computedBalances,
selectedAddressTxList: state.metamask.selectedAddressTxList,
} }
} }
@ -48,6 +49,23 @@ function ConfirmTxScreen () {
Component.call(this) Component.call(this)
} }
ConfirmTxScreen.prototype.componentDidUpdate = function (prevProps) {
const {
unapprovedTxs,
network,
selectedAddressTxList,
} = this.props
const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps
const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network)
const prevTxData = prevUnconfTxList[prevIndex] || {}
const prevTx = selectedAddressTxList.find(({ id }) => id === prevTxData.id) || {}
const unconfTxList = txHelper(unapprovedTxs, {}, {}, {}, network)
if (prevTx.status === 'dropped' && unconfTxList.length === 0) {
this.goHome({})
}
}
ConfirmTxScreen.prototype.render = function () { ConfirmTxScreen.prototype.render = function () {
const props = this.props const props = this.props
const { const {

@ -187,6 +187,18 @@ const conversionGreaterThan = (
return firstValue.gt(secondValue) return firstValue.gt(secondValue)
} }
const conversionMax = (
{ ...firstProps },
{ ...secondProps },
) => {
const firstIsGreater = conversionGreaterThan(
{ ...firstProps },
{ ...secondProps }
)
return firstIsGreater ? firstProps.value : secondProps.value
}
const conversionGTE = ( const conversionGTE = (
{ ...firstProps }, { ...firstProps },
{ ...secondProps }, { ...secondProps },
@ -216,6 +228,7 @@ module.exports = {
conversionGreaterThan, conversionGreaterThan,
conversionGTE, conversionGTE,
conversionLTE, conversionLTE,
conversionMax,
toNegative, toNegative,
subtractCurrencies, subtractCurrencies,
} }

@ -87,7 +87,6 @@
flex: 1 0 auto; flex: 1 0 auto;
display: flex; display: flex;
flex-flow: column nowrap; flex-flow: column nowrap;
padding-top: 4px;
} }
&__check-mark { &__check-mark {
@ -115,13 +114,11 @@
color: $white; color: $white;
font-size: 18px; font-size: 18px;
font-weight: 300; font-weight: 300;
line-height: 16px;
} }
&__balance { &__balance {
color: $dusty-gray; color: $dusty-gray;
font-size: 14px; font-size: 14px;
line-height: 19px;
} }
&__action { &__action {

@ -242,6 +242,22 @@ section .confirm-screen-account-number,
} }
} }
@media screen and (max-width: 379px) {
.confirm-screen-row {
span.confirm-screen-section-column {
flex: 0.4;
}
div.confirm-screen-section-column {
flex: 0.6;
}
.currency-display__input {
font-size: 14px;
}
}
}
.confirm-screen-row-detail { .confirm-screen-row-detail {
font-size: 12px; font-size: 12px;
line-height: 16px; line-height: 16px;

@ -10,8 +10,9 @@
.network-component.pointer { .network-component.pointer {
border: 2px solid $silver; border: 2px solid $silver;
border-radius: 82px; border-radius: 82px;
padding: 3px; padding: 7px 3px;
flex: 0 0 auto; flex: 0 0 auto;
display: flex;
&.ethereum-network .menu-icon-circle div { &.ethereum-network .menu-icon-circle div {
background-color: rgba(3, 135, 137, .7) !important; background-color: rgba(3, 135, 137, .7) !important;

@ -265,7 +265,6 @@ $wallet-view-bg: $alabaster;
.account-name { .account-name {
font-size: 24px; font-size: 24px;
font-weight: 300; font-weight: 300;
line-height: 20px;
color: $black; color: $black;
margin-top: 8px; margin-top: 8px;
margin-bottom: .9rem; margin-bottom: .9rem;

@ -660,6 +660,13 @@
&__gas-fee-display { &__gas-fee-display {
width: 100%; width: 100%;
position: relative;
.currency-display--message {
padding: 8px 38px 8px 10px;
display: flex;
align-items: center;
}
} }
&__sliders-icon-container { &__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;
}

@ -97,7 +97,7 @@
.tx-list-content-wrapper { .tx-list-content-wrapper {
align-items: stretch; align-items: stretch;
margin-bottom: 4px; margin: 4px 0;
flex: 1 0 auto; flex: 1 0 auto;
width: 100%; width: 100%;
display: flex; display: flex;
@ -126,6 +126,54 @@
} }
} }
.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;
text-align: center;
}
}
.tx-list-date { .tx-list-date {
color: $dusty-gray; color: $dusty-gray;
font-size: 12px; font-size: 12px;
@ -136,6 +184,7 @@
align-self: center; align-self: center;
flex: 0 0 auto; flex: 0 0 auto;
margin-right: 16px; margin-right: 16px;
display: flex;
} }
.tx-list-account-and-status-wrapper { .tx-list-account-and-status-wrapper {
@ -189,6 +238,10 @@
.tx-list-status--failed { .tx-list-status--failed {
color: $monzo; color: $monzo;
} }
.tx-list-status--dropped {
opacity: 0.5;
}
} }
.tx-list-item { .tx-list-item {

@ -13,7 +13,6 @@ body {
font-family: Roboto, Arial; font-family: Roboto, Arial;
color: #4d4d4d; color: #4d4d4d;
font-weight: 300; font-weight: 300;
line-height: 1.4em;
background: #f7f7f7; background: #f7f7f7;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -103,6 +102,7 @@ input.large-input {
&::after { &::after {
content: '\00D7'; content: '\00D7';
font-size: 40px; font-size: 40px;
line-height: 20px;
} }
} }

@ -112,10 +112,6 @@ section {
display: block; display: block;
} }
body {
line-height: 1;
}
ol, ol,
ul { ul {
list-style: none; list-style: none;

@ -46,6 +46,7 @@ $manatee: #93949d;
$spindle: #c7ddec; $spindle: #c7ddec;
$mid-gray: #5b5d67; $mid-gray: #5b5d67;
$cape-cod: #38393a; $cape-cod: #38393a;
$onahau: #d1edff;
$java: #29b6af; $java: #29b6af;
$wild-strawberry: #ff4a8d; $wild-strawberry: #ff4a8d;
$cornflower-blue: #7057ff; $cornflower-blue: #7057ff;

@ -4,6 +4,7 @@ const Component = require('react').Component
const connect = require('../../../metamask-connect') const connect = require('../../../metamask-connect')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const actions = require('../../../actions') const actions = require('../../../actions')
const t = require('../../../../i18n')
module.exports = connect(mapStateToProps)(RevealSeedConfirmation) module.exports = connect(mapStateToProps)(RevealSeedConfirmation)
@ -49,13 +50,13 @@ RevealSeedConfirmation.prototype.render = function () {
}, },
}, [ }, [
h('h4', 'Do not recover your seed words in a public place! These words can be used to steal all your accounts.'), h('h4', t('revealSeedWordsWarning')),
// confirmation // confirmation
h('input.large-input.letter-spacey', { h('input.large-input.letter-spacey', {
type: 'password', type: 'password',
id: 'password-box', id: 'password-box',
placeholder: 'Enter your password to confirm', placeholder: t('enterPasswordConfirm'),
onKeyPress: this.checkConfirmation.bind(this), onKeyPress: this.checkConfirmation.bind(this),
style: { style: {
width: 260, width: 260,
@ -91,7 +92,7 @@ RevealSeedConfirmation.prototype.render = function () {
), ),
props.inProgress && ( props.inProgress && (
h('span.in-progress-notification', 'Generating Seed...') h('span.in-progress-notification', t('generatingSeed'))
), ),
]), ]),
]) ])

@ -2,6 +2,7 @@ const inherits = require('util').inherits
const PersistentForm = require('../../../lib/persistent-form') const PersistentForm = require('../../../lib/persistent-form')
const connect = require('../../metamask-connect') const connect = require('../../metamask-connect')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const t = require('../../../i18n')
const actions = require('../../actions') const actions = require('../../actions')
module.exports = connect(mapStateToProps)(RestoreVaultScreen) module.exports = connect(mapStateToProps)(RestoreVaultScreen)
@ -36,23 +37,23 @@ RestoreVaultScreen.prototype.render = function () {
padding: 6, padding: 6,
}, },
}, [ }, [
'Restore Vault', t('restoreVault'),
]), ]),
// wallet seed entry // wallet seed entry
h('h3', 'Wallet Seed'), h('h3', t('walletSeed')),
h('textarea.twelve-word-phrase.letter-spacey', { h('textarea.twelve-word-phrase.letter-spacey', {
dataset: { dataset: {
persistentFormId: 'wallet-seed', persistentFormId: 'wallet-seed',
}, },
placeholder: 'Enter your secret twelve word phrase here to restore your vault.', placeholder: t('secretPhrase'),
}), }),
// password // password
h('input.large-input.letter-spacey', { h('input.large-input.letter-spacey', {
type: 'password', type: 'password',
id: 'password-box', id: 'password-box',
placeholder: 'New Password (min 8 chars)', placeholder: t('newPassword8Chars'),
dataset: { dataset: {
persistentFormId: 'password', persistentFormId: 'password',
}, },
@ -66,7 +67,7 @@ RestoreVaultScreen.prototype.render = function () {
h('input.large-input.letter-spacey', { h('input.large-input.letter-spacey', {
type: 'password', type: 'password',
id: 'password-box-confirm', id: 'password-box-confirm',
placeholder: 'Confirm Password', placeholder: t('confirmPassword'),
onKeyPress: this.createOnEnter.bind(this), onKeyPress: this.createOnEnter.bind(this),
dataset: { dataset: {
persistentFormId: 'password-confirmation', persistentFormId: 'password-confirmation',
@ -93,16 +94,20 @@ RestoreVaultScreen.prototype.render = function () {
// cancel // cancel
h('button.primary', { h('button.primary', {
onClick: this.showInitializeMenu.bind(this), onClick: this.showInitializeMenu.bind(this),
}, 'CANCEL'), style: {
textTransform: 'uppercase',
},
}, t('cancel')),
// submit // submit
h('button.primary', { h('button.primary', {
onClick: this.createNewVaultAndRestore.bind(this), onClick: this.createNewVaultAndRestore.bind(this),
}, 'OK'), style: {
textTransform: 'uppercase',
},
}, t('ok')),
]), ]),
]) ])
) )
} }
@ -131,13 +136,13 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
var passwordConfirmBox = document.getElementById('password-box-confirm') var passwordConfirmBox = document.getElementById('password-box-confirm')
var passwordConfirm = passwordConfirmBox.value var passwordConfirm = passwordConfirmBox.value
if (password.length < 8) { if (password.length < 8) {
this.warning = 'Password not long enough' this.warning = t('passwordNotLongEnough')
this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.displayWarning(this.warning))
return return
} }
if (password !== passwordConfirm) { if (password !== passwordConfirm) {
this.warning = 'Passwords don\'t match' this.warning = t('passwordsDontMatch')
this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.displayWarning(this.warning))
return return
} }
@ -147,18 +152,18 @@ RestoreVaultScreen.prototype.createNewVaultAndRestore = function () {
// true if the string has more than a space between words. // true if the string has more than a space between words.
if (seed.split(' ').length > 1) { if (seed.split(' ').length > 1) {
this.warning = 'there can only be a space between words' this.warning = t('spaceBetween')
this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.displayWarning(this.warning))
return return
} }
// true if seed contains a character that is not between a-z or a space // true if seed contains a character that is not between a-z or a space
if (!seed.match(/^[a-z ]+$/)) { if (!seed.match(/^[a-z ]+$/)) {
this.warning = 'seed words only have lowercase characters' this.warning = t('loweCaseWords')
this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.displayWarning(this.warning))
return return
} }
if (seed.split(' ').length !== 12) { if (seed.split(' ').length !== 12) {
this.warning = 'seed phrases are 12 words long' this.warning = t('seedPhraseReq')
this.props.dispatch(actions.displayWarning(this.warning)) this.props.dispatch(actions.displayWarning(this.warning))
return return
} }

@ -38,6 +38,7 @@ function reduceMetamask (state, action) {
errors: {}, errors: {},
maxModeOn: false, maxModeOn: false,
editingTransactionId: null, editingTransactionId: null,
forceGasMin: null,
}, },
coinOptions: {}, coinOptions: {},
useBlockie: false, useBlockie: false,
@ -298,6 +299,7 @@ function reduceMetamask (state, action) {
memo: '', memo: '',
errors: {}, errors: {},
editingTransactionId: null, editingTransactionId: null,
forceGasMin: null,
}, },
}) })

@ -18,6 +18,7 @@ const selectors = {
getCurrentAccountWithSendEtherInfo, getCurrentAccountWithSendEtherInfo,
getGasPrice, getGasPrice,
getGasLimit, getGasLimit,
getForceGasMin,
getAddressBook, getAddressBook,
getSendFrom, getSendFrom,
getCurrentCurrency, getCurrentCurrency,
@ -130,6 +131,10 @@ function getGasLimit (state) {
return state.metamask.send.gasLimit return state.metamask.send.gasLimit
} }
function getForceGasMin (state) {
return state.metamask.send.forceGasMin
}
function getSendFrom (state) { function getSendFrom (state) {
return state.metamask.send.from return state.metamask.send.from
} }

@ -1,6 +1,7 @@
const { inherits } = require('util') const { inherits } = require('util')
const PersistentForm = require('../lib/persistent-form') const PersistentForm = require('../lib/persistent-form')
const h = require('react-hyperscript') const h = require('react-hyperscript')
const t = require('../i18n')
const ethAbi = require('ethereumjs-abi') const ethAbi = require('ethereumjs-abi')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
@ -42,6 +43,7 @@ function SendTransactionScreen () {
to: null, to: null,
amount: null, amount: null,
}, },
gasLoadingError: false,
} }
this.handleToChange = this.handleToChange.bind(this) this.handleToChange = this.handleToChange.bind(this)
@ -128,6 +130,10 @@ SendTransactionScreen.prototype.updateGas = function () {
.then(([gasPrice, gas]) => { .then(([gasPrice, gas]) => {
const newGasTotal = this.getGasTotal(gas, gasPrice) const newGasTotal = this.getGasTotal(gas, gasPrice)
updateGasTotal(newGasTotal) updateGasTotal(newGasTotal)
this.setState({ gasLoadingError: false })
})
.catch(err => {
this.setState({ gasLoadingError: true })
}) })
} else { } else {
const newGasTotal = this.getGasTotal(gasLimit, gasPrice) const newGasTotal = this.getGasTotal(gasLimit, gasPrice)
@ -180,13 +186,12 @@ SendTransactionScreen.prototype.componentDidUpdate = function (prevProps) {
SendTransactionScreen.prototype.renderHeader = function () { SendTransactionScreen.prototype.renderHeader = function () {
const { selectedToken, clearSend, goHome } = this.props const { selectedToken, clearSend, goHome } = this.props
const tokenText = selectedToken ? 'tokens' : 'ETH'
return h('div.page-container__header', [ return h('div.page-container__header', [
h('div.page-container__title', selectedToken ? 'Send Tokens' : 'Send ETH'), h('div.page-container__title', selectedToken ? t('sendTokens') : t('sendETH')),
h('div.page-container__subtitle', `Only send ${tokenText} to an Ethereum address.`), h('div.page-container__subtitle', t('onlySendToEtherAddress')),
h('div.page-container__header-close', { h('div.page-container__header-close', {
onClick: () => { onClick: () => {
@ -257,11 +262,11 @@ SendTransactionScreen.prototype.handleToChange = function (to) {
let toError = null let toError = null
if (!to) { if (!to) {
toError = 'Required' toError = t('required')
} else if (!isValidAddress(to)) { } else if (!isValidAddress(to)) {
toError = 'Recipient address is invalid' toError = t('invalidAddressRecipient')
} else if (to === from) { } else if (to === from) {
toError = 'From and To address cannot be the same' toError = t('fromToSame')
} }
updateSendTo(to) updateSendTo(to)
@ -277,9 +282,9 @@ SendTransactionScreen.prototype.renderToRow = function () {
h('div.send-v2__form-label', [ h('div.send-v2__form-label', [
'To:', t('to'),
this.renderErrorMessage('to'), this.renderErrorMessage(t('to')),
]), ]),
@ -377,11 +382,11 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
) )
if (conversionRate && !sufficientBalance) { if (conversionRate && !sufficientBalance) {
amountError = 'Insufficient funds.' amountError = t('insufficientFunds')
} else if (verifyTokenBalance && !sufficientTokens) { } else if (verifyTokenBalance && !sufficientTokens) {
amountError = 'Insufficient tokens.' amountError = t('insufficientTokens')
} else if (amountLessThanZero) { } else if (amountLessThanZero) {
amountError = 'Can not send negative amounts of ETH.' amountError = t('negativeETH')
} }
updateSendErrors({ amount: amountError }) updateSendErrors({ amount: amountError })
@ -411,7 +416,7 @@ SendTransactionScreen.prototype.renderAmountRow = function () {
setMaxModeTo(true) setMaxModeTo(true)
this.setAmountToMax() this.setAmountToMax()
}, },
}, [ !maxModeOn ? 'Max' : '' ]), }, [ !maxModeOn ? t('max') : '' ]),
]), ]),
h('div.send-v2__form-field', [ h('div.send-v2__form-field', [
@ -436,10 +441,11 @@ SendTransactionScreen.prototype.renderGasRow = function () {
showCustomizeGasModal, showCustomizeGasModal,
gasTotal, gasTotal,
} = this.props } = this.props
const { gasLoadingError } = this.state
return h('div.send-v2__form-row', [ return h('div.send-v2__form-row', [
h('div.send-v2__form-label', 'Gas fee:'), h('div.send-v2__form-label', h('gasFee')),
h('div.send-v2__form-field', [ h('div.send-v2__form-field', [
@ -448,6 +454,7 @@ SendTransactionScreen.prototype.renderGasRow = function () {
conversionRate, conversionRate,
convertedCurrency, convertedCurrency,
onClick: showCustomizeGasModal, onClick: showCustomizeGasModal,
gasLoadingError,
}), }),
]), ]),
@ -507,11 +514,11 @@ SendTransactionScreen.prototype.renderFooter = function () {
clearSend() clearSend()
goHome() goHome()
}, },
}, 'Cancel'), }, t('cancel')),
h('button.btn-clear.page-container__footer-button', { h('button.btn-clear.page-container__footer-button', {
disabled: !noErrors || !gasTotal || missingTokenBalance, disabled: !noErrors || !gasTotal || missingTokenBalance,
onClick: event => this.onSubmit(event), onClick: event => this.onSubmit(event),
}, 'Next'), }, t('next')),
]) ])
} }
@ -571,9 +578,11 @@ SendTransactionScreen.prototype.getEditedTx = function () {
data, data,
}) })
} else { } else {
const data = unapprovedTxs[editingTransactionId].txParams.data
Object.assign(editingTx.txParams, { Object.assign(editingTx.txParams, {
value: ethUtil.addHexPrefix(amount), value: ethUtil.addHexPrefix(amount),
to: ethUtil.addHexPrefix(to), to: ethUtil.addHexPrefix(to),
data,
}) })
} }

@ -10,6 +10,7 @@ const TabBar = require('./components/tab-bar')
const SimpleDropdown = require('./components/dropdowns/simple-dropdown') const SimpleDropdown = require('./components/dropdowns/simple-dropdown')
const ToggleButton = require('react-toggle-button') const ToggleButton = require('react-toggle-button')
const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums const { OLD_UI_NETWORK_TYPE } = require('../../app/scripts/config').enums
const t = require('../i18n')
const getInfuraCurrencyOptions = () => { const getInfuraCurrencyOptions = () => {
const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => { const sortedCurrencies = infuraCurrencies.objects.sort((a, b) => {
@ -61,8 +62,8 @@ class Settings extends Component {
return h('div.settings__tabs', [ return h('div.settings__tabs', [
h(TabBar, { h(TabBar, {
tabs: [ tabs: [
{ content: 'Settings', key: 'settings' }, { content: t('settings'), key: 'settings' },
{ content: 'Info', key: 'info' }, { content: t('info'), key: 'info' },
], ],
defaultTab: activeTab, defaultTab: activeTab,
tabSelected: key => this.setState({ activeTab: key }), tabSelected: key => this.setState({ activeTab: key }),
@ -75,7 +76,7 @@ class Settings extends Component {
return h('div.settings__content-row', [ return h('div.settings__content-row', [
h('div.settings__content-item', [ h('div.settings__content-item', [
h('span', 'Use Blockies Identicon'), h('span', t('blockiesIdenticon')),
]), ]),
h('div.settings__content-item', [ h('div.settings__content-item', [
h('div.settings__content-item-col', [ h('div.settings__content-item-col', [
@ -95,13 +96,13 @@ class Settings extends Component {
return h('div.settings__content-row', [ return h('div.settings__content-row', [
h('div.settings__content-item', [ h('div.settings__content-item', [
h('span', 'Current Conversion'), h('span', t('currentConversion')),
h('span.settings__content-description', `Updated ${Date(conversionDate)}`), h('span.settings__content-description', `Updated ${Date(conversionDate)}`),
]), ]),
h('div.settings__content-item', [ h('div.settings__content-item', [
h('div.settings__content-item-col', [ h('div.settings__content-item-col', [
h(SimpleDropdown, { h(SimpleDropdown, {
placeholder: 'Select Currency', placeholder: t('selectCurrency'),
options: getInfuraCurrencyOptions(), options: getInfuraCurrencyOptions(),
selectedOption: currentCurrency, selectedOption: currentCurrency,
onSelect: newCurrency => setCurrentCurrency(newCurrency), onSelect: newCurrency => setCurrentCurrency(newCurrency),
@ -141,31 +142,31 @@ class Settings extends Component {
switch (provider.type) { switch (provider.type) {
case 'mainnet': case 'mainnet':
title = 'Current Network' title = t('currentNetwork')
value = 'Main Ethereum Network' value = t('mainnet')
color = '#038789' color = '#038789'
break break
case 'ropsten': case 'ropsten':
title = 'Current Network' title = t('currentNetwork')
value = 'Ropsten Test Network' value = t('ropsten')
color = '#e91550' color = '#e91550'
break break
case 'kovan': case 'kovan':
title = 'Current Network' title = t('currentNetwork')
value = 'Kovan Test Network' value = t('kovan')
color = '#690496' color = '#690496'
break break
case 'rinkeby': case 'rinkeby':
title = 'Current Network' title = t('currentNetwork')
value = 'Rinkeby Test Network' value = t('rinkeby')
color = '#ebb33f' color = '#ebb33f'
break break
default: default:
title = 'Current RPC' title = t('currentRpc')
value = provider.rpcTarget value = provider.rpcTarget
} }
@ -186,12 +187,12 @@ class Settings extends Component {
return ( return (
h('div.settings__content-row', [ h('div.settings__content-row', [
h('div.settings__content-item', [ h('div.settings__content-item', [
h('span', 'New RPC URL'), h('span', t('newRPC')),
]), ]),
h('div.settings__content-item', [ h('div.settings__content-item', [
h('div.settings__content-item-col', [ h('div.settings__content-item-col', [
h('input.settings__input', { h('input.settings__input', {
placeholder: 'New RPC URL', placeholder: t('newRPC'),
onChange: event => this.setState({ newRpc: event.target.value }), onChange: event => this.setState({ newRpc: event.target.value }),
onKeyPress: event => { onKeyPress: event => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
@ -204,7 +205,7 @@ class Settings extends Component {
event.preventDefault() event.preventDefault()
this.validateRpc(this.state.newRpc) this.validateRpc(this.state.newRpc)
}, },
}, 'Save'), }, t('save')),
]), ]),
]), ]),
]) ])
@ -220,9 +221,9 @@ class Settings extends Component {
const appendedRpc = `http://${newRpc}` const appendedRpc = `http://${newRpc}`
if (validUrl.isWebUri(appendedRpc)) { if (validUrl.isWebUri(appendedRpc)) {
displayWarning('URIs require the appropriate HTTP/HTTPS prefix.') displayWarning(t('uriErrorMsg'))
} else { } else {
displayWarning('Invalid RPC URI') displayWarning(t('invalidRPC'))
} }
} }
} }
@ -231,10 +232,10 @@ class Settings extends Component {
return ( return (
h('div.settings__content-row', [ h('div.settings__content-row', [
h('div.settings__content-item', [ h('div.settings__content-item', [
h('div', 'State Logs'), h('div', t('stateLogs')),
h( h(
'div.settings__content-description', 'div.settings__content-description',
'State logs contain your public account addresses and sent transactions.' t('stateLogsDescription')
), ),
]), ]),
h('div.settings__content-item', [ h('div.settings__content-item', [
@ -243,13 +244,13 @@ class Settings extends Component {
onClick (event) { onClick (event) {
window.logStateString((err, result) => { window.logStateString((err, result) => {
if (err) { if (err) {
this.state.dispatch(actions.displayWarning('Error in retrieving state logs.')) this.state.dispatch(actions.displayWarning(t('stateLogError')))
} else { } else {
exportAsFile('MetaMask State Logs.json', result) exportAsFile('MetaMask State Logs.json', result)
} }
}) })
}, },
}, 'Download State Logs'), }, t('downloadStateLogs')),
]), ]),
]), ]),
]) ])
@ -261,7 +262,7 @@ class Settings extends Component {
return ( return (
h('div.settings__content-row', [ h('div.settings__content-row', [
h('div.settings__content-item', 'Reveal Seed Words'), h('div.settings__content-item', t('revealSeedWords')),
h('div.settings__content-item', [ h('div.settings__content-item', [
h('div.settings__content-item-col', [ h('div.settings__content-item-col', [
h('button.settings__clear-button.settings__clear-button--red', { h('button.settings__clear-button.settings__clear-button--red', {
@ -269,7 +270,7 @@ class Settings extends Component {
event.preventDefault() event.preventDefault()
revealSeedConfirmation() revealSeedConfirmation()
}, },
}, 'Reveal Seed Words'), }, t('revealSeedWords')),
]), ]),
]), ]),
]) ])
@ -281,7 +282,7 @@ class Settings extends Component {
return ( return (
h('div.settings__content-row', [ h('div.settings__content-row', [
h('div.settings__content-item', 'Use old UI'), h('div.settings__content-item', t('useOldUI')),
h('div.settings__content-item', [ h('div.settings__content-item', [
h('div.settings__content-item-col', [ h('div.settings__content-item-col', [
h('button.settings__clear-button.settings__clear-button--orange', { h('button.settings__clear-button.settings__clear-button--orange', {
@ -289,7 +290,7 @@ class Settings extends Component {
event.preventDefault() event.preventDefault()
setFeatureFlagToBeta() setFeatureFlagToBeta()
}, },
}, 'Use old UI'), }, t('useOldUI')),
]), ]),
]), ]),
]) ])
@ -300,7 +301,7 @@ class Settings extends Component {
const { showResetAccountConfirmationModal } = this.props const { showResetAccountConfirmationModal } = this.props
return h('div.settings__content-row', [ return h('div.settings__content-row', [
h('div.settings__content-item', 'Reset Account'), h('div.settings__content-item', t('resetAccount')),
h('div.settings__content-item', [ h('div.settings__content-item', [
h('div.settings__content-item-col', [ h('div.settings__content-item-col', [
h('button.settings__clear-button.settings__clear-button--orange', { h('button.settings__clear-button.settings__clear-button--orange', {
@ -308,7 +309,7 @@ class Settings extends Component {
event.preventDefault() event.preventDefault()
showResetAccountConfirmationModal() showResetAccountConfirmationModal()
}, },
}, 'Reset Account'), }, t('resetAccount')),
]), ]),
]), ]),
]) ])
@ -344,13 +345,13 @@ class Settings extends Component {
renderInfoLinks () { renderInfoLinks () {
return ( return (
h('div.settings__content-item.settings__content-item--without-height', [ h('div.settings__content-item.settings__content-item--without-height', [
h('div.settings__info-link-header', 'Links'), h('div.settings__info-link-header', t('links')),
h('div.settings__info-link-item', [ h('div.settings__info-link-item', [
h('a', { h('a', {
href: 'https://metamask.io/privacy.html', href: 'https://metamask.io/privacy.html',
target: '_blank', target: '_blank',
}, [ }, [
h('span.settings__info-link', 'Privacy Policy'), h('span.settings__info-link', t('privacyMsg')),
]), ]),
]), ]),
h('div.settings__info-link-item', [ h('div.settings__info-link-item', [
@ -358,7 +359,7 @@ class Settings extends Component {
href: 'https://metamask.io/terms.html', href: 'https://metamask.io/terms.html',
target: '_blank', target: '_blank',
}, [ }, [
h('span.settings__info-link', 'Terms of Use'), h('span.settings__info-link', t('terms')),
]), ]),
]), ]),
h('div.settings__info-link-item', [ h('div.settings__info-link-item', [
@ -366,7 +367,7 @@ class Settings extends Component {
href: 'https://metamask.io/attributions.html', href: 'https://metamask.io/attributions.html',
target: '_blank', target: '_blank',
}, [ }, [
h('span.settings__info-link', 'Attributions'), h('span.settings__info-link', t('attributions')),
]), ]),
]), ]),
h('hr.settings__info-separator'), h('hr.settings__info-separator'),
@ -375,7 +376,7 @@ class Settings extends Component {
href: 'https://support.metamask.io', href: 'https://support.metamask.io',
target: '_blank', target: '_blank',
}, [ }, [
h('span.settings__info-link', 'Visit our Support Center'), h('span.settings__info-link', t('supportCenter')),
]), ]),
]), ]),
h('div.settings__info-link-item', [ h('div.settings__info-link-item', [
@ -383,7 +384,7 @@ class Settings extends Component {
href: 'https://metamask.io/', href: 'https://metamask.io/',
target: '_blank', target: '_blank',
}, [ }, [
h('span.settings__info-link', 'Visit our web site'), h('span.settings__info-link', t('visitWebSite')),
]), ]),
]), ]),
h('div.settings__info-link-item', [ h('div.settings__info-link-item', [
@ -391,7 +392,7 @@ class Settings extends Component {
target: '_blank', target: '_blank',
href: 'mailto:help@metamask.io?subject=Feedback', href: 'mailto:help@metamask.io?subject=Feedback',
}, [ }, [
h('span.settings__info-link', 'Email us!'), h('span.settings__info-link', t('emailUs')),
]), ]),
]), ]),
]) ])
@ -413,7 +414,7 @@ class Settings extends Component {
h('div.settings__info-item', [ h('div.settings__info-item', [
h( h(
'div.settings__info-about', 'div.settings__info-about',
'MetaMask is designed and built in California.' t('builtInCalifornia')
), ),
]), ]),
]), ]),
@ -490,3 +491,4 @@ const mapDispatchToProps = dispatch => {
} }
module.exports = connect(mapStateToProps, mapDispatchToProps)(Settings) module.exports = connect(mapStateToProps, mapDispatchToProps)(Settings)

@ -67,7 +67,7 @@ UnlockScreen.prototype.render = function () {
style: { style: {
margin: 10, margin: 10,
}, },
}, 'Log In'), }, t('login')),
h('p.pointer', { h('p.pointer', {
onClick: () => { onClick: () => {
@ -81,7 +81,7 @@ UnlockScreen.prototype.render = function () {
color: 'rgb(247, 134, 28)', color: 'rgb(247, 134, 28)',
textDecoration: 'underline', textDecoration: 'underline',
}, },
}, 'Restore from seed phrase'), }, t('restoreFromSeed')),
h('p.pointer', { h('p.pointer', {
onClick: () => { onClick: () => {
@ -94,7 +94,7 @@ UnlockScreen.prototype.render = function () {
textDecoration: 'underline', textDecoration: 'underline',
marginTop: '32px', marginTop: '32px',
}, },
}, 'Use classic interface'), }, t('classicInterface')),
]) ])
) )
} }

Loading…
Cancel
Save