aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorDan <danjm.com@gmail.com>2018-03-14 10:32:27 +0800
committerDan <danjm.com@gmail.com>2018-03-14 10:32:27 +0800
commit798988597bd17aa73d7a20502905a453f1d51ba4 (patch)
tree69f083adbe19ce4e5b8094f1004cb1fd3a3f75c9 /app
parentcc267d6c818c83b0384b569733d05efef384ac3e (diff)
parent5fbfb0b67c9eb50b6cc503e78154ab237d2d9731 (diff)
downloadtangerine-wallet-browser-798988597bd17aa73d7a20502905a453f1d51ba4.tar.gz
tangerine-wallet-browser-798988597bd17aa73d7a20502905a453f1d51ba4.tar.zst
tangerine-wallet-browser-798988597bd17aa73d7a20502905a453f1d51ba4.zip
Merge branch 'master' into retry-tx-refractor
Diffstat (limited to 'app')
-rw-r--r--app/_locales/en/messages.json605
-rw-r--r--app/manifest.json7
-rw-r--r--app/scripts/background.js35
-rw-r--r--app/scripts/lib/local-store.js38
-rw-r--r--app/scripts/lib/tx-gas-utils.js6
5 files changed, 680 insertions, 11 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 8c28f1c43..1ca31427d 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1,10 +1,609 @@
{
+ "accept": {
+ "message": "Accept"
+ },
+ "account": {
+ "message": "Account"
+ },
+ "accountDetails": {
+ "message": "Account Details"
+ },
+ "accountName": {
+ "message": "Account Name"
+ },
+ "address": {
+ "message": "Address"
+ },
+ "addToken": {
+ "message": "Add Token"
+ },
+ "amount": {
+ "message": "Amount"
+ },
+ "amountPlusGas": {
+ "message": "Amount + Gas"
+ },
+ "appDescription": {
+ "message": "Ethereum Browser Extension",
+ "description": "The description of the application"
+ },
"appName": {
"message": "MetaMask",
"description": "The name of the application"
},
- "appDescription": {
- "message": "Ethereum Identity Management",
- "description": "The description of the application"
+ "attemptingConnect": {
+ "message": "Attempting to connect to blockchain."
+ },
+ "available": {
+ "message": "Available"
+ },
+ "back": {
+ "message": "Back"
+ },
+ "balance": {
+ "message": "Balance:"
+ },
+ "balanceIsInsufficientGas": {
+ "message": "Insufficient balance for current gas total"
+ },
+ "beta": {
+ "message": "BETA"
+ },
+ "betweenMinAndMax": {
+ "message": "must be greater than or equal to $1 and less than or equal to $2.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "borrowDharma": {
+ "message": "Borrow With Dharma (Beta)"
+ },
+ "buy": {
+ "message": "Buy"
+ },
+ "buyCoinbase": {
+ "message": "Buy on Coinbase"
+ },
+ "buyCoinbaseExplainer": {
+ "message": "Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin."
+ },
+ "cancel": {
+ "message": "Cancel"
+ },
+ "clickCopy": {
+ "message": "Click to Copy"
+ },
+ "confirm": {
+ "message": "Confirm"
+ },
+ "confirmContract": {
+ "message": "Confirm Contract"
+ },
+ "confirmPassword": {
+ "message": "Confirm Password"
+ },
+ "confirmTransaction": {
+ "message": "Confirm Transaction"
+ },
+ "continueToCoinbase": {
+ "message": "Continue to Coinbase"
+ },
+ "contractDeployment": {
+ "message": "Contract Deployment"
+ },
+ "conversionProgress": {
+ "message": "Conversion in progress"
+ },
+ "copiedButton": {
+ "message": "Copied"
+ },
+ "copiedClipboard": {
+ "message": "Copied to Clipboard"
+ },
+ "copiedExclamation": {
+ "message": "Copied!"
+ },
+ "copy": {
+ "message": "Copy"
+ },
+ "copyToClipboard": {
+ "message": "Copy to clipboard"
+ },
+ "copyButton": {
+ "message": " Copy "
+ },
+ "copyPrivateKey": {
+ "message": "This is your private key (click to copy)"
+ },
+ "create": {
+ "message": "Create"
+ },
+ "createAccount": {
+ "message": "Create Account"
+ },
+ "createDen": {
+ "message": "Create"
+ },
+ "crypto": {
+ "message": "Crypto",
+ "description": "Exchange type (cryptocurrencies)"
+ },
+ "customGas": {
+ "message": "Customize Gas"
+ },
+ "customize": {
+ "message": "Customize"
+ },
+ "customRPC": {
+ "message": "Custom RPC"
+ },
+ "defaultNetwork": {
+ "message": "The default network for Ether transactions is Main Net."
+ },
+ "denExplainer": {
+ "message": "Your DEN is your password-encrypted storage within MetaMask."
+ },
+ "deposit": {
+ "message": "Deposit"
+ },
+ "depositBTC": {
+ "message": "Deposit your BTC to the address below:"
+ },
+ "depositCoin": {
+ "message": "Deposit your $1 to the address below",
+ "description": "Tells the user what coin they have selected to deposit with shapeshift"
+ },
+ "depositEth": {
+ "message": "Deposit Eth"
+ },
+ "depositEther": {
+ "message": "Deposit Ether"
+ },
+ "depositFiat": {
+ "message": "Deposit with Fiat"
+ },
+ "depositFromAccount": {
+ "message": "Deposit from another account"
+ },
+ "depositShapeShift": {
+ "message": "Deposit with ShapeShift"
+ },
+ "depositShapeShiftExplainer": {
+ "message": "If you own other cryptocurrencies, you can trade and deposit Ether directly into your MetaMask wallet. No Account Needed."
+ },
+ "details": {
+ "message": "Details"
+ },
+ "directDeposit": {
+ "message": "Direct Deposit"
+ },
+ "directDepositEther": {
+ "message": "Directly Deposit Ether"
+ },
+ "directDepositEtherExplainer": {
+ "message": "If you already have some Ether, the quickest way to get Ether in your new wallet by direct deposit."
+ },
+ "done": {
+ "message": "Done"
+ },
+ "edit": {
+ "message": "Edit"
+ },
+ "editAccountName": {
+ "message": "Edit Account Name"
+ },
+ "encryptNewDen": {
+ "message": "Encrypt your new DEN"
+ },
+ "enterPassword": {
+ "message": "Enter password"
+ },
+ "etherscanView": {
+ "message": "View account on Etherscan"
+ },
+ "exchangeRate": {
+ "message": "Exchange Rate"
+ },
+ "exportPrivateKey": {
+ "message": "Export Private Key"
+ },
+ "exportPrivateKeyWarning": {
+ "message": "Export private keys at your own risk."
+ },
+ "failed": {
+ "message": "Failed"
+ },
+ "fiat": {
+ "message": "FIAT",
+ "description": "Exchange type"
+ },
+ "fileImportFail": {
+ "message": "File import not working? Click here!",
+ "description": "Helps user import their account from a JSON file"
+ },
+ "from": {
+ "message": "From"
+ },
+ "fromShapeShift": {
+ "message": "From ShapeShift"
+ },
+ "gas": {
+ "message": "Gas",
+ "description": "Short indication of gas cost"
+ },
+ "gasFee": {
+ "message": "Gas Fee"
+ },
+ "gasLimit": {
+ "message": "Gas Limit"
+ },
+ "gasLimitCalculation": {
+ "message": "We calculate the suggested gas limit based on network success rates."
+ },
+ "gasLimitRequired": {
+ "message": "Gas Limit Required"
+ },
+ "gasLimitTooLow": {
+ "message": "Gas limit must be at least 21000"
+ },
+ "gasPrice": {
+ "message": "Gas Price (GWEI)"
+ },
+ "gasPriceCalculation": {
+ "message": "We calculate the suggested gas prices based on network success rates."
+ },
+ "gasPriceRequired": {
+ "message": "Gas Price Required"
+ },
+ "getEther": {
+ "message": "Get Ether"
+ },
+ "getEtherFromFaucet": {
+ "message": "Get Ether from a faucet for the $1",
+ "description": "Displays network name for Ether faucet"
+ },
+ "greaterThanMin": {
+ "message": "must be greater than or equal to $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "here": {
+ "message": "here",
+ "description": "as in -click here- for more information (goes with troubleTokenBalances)"
+ },
+ "hide": {
+ "message": "Hide"
+ },
+ "hideToken": {
+ "message": "Hide Token"
+ },
+ "hideTokenPrompt": {
+ "message": "Hide Token?"
+ },
+ "howToDeposit": {
+ "message": "How would you like to deposit Ether?"
+ },
+ "import": {
+ "message": "Import",
+ "description": "Button to import an account from a selected file"
+ },
+ "importAccount": {
+ "message": "Import Account"
+ },
+ "importAnAccount": {
+ "message": "Import an account"
+ },
+ "importDen": {
+ "message": "Import Existing DEN"
+ },
+ "imported": {
+ "message": "Imported",
+ "description": "status showing that an account has been fully loaded into the keyring"
+ },
+ "infoHelp": {
+ "message": "Info & Help"
+ },
+ "invalidAddress": {
+ "message": "Invalid address"
+ },
+ "invalidGasParams": {
+ "message": "Invalid Gas Parameters"
+ },
+ "invalidInput": {
+ "message": "Invalid input."
+ },
+ "invalidRequest": {
+ "message": "Invalid Request"
+ },
+ "jsonFile": {
+ "message": "JSON File",
+ "description": "format for importing an account"
+ },
+ "kovan": {
+ "message": "Kovan Test Network"
+ },
+ "lessThanMax": {
+ "message": "must be less than or equal to $1.",
+ "description": "helper for inputting hex as decimal input"
+ },
+ "limit": {
+ "message": "Limit"
+ },
+ "loading": {
+ "message": "Loading..."
+ },
+ "loadingTokens": {
+ "message": "Loading Tokens..."
+ },
+ "localhost": {
+ "message": "Localhost 8545"
+ },
+ "logout": {
+ "message": "Log out"
+ },
+ "loose": {
+ "message": "Loose"
+ },
+ "mainnet": {
+ "message": "Main Ethereum Network"
+ },
+ "message": {
+ "message": "Message"
+ },
+ "min": {
+ "message": "Minimum"
+ },
+ "myAccounts": {
+ "message": "My Accounts"
+ },
+ "needEtherInWallet": {
+ "message": "To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet."
+ },
+ "needImportFile": {
+ "message": "You must select a file to import.",
+ "description": "User is important an account and needs to add a file to continue"
+ },
+ "needImportPassword": {
+ "message": "You must enter a password for the selected file.",
+ "description": "Password and file needed to import an account"
+ },
+ "networks": {
+ "message": "Networks"
+ },
+ "newAccount": {
+ "message": "New Account"
+ },
+ "newAccountNumberName": {
+ "message": "Account $1",
+ "description": "Default name of next account to be created on create account screen"
+ },
+ "newContract": {
+ "message": "New Contract"
+ },
+ "newPassword": {
+ "message": "New Password (min 8 chars)"
+ },
+ "newRecipient": {
+ "message": "New Recipient"
+ },
+ "next": {
+ "message": "Next"
+ },
+ "noAddressForName": {
+ "message": "No address has been set for this name."
+ },
+ "noDeposits": {
+ "message": "No deposits received"
+ },
+ "noTransactionHistory": {
+ "message": "No transaction history."
+ },
+ "noTransactions": {
+ "message": "No Transactions"
+ },
+ "notStarted": {
+ "message": "Not Started"
+ },
+ "oldUI": {
+ "message": "Old UI"
+ },
+ "oldUIMessage": {
+ "message": "You have returned to the old UI. You can switch back to the New UI through the option in the top right dropdown menu."
+ },
+ "or": {
+ "message": "or",
+ "description": "choice between creating or importing a new account"
+ },
+ "passwordMismatch": {
+ "message": "passwords don't match",
+ "description": "in password creation process, the two new password fields did not match"
+ },
+ "passwordShort": {
+ "message": "password not long enough",
+ "description": "in password creation process, the password is not long enough to be secure"
+ },
+ "pastePrivateKey": {
+ "message": "Paste your private key string here:",
+ "description": "For importing an account from a private key"
+ },
+ "pasteSeed": {
+ "message": "Paste your seed phrase here!"
+ },
+ "pleaseReviewTransaction": {
+ "message": "Please review your transaction."
+ },
+ "privateKey": {
+ "message": "Private Key",
+ "description": "select this type of file to use to import an account"
+ },
+ "privateKeyWarning": {
+ "message": "Warning: Never disclose this key. Anyone with your private keys can take steal any assets held in your account."
+ },
+ "privateNetwork": {
+ "message": "Private Network"
+ },
+ "qrCode": {
+ "message": "Show QR Code"
+ },
+ "readdToken": {
+ "message": "You can add this token back in the future by going go to “Add token” in your accounts options menu."
+ },
+ "readMore": {
+ "message": "Read more here."
+ },
+ "receive": {
+ "message": "Receive"
+ },
+ "recipientAddress": {
+ "message": "Recipient Address"
+ },
+ "refundAddress": {
+ "message": "Your Refund Address"
+ },
+ "rejected": {
+ "message": "Rejected"
+ },
+ "required": {
+ "message": "Required"
+ },
+ "retryWithMoreGas": {
+ "message": "Retry with a higher gas price here"
+ },
+ "revert": {
+ "message": "Revert"
+ },
+ "rinkeby": {
+ "message": "Rinkeby Test Network"
+ },
+ "ropsten": {
+ "message": "Ropsten Test Network"
+ },
+ "sampleAccountName": {
+ "message": "E.g. My new account",
+ "description": "Help user understand concept of adding a human-readable name to their account"
+ },
+ "save": {
+ "message": "Save"
+ },
+ "saveAsFile": {
+ "message": "Save as File",
+ "description": "Account export process"
+ },
+ "selectService": {
+ "message": "Select Service"
+ },
+ "send": {
+ "message": "Send"
+ },
+ "sendTokens": {
+ "message": "Send Tokens"
+ },
+ "sendTokensAnywhere": {
+ "message": "Send Tokens to anyone with an Ethereum account"
+ },
+ "settings": {
+ "message": "Settings"
+ },
+ "shapeshiftBuy": {
+ "message": "Buy with Shapeshift"
+ },
+ "showPrivateKeys": {
+ "message": "Show Private Keys"
+ },
+ "showQRCode": {
+ "message": "Show QR Code"
+ },
+ "sign": {
+ "message": "Sign"
+ },
+ "signMessage": {
+ "message": "Sign Message"
+ },
+ "signNotice": {
+ "message": "Signing this message can have \ndangerous side effects. Only sign messages from \nsites you fully trust with your entire account.\n This dangerous method will be removed in a future version. "
+ },
+ "sigRequest": {
+ "message": "Signature Request"
+ },
+ "sigRequested": {
+ "message": "Signature Requested"
+ },
+ "status": {
+ "message": "Status"
+ },
+ "submit": {
+ "message": "Submit"
+ },
+ "takesTooLong": {
+ "message": "Taking too long?"
+ },
+ "testFaucet": {
+ "message": "Test Faucet"
+ },
+ "to": {
+ "message": "To"
+ },
+ "toETHviaShapeShift": {
+ "message": "$1 to ETH via ShapeShift",
+ "description": "system will fill in deposit type in start of message"
+ },
+ "tokenBalance": {
+ "message": "Your Token Balance is:"
+ },
+ "total": {
+ "message": "Total"
+ },
+ "transactionMemo": {
+ "message": "Transaction memo (optional)"
+ },
+ "transactionNumber": {
+ "message": "Transaction Number"
+ },
+ "transfers": {
+ "message": "Transfers"
+ },
+ "troubleTokenBalances": {
+ "message": "We had trouble loading your token balances. You can view them ",
+ "description": "Followed by a link (here) to view token balances"
+ },
+ "typePassword": {
+ "message": "Type Your Password"
+ },
+ "uiWelcome": {
+ "message": "Welcome to the New UI (Beta)"
+ },
+ "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."
+ },
+ "unavailable": {
+ "message": "Unavailable"
+ },
+ "unknown": {
+ "message": "Unknown"
+ },
+ "unknownNetwork": {
+ "message": "Unknown Private Network"
+ },
+ "unknownNetworkId": {
+ "message": "Unknown network ID"
+ },
+ "usaOnly": {
+ "message": "USA only",
+ "description": "Using this exchange is limited to people inside the USA"
+ },
+ "usedByClients": {
+ "message": "Used by a variety of different clients"
+ },
+ "viewAccount": {
+ "message": "View Account"
+ },
+ "warning": {
+ "message": "Warning"
+ },
+ "whatsThis": {
+ "message": "What's this?"
+ },
+ "yourSigRequested": {
+ "message": "Your signature is being requested"
+ },
+ "youSign": {
+ "message": "You are signing"
}
}
diff --git a/app/manifest.json b/app/manifest.json
index 0c89c2b3e..6fcf6cd7c 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,10 +1,10 @@
{
- "name": "MetaMask",
- "short_name": "Metamask",
+ "name": "__MSG_appName__",
+ "short_name": "__MSG_appName__",
"version": "4.2.0",
"manifest_version": 2,
"author": "https://metamask.io",
- "description": "Ethereum Browser Extension",
+ "description": "__MSG_appDescription__",
"commands": {
"_execute_browser_action": {
"suggested_key": {
@@ -56,6 +56,7 @@
],
"permissions": [
"storage",
+ "unlimitedStorage",
"clipboardWrite",
"http://localhost:8545/",
"https://*.infura.io/"
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 601ae0372..ef5513ec7 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -1,9 +1,11 @@
const urlUtil = require('url')
const endOfStream = require('end-of-stream')
const pump = require('pump')
+const debounce = require('debounce-stream')
const log = require('loglevel')
const extension = require('extensionizer')
const LocalStorageStore = require('obs-store/lib/localStorage')
+const LocalStore = require('./lib/local-store')
const storeTransform = require('obs-store/lib/transform')
const asStream = require('obs-store/lib/asStream')
const ExtensionPlatform = require('./platforms/extension')
@@ -44,6 +46,8 @@ let openMetamaskTabsIDs = {}
// state persistence
const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY })
+const localStore = new LocalStore()
+let versionedData
// initialization flow
initialize().catch(log.error)
@@ -64,12 +68,23 @@ async function initialize () {
async function loadStateFromPersistence () {
// migrations
const migrator = new Migrator({ migrations })
+
// read from disk
- let versionedData = diskStore.getState() || migrator.generateInitialState(firstTimeState)
+ // first from preferred, async API:
+ versionedData = (await localStore.get()) ||
+ diskStore.getState() ||
+ migrator.generateInitialState(firstTimeState)
+
// migrate data
versionedData = await migrator.migrateData(versionedData)
+ if (!versionedData) {
+ throw new Error('MetaMask - migrator returned undefined')
+ }
+
// write to disk
+ if (localStore.isSupported) localStore.set(versionedData)
diskStore.putState(versionedData)
+
// return just the data
return versionedData.data
}
@@ -102,16 +117,30 @@ function setupController (initState) {
// setup state persistence
pump(
asStream(controller.store),
+ debounce(1000),
storeTransform(versionifyData),
- asStream(diskStore)
+ storeTransform(syncDataWithExtension),
+ asStream(diskStore),
+ (error) => {
+ log.error('pump hit error', error)
+ }
)
function versionifyData (state) {
- const versionedData = diskStore.getState()
versionedData.data = state
return versionedData
}
+ function syncDataWithExtension(state) {
+ if (localStore.isSupported) {
+ localStore.set(state)
+ .catch((err) => {
+ log.error('error setting state in local store:', err)
+ })
+ }
+ return state
+ }
+
//
// connect to other contexts
//
diff --git a/app/scripts/lib/local-store.js b/app/scripts/lib/local-store.js
new file mode 100644
index 000000000..781aea17e
--- /dev/null
+++ b/app/scripts/lib/local-store.js
@@ -0,0 +1,38 @@
+// We should not rely on local storage in an extension!
+// We should use this instead!
+// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/local
+
+const extension = require('extensionizer')
+const { promisify } = require('util').promisify
+
+module.exports = class ExtensionStore {
+ constructor() {
+ this.isSupported = !!(extension.storage.local)
+ if (!this.isSupported) {
+ log.error('Storage local API not available.')
+ }
+ const local = extension.storage.local
+ this._get = promisify(local.get).bind(local)
+ this._set = promisify(local.set).bind(local)
+ }
+
+ async get() {
+ if (!this.isSupported) return undefined
+ const result = await this._get()
+ // extension.storage.local always returns an obj
+ // if the object is empty, treat it as undefined
+ if (isEmpty(result)) {
+ return undefined
+ } else {
+ return result
+ }
+ }
+
+ async set(state) {
+ return this._set(state)
+ }
+}
+
+function isEmpty(obj) {
+ return Object.keys(obj).length === 0
+}
diff --git a/app/scripts/lib/tx-gas-utils.js b/app/scripts/lib/tx-gas-utils.js
index 6f6ff7852..0fa9dd8d4 100644
--- a/app/scripts/lib/tx-gas-utils.js
+++ b/app/scripts/lib/tx-gas-utils.js
@@ -4,7 +4,7 @@ const {
BnMultiplyByFraction,
bnToHex,
} = require('./util')
-const addHexPrefix = require('ethereumjs-util').addHexPrefix
+const { addHexPrefix, isValidAddress } = require('ethereumjs-util')
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
/*
@@ -113,12 +113,14 @@ module.exports = class TxGasUtil {
}
}
validateRecipient (txParams) {
- if (txParams.to === '0x') {
+ if (txParams.to === '0x' || txParams.to === null ) {
if (txParams.data) {
delete txParams.to
} else {
throw new Error('Invalid recipient address')
}
+ } else if ( txParams.to !== undefined && !isValidAddress(txParams.to) ) {
+ throw new Error('Invalid recipient address')
}
return txParams
}