aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/_locales/en/messages.json36
-rw-r--r--app/_locales/sl/messages.json8
-rw-r--r--app/_locales/zh_CN/messages.json383
-rw-r--r--app/images/check-icon.svg17
-rw-r--r--app/images/search.svg14
-rw-r--r--app/images/tokensearch.svg15
-rw-r--r--app/manifest.json6
-rw-r--r--app/scripts/background.js2
-rw-r--r--app/scripts/contentscript.js4
-rw-r--r--app/scripts/controllers/address-book.js27
-rw-r--r--app/scripts/controllers/network/enums.js26
-rw-r--r--app/scripts/controllers/network/network.js121
-rw-r--r--app/scripts/controllers/network/util.js36
-rw-r--r--app/scripts/controllers/preferences.js30
-rw-r--r--app/scripts/controllers/recent-blocks.js22
-rw-r--r--app/scripts/controllers/transactions/lib/tx-state-history-helper.js21
-rw-r--r--app/scripts/controllers/transactions/tx-state-manager.js4
-rw-r--r--app/scripts/first-time-state.js9
-rw-r--r--app/scripts/lib/createErrorMiddleware.js66
-rw-r--r--app/scripts/lib/get-first-preferred-lang-code.js11
-rw-r--r--app/scripts/lib/inpage-provider.js2
-rw-r--r--app/scripts/lib/setupRaven.js42
-rw-r--r--app/scripts/metamask-controller.js47
-rw-r--r--app/scripts/migrations/026.js47
-rw-r--r--app/scripts/migrations/index.js1
25 files changed, 719 insertions, 278 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index a40c2635c..4851508a3 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -23,6 +23,9 @@
"addTokens": {
"message": "Add Tokens"
},
+ "addAcquiredTokens": {
+ "message": "Add the tokens you've acquired using MetaMask"
+ },
"amount": {
"message": "Amount"
},
@@ -53,7 +56,7 @@
"message": "Back"
},
"balance": {
- "message": "Balance:"
+ "message": "Balance"
},
"balances": {
"message": "Token balance(s)"
@@ -393,9 +396,15 @@
"message": "Imported",
"description": "status showing that an account has been fully loaded into the keyring"
},
+ "importUsingSeed": {
+ "message": "Import using account seed phrase"
+ },
"infoHelp": {
"message": "Info & Help"
},
+ "initialTransactionConfirmed": {
+ "message": "Your initial transaction was confirmed by the network. Click OK to go back."
+ },
"insufficientFunds": {
"message": "Insufficient funds."
},
@@ -632,7 +641,7 @@
"message": "Reset Account"
},
"restoreFromSeed": {
- "message": "Restore from seed phrase"
+ "message": "Restore account?"
},
"restoreVault": {
"message": "Restore Vault"
@@ -670,6 +679,9 @@
"ropsten": {
"message": "Ropsten Test Network"
},
+ "rpc": {
+ "message": "Custom RPC"
+ },
"currentRpc": {
"message": "Current RPC"
},
@@ -695,10 +707,10 @@
"save": {
"message": "Save"
},
- "reprice_title": {
- "message": "Reprice Transaction"
+ "speedUpTitle": {
+ "message": "Speed Up Transaction"
},
- "reprice_subtitle": {
+ "speedUpSubtitle": {
"message": "Increase your gas price to attempt to overwrite and speed up your transaction"
},
"saveAsCsvFile": {
@@ -714,6 +726,9 @@
"search": {
"message": "Search"
},
+ "searchResults": {
+ "message": "Search Results"
+ },
"secretPhrase": {
"message": "Enter your secret twelve word phrase here to restore your vault."
},
@@ -721,7 +736,7 @@
"message": "New Password (min 8 chars)"
},
"seedPhraseReq": {
- "message": "seed phrases are 12 words long"
+ "message": "Seed phrases are 12 words long"
},
"select": {
"message": "Select"
@@ -829,6 +844,9 @@
"message": "$1 to ETH via ShapeShift",
"description": "system will fill in deposit type in start of message"
},
+ "token": {
+ "message": "Token"
+ },
"tokenAddress": {
"message": "Token Address"
},
@@ -896,6 +914,9 @@
"unknownNetworkId": {
"message": "Unknown network ID"
},
+ "unlockMessage": {
+ "message": "The decentralized web awaits"
+ },
"uriErrorMsg": {
"message": "URIs require the appropriate HTTP/HTTPS prefix."
},
@@ -924,6 +945,9 @@
"warning": {
"message": "Warning"
},
+ "welcomeBack": {
+ "message": "Welcome Back!"
+ },
"welcomeBeta": {
"message": "Welcome to MetaMask Beta"
},
diff --git a/app/_locales/sl/messages.json b/app/_locales/sl/messages.json
index b089f3476..25bd0bcbb 100644
--- a/app/_locales/sl/messages.json
+++ b/app/_locales/sl/messages.json
@@ -181,7 +181,7 @@
"message": "DEN je vaša šifrirana shramba v MetaMasku."
},
"deposit": {
- "message": "Vplačilo"
+ "message": "Vplačaj"
},
"depositBTC": {
"message": "Vplačajte vaš BTC na spodnji naslov:"
@@ -507,10 +507,10 @@
"message": "Ni se začelo"
},
"oldUI": {
- "message": "Starejši uporabniški vmesnik"
+ "message": "Star UI"
},
"oldUIMessage": {
- "message": "Vrnili ste se v starejši uporabniški vmesnik. V novega se lahko vrnete z možnostjo v spustnem meniju v zgornjem desnem kotu."
+ "message": "Vrnili ste se v star uporabniški vmesnik. V novega se lahko vrnete z možnostjo v spustnem meniju v zgornjem desnem kotu."
},
"or": {
"message": "ali",
@@ -759,7 +759,7 @@
"message": "Vpišite vaše geslo"
},
"uiWelcome": {
- "message": "Dobrodošli v novem uporabniškem vmesniku (Beta)"
+ "message": "Dobrodošli v nov UI (Beta)"
},
"uiWelcomeMessage": {
"message": "Zdaj uporabljate novi MetaMask uporabniški vmesnik. Razglejte se, preizkusite nove funkcije, kot so pošiljanje žetonov, in nas obvestite, če imate kakšne težave."
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index 203ab1923..241ea948d 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -14,9 +14,15 @@
"address": {
"message": "地址"
},
+ "addCustomToken": {
+ "message": "添加自定义代币"
+ },
"addToken": {
"message": "添加代币"
},
+ "addTokens": {
+ "message": "添加代币"
+ },
"amount": {
"message": "数量"
},
@@ -31,9 +37,15 @@
"message": "MetaMask",
"description": "The name of the application"
},
+ "approved": {
+ "message": "批准"
+ },
"attemptingConnect": {
"message": "正在尝试连接区块链。"
},
+ "attributions": {
+ "message": "来源"
+ },
"available": {
"message": "可用"
},
@@ -43,6 +55,9 @@
"balance": {
"message": "余额:"
},
+ "balances": {
+ "message": "代币余额"
+ },
"balanceIsInsufficientGas": {
"message": "当前余额不足以支付 Gas"
},
@@ -53,9 +68,15 @@
"message": "必须大于等于 $1 并且小于等于 $2 。",
"description": "helper for inputting hex as decimal input"
},
+ "blockiesIdenticon": {
+ "message": "使用区块Identicon"
+ },
"borrowDharma": {
"message": "Borrow With Dharma (Beta)"
},
+ "builtInCalifornia": {
+ "message": "MetaMask在加利福尼亚设计和制造。"
+ },
"buy": {
"message": "购买"
},
@@ -65,15 +86,27 @@
"buyCoinbaseExplainer": {
"message": "Coinbase 是世界上最流行的买卖比特币,以太币和莱特币的交易所。"
},
+ "ok": {
+ "message": "确认"
+ },
"cancel": {
"message": "取消"
},
+ "classicInterface": {
+ "message": "使用经典接口"
+ },
"clickCopy": {
"message": "点击复制"
},
+ "close": {
+ "message": "关闭"
+ },
"confirm": {
"message": "确认"
},
+ "confirmed": {
+ "message": "确认"
+ },
"confirmContract": {
"message": "确认合约"
},
@@ -83,6 +116,9 @@
"confirmTransaction": {
"message": "确认交易"
},
+ "continue": {
+ "message": "继续"
+ },
"continueToCoinbase": {
"message": "继续访问 Coinbase"
},
@@ -99,7 +135,10 @@
"message": "已复制到剪贴板"
},
"copiedExclamation": {
- "message": "已复制!"
+ "message": "已复制"
+ },
+ "copiedSafe": {
+ "message": "我已将它复制保存到某个安全的地方"
},
"copy": {
"message": "复制"
@@ -126,15 +165,30 @@
"message": "加密",
"description": "Exchange type (cryptocurrencies)"
},
+ "currentConversion": {
+ "message": "当前汇率"
+ },
+ "currentNetwork": {
+ "message": "当前网络"
+ },
"customGas": {
"message": "自定义 Gas"
},
+ "customToken": {
+ "message": "自定义代币"
+ },
"customize": {
"message": "自定义"
},
"customRPC": {
"message": "自定义 RPC"
},
+ "decimalsMustZerotoTen": {
+ "message": "小数位最小为0并且不超过36位."
+ },
+ "decimal": {
+ "message": "精确小数点"
+ },
"defaultNetwork": {
"message": "默认以太坊交易网络为主网。"
},
@@ -184,18 +238,39 @@
"done": {
"message": "完成"
},
+ "downloadStateLogs": {
+ "message": "下载日志"
+ },
+ "dropped": {
+ "message": "丢弃"
+ },
"edit": {
"message": "编辑"
},
"editAccountName": {
"message": "编辑账户名称"
},
+ "emailUs": {
+ "message": "联系我们"
+ },
"encryptNewDen": {
"message": "加密你的新 DEN"
},
"enterPassword": {
"message": "请输入密码"
},
+ "enterPasswordConfirm": {
+ "message": "请输入密码以确认"
+ },
+ "enterPasswordContinue": {
+ "message": "请输入密码以继续"
+ },
+ "passwordNotLongEnough": {
+ "message": "密码长度不足"
+ },
+ "passwordsDontMatch": {
+ "message": "密码不匹配"
+ },
"etherscanView": {
"message": "在 Etherscan 上查看账户"
},
@@ -219,9 +294,15 @@
"message": "文件导入失败? 点击这里!",
"description": "Helps user import their account from a JSON file"
},
+ "followTwitter": {
+ "message": "关注我们的Twitter"
+ },
"from": {
"message": "来自"
},
+ "fromToSame": {
+ "message": "发送和接受地址不能相同"
+ },
"fromShapeShift": {
"message": "来自 ShapeShift"
},
@@ -244,6 +325,9 @@
"gasLimitTooLow": {
"message": "Gas Limit 至少要 21000"
},
+ "generatingSeed": {
+ "message": "生成密钥中..."
+ },
"gasPrice": {
"message": "Gas Price (GWEI)"
},
@@ -253,6 +337,9 @@
"gasPriceRequired": {
"message": "Gas Price 必填"
},
+ "generatingTransaction": {
+ "message": "生成 交易"
+ },
"getEther": {
"message": "获取 Ether"
},
@@ -268,6 +355,9 @@
"message": "这里",
"description": "as in -click here- for more information (goes with troubleTokenBalances)"
},
+ "hereList": {
+ "message": "Here's a list!!!!"
+ },
"hide": {
"message": "隐藏"
},
@@ -280,6 +370,9 @@
"howToDeposit": {
"message": "你想怎样转入 Ether?"
},
+ "holdEther": {
+ "message": "它允许你保存ether和代币,并作为你使用Dapp的桥梁."
+ },
"import": {
"message": "导入",
"description": "Button to import an account from a selected file"
@@ -287,6 +380,9 @@
"importAccount": {
"message": "导入账户"
},
+ "importAccountMsg": {
+ "message":" Imported accounts will not be associated with your originally created MetaMask account seedphrase. Learn more about imported accounts "
+ },
"importAnAccount": {
"message": "导入一个账户"
},
@@ -294,46 +390,82 @@
"message": "导入存在的 DEN"
},
"imported": {
- "message": "已导入私钥",
+ "message": "已导入",
"description": "status showing that an account has been fully loaded into the keyring"
},
"infoHelp": {
"message": "信息 & 帮助"
},
+ "insufficientFunds": {
+ "message": "余额不足."
+ },
+ "insufficientTokens": {
+ "message": "代币余额不足."
+ },
"invalidAddress": {
- "message": "错误的地址"
+ "message": "无效地址"
+ },
+ "invalidAddressRecipient": {
+ "message": "收款地址不合法"
},
"invalidGasParams": {
- "message": "错误的 Gas 参数"
+ "message": "无效 Gas 参数"
},
"invalidInput": {
- "message": "错误的输入。"
+ "message": "无效输入."
},
"invalidRequest": {
"message": "无效请求"
},
+ "invalidRPC": {
+ "message": "无效 RPC URI"
+ },
+ "jsonFail": {
+ "message": "Something went wrong. Please make sure your JSON file is properly formatted."
+ },
"jsonFile": {
"message": "JSON 文件",
"description": "format for importing an account"
},
+ "keepTrackTokens": {
+ "message": "Keep track of the tokens you’ve bought with your MetaMask account."
+ },
"kovan": {
"message": "Kovan 测试网络"
},
+ "knowledgeDataBase": {
+ "message": "浏览我们的知识库"
+ },
+ "max": {
+ "message": "最大"
+ },
+ "learnMore": {
+ "message": "查看更多."
+ },
"lessThanMax": {
- "message": "必须小于等于 $1.",
+ "message": "必须小于或等于 $1.",
"description": "helper for inputting hex as decimal input"
},
+ "likeToAddTokens": {
+ "message": "你想添加这些代币吗?"
+ },
+ "links": {
+ "message": "链接"
+ },
"limit": {
- "message": "限定"
+ "message": "限制"
},
"loading": {
- "message": "加载..."
+ "message": "加载中..."
},
"loadingTokens": {
- "message": "加载代币..."
+ "message": "加载代币中..."
},
"localhost": {
- "message": "本地主机 8545"
+ "message": "Localhost 8545"
+ },
+ "login": {
+ "message": "登录"
},
"logout": {
"message": "登出"
@@ -341,17 +473,29 @@
"loose": {
"message": "疏松"
},
+ "loweCaseWords": {
+ "message": "助记词只有小写字符"
+ },
"mainnet": {
"message": "以太坊主网络"
},
"message": {
"message": "消息"
},
+ "metamaskDescription": {
+ "message": "MetaMask is a secure identity vault for Ethereum."
+ },
+ "metamaskSeedWords": {
+ "message": "MetaMask 助记词"
+ },
"min": {
"message": "最小"
},
"myAccounts": {
- "message": "我的账户"
+ "message": "My Accounts"
+ },
+ "mustSelectOne": {
+ "message": "至少选择一种代币."
},
"needEtherInWallet": {
"message": "使用 MetaMask 与 DAPP 交互,需要你的钱包里有 Ether。"
@@ -361,9 +505,12 @@
"description": "User is important an account and needs to add a file to continue"
},
"needImportPassword": {
- "message": "必须为已选择的文件输入密码。",
+ "message": "必须为已选择的文件输入密码。",
"description": "Password and file needed to import an account"
},
+ "negativeETH": {
+ "message": "Can not send negative amounts of ETH."
+ },
"networks": {
"message": "网络"
},
@@ -383,8 +530,11 @@
"newRecipient": {
"message": "新收款人"
},
+ "newRPC": {
+ "message": "新 RPC URL"
+ },
"next": {
- "message": "下一个"
+ "message": "下一步"
},
"noAddressForName": {
"message": "此 ENS 名字还没有指定地址。"
@@ -405,12 +555,18 @@
"message": "旧版界面"
},
"oldUIMessage": {
- "message": "你已经切换到旧版界面。 你可以通过右上方下拉菜单中的选项切换回新的用户界面。"
+ "message": "你已经切换到旧版界面。 你可以通过右上方下拉菜单中的选项切换回新的用户界面。"
},
"or": {
"message": "或",
"description": "choice between creating or importing a new account"
},
+ "password": {
+ "message": "密码"
+ },
+ "passwordCorrect": {
+ "message": "Please make sure your password is correct."
+ },
"passwordMismatch": {
"message": "密码不匹配",
"description": "in password creation process, the two new password fields did not match"
@@ -426,15 +582,24 @@
"pasteSeed": {
"message": "请粘贴你的助记词!"
},
+ "personalAddressDetected": {
+ "message": "检测到个人地址。请输入代币合约地址。"
+ },
"pleaseReviewTransaction": {
"message": "请检查你的交易。"
},
+ "popularTokens": {
+ "message": "常用代币"
+ },
+ "privacyMsg": {
+ "message": "隐私政策"
+ },
"privateKey": {
"message": "私钥",
"description": "select this type of file to use to import an account"
},
"privateKeyWarning": {
- "message": "注意:永远不要公开这个私钥。任何拥有你的私钥的人都可以窃取你帐户中的任何资产。"
+ "message": "注意:永远不要公开这个私钥。任何拥有你的私钥的人都可以窃取你帐户中的任何资产。"
},
"privateNetwork": {
"message": "私有网络"
@@ -443,11 +608,14 @@
"message": "显示二维码"
},
"readdToken": {
- "message": "之后你还可以通过帐户选项菜单中的“添加代币”来添加此代币。"
+ "message": "之后你还可以通过帐户选项菜单中的“添加代币”来添加此代币。"
},
"readMore": {
"message": "了解更多。"
},
+ "readMore2": {
+ "message": "了解更多。"
+ },
"receive": {
"message": "接收"
},
@@ -460,12 +628,39 @@
"rejected": {
"message": "拒绝"
},
+ "resetAccount": {
+ "message": "重设账户"
+ },
+ "restoreFromSeed": {
+ "message": "从助记词还原"
+ },
+ "restoreVault": {
+ "message": "还原保险柜"
+ },
"required": {
"message": "必填"
},
"retryWithMoreGas": {
"message": "使用更高的 Gas Price 重试"
},
+ "walletSeed": {
+ "message": "钱包助记词"
+ },
+ "revealSeedWords": {
+ "message": "显示助记词"
+ },
+ "revealSeedWordsTitle": {
+ "message": "助记词"
+ },
+ "revealSeedWordsDescription": {
+ "message": "如果您更换浏览器或计算机,则需要使用此助记词访问您的帐户。请将它们保存在安全秘密的地方。"
+ },
+ "revealSeedWordsWarningTitle": {
+ "message": "不要对任何人展示助记词!"
+ },
+ "revealSeedWordsWarning": {
+ "message": "助记词可以用来窃取您的所有帐户."
+ },
"revert": {
"message": "还原"
},
@@ -475,6 +670,24 @@
"ropsten": {
"message": "Ropsten 测试网络"
},
+ "currentRpc": {
+ "message": "当前 RPC"
+ },
+ "connectingToMainnet": {
+ "message": "正在连接到以太坊主网"
+ },
+ "connectingToRopsten": {
+ "message": "正在连接到Ropsten测试网络"
+ },
+ "connectingToKovan": {
+ "message": "正在连接到Kovan测试网络"
+ },
+ "connectingToRinkeby": {
+ "message": "正在连接到Rinkeby测试网络"
+ },
+ "connectingToUnknown": {
+ "message": "正在连接到未知网络"
+ },
"sampleAccountName": {
"message": "例如:我的账户",
"description": "Help user understand concept of adding a human-readable name to their account"
@@ -482,25 +695,70 @@
"save": {
"message": "保存"
},
+ "reprice_title": {
+ "message": "重新出价交易"
+ },
+ "reprice_subtitle": {
+ "message": "提高 GAS 价格尝试覆盖并加速交易"
+ },
+ "saveAsCsvFile": {
+ "message": "另存为CSV文件"
+ },
"saveAsFile": {
"message": "保存文件",
"description": "Account export process"
},
+ "saveSeedAsFile": {
+ "message": "保存助记词为文件"
+ },
+ "search": {
+ "message": "搜索"
+ },
+ "secretPhrase": {
+ "message": "输入12位助记词以恢复金库."
+ },
+ "newPassword8Chars": {
+ "message": "新密码(至少8位)"
+ },
+ "seedPhraseReq": {
+ "message": "助记词为12个单词"
+ },
+ "select": {
+ "message": "选择"
+ },
+ "selectCurrency": {
+ "message": "选择货币"
+ },
"selectService": {
"message": "选择服务"
},
+ "selectType": {
+ "message": "选择类型"
+ },
"send": {
"message": "发送"
},
+ "sendETH": {
+ "message": "发送 ETH"
+ },
"sendTokens": {
- "message": "发送代币"
+ "message": "发送 代币"
+ },
+ "onlySendToEtherAddress": {
+ "message": "只发送 ETH 给一个以太坊地址"
+ },
+ "searchTokens": {
+ "message": "搜索代币"
},
"sendTokensAnywhere": {
- "message": "发送代币给拥有以太坊账户的任何人"
+ "message": "将代币发送给拥有以太坊地址的任何人"
},
"settings": {
"message": "设置"
},
+ "info": {
+ "message": "信息"
+ },
"shapeshiftBuy": {
"message": "使用 Shapeshift 购买"
},
@@ -513,6 +771,9 @@
"sign": {
"message": "签名"
},
+ "signed": {
+ "message": "已签名"
+ },
"signMessage": {
"message": "签署消息"
},
@@ -525,15 +786,39 @@
"sigRequested": {
"message": "签名已请求"
},
+ "spaceBetween": {
+ "message": "单词之间只能有一个空格"
+ },
"status": {
"message": "状态"
},
+ "stateLogs": {
+ "message": "状态日志"
+ },
+ "stateLogsDescription": {
+ "message": "状态日志包含您的账户地址和已发送的交易。"
+ },
+ "stateLogError": {
+ "message": "检索状态日志时出错。"
+ },
"submit": {
"message": "提交"
},
+ "submitted": {
+ "message": "已提交"
+ },
+ "supportCenter": {
+ "message": "访问我们的支持中心"
+ },
+ "symbolBetweenZeroTen": {
+ "message": "符号应该有0-10个字符."
+ },
"takesTooLong": {
"message": "花费太长时间?"
},
+ "terms": {
+ "message": "使用条款"
+ },
"testFaucet": {
"message": "测试水管"
},
@@ -544,33 +829,60 @@
"message": "$1 ETH 通过 ShapeShift",
"description": "system will fill in deposit type in start of message"
},
+ "tokenAddress": {
+ "message": "代币地址"
+ },
+ "tokenAlreadyAdded": {
+ "message": "代币已经被添加."
+ },
"tokenBalance": {
"message": "代币余额:"
},
+ "tokenSelection": {
+ "message": "搜索代币或从我们的常用代币列表中进行选择"
+ },
+ "tokenSymbol": {
+ "message": "代币符号"
+ },
+ "tokenWarning1": {
+ "message": "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."
+ },
"total": {
"message": "总量"
},
+ "transactions": {
+ "message": "交易"
+ },
+ "transactionError": {
+ "message": "交易出错. 合约代码执行异常."
+ },
"transactionMemo": {
- "message": "交易备注 (可选)"
+ "message": "交易备注(可选)"
},
"transactionNumber": {
- "message": "交易号"
+ "message": "交易 number"
},
"transfers": {
- "message": "Transfers"
+ "message": "交易"
},
"troubleTokenBalances": {
- "message": "无法加载代币余额。你可以再这里查看 ",
+ "message": "我们无法加载您的代币余额。你可以查看它们",
"description": "Followed by a link (here) to view token balances"
},
+ "twelveWords": {
+ "message": "这12个单词是恢复MetaMask帐户的唯一方法。.\n将它们存放在安全和秘密的地方。."
+ },
"typePassword": {
- "message": "请输入密码"
+ "message": "输入你的密码"
},
"uiWelcome": {
"message": "欢迎使用新版界面 (Beta)"
},
"uiWelcomeMessage": {
- "message": "你现在正在使用新的 Metamask 界面。 尝试发送代币等新功能,有任何问题请告知我们。"
+ "message": "你现在正在使用新的 Metamask 界面。 尝试发送代币等新功能,有任何问题请告知我们。"
+ },
+ "unapproved": {
+ "message": "未批准"
},
"unavailable": {
"message": "不可用"
@@ -582,7 +894,10 @@
"message": "未知私有网络"
},
"unknownNetworkId": {
- "message": "未知网络 ID"
+ "message": "未知网络ID"
+ },
+ "uriErrorMsg": {
+ "message": "URIs require the appropriate HTTP/HTTPS prefix."
},
"usaOnly": {
"message": "只限于美国",
@@ -591,12 +906,27 @@
"usedByClients": {
"message": "可用于各种不同的客户端"
},
+ "useOldUI": {
+ "message": "使用旧版 UI"
+ },
+ "validFileImport": {
+ "message": "您必须选择一个有效的文件进行导入."
+ },
+ "vaultCreated": {
+ "message": "已创建保险库"
+ },
"viewAccount": {
"message": "查看账户"
},
+ "visitWebSite": {
+ "message": "访问我们的网站"
+ },
"warning": {
"message": "警告"
},
+ "welcomeBeta": {
+ "message": "欢迎使用 MetaMask 测试版"
+ },
"whatsThis": {
"message": "这是什么?"
},
@@ -605,5 +935,8 @@
},
"youSign": {
"message": "正在签名"
+ },
+ "yourPrivateSeedPhrase": {
+ "message": "你的私有助记词"
}
}
diff --git a/app/images/check-icon.svg b/app/images/check-icon.svg
new file mode 100644
index 000000000..cafa864e5
--- /dev/null
+++ b/app/images/check-icon.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: sketchtool 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
+ <title>76BCDB09-52B0-41CB-908F-12F9087A2F1B</title>
+ <desc>Created with sketchtool.</desc>
+ <defs></defs>
+ <g id="Confirm-TX-screen" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="confirmed-alert" transform="translate(-144.000000, -53.000000)" stroke="#61BA00" stroke-width="4">
+ <g id="Group-17-Copy" transform="translate(22.000000, 20.000000)">
+ <g id="check-icon" transform="translate(124.000000, 35.000000)">
+ <circle id="Oval-5" cx="48" cy="48" r="48"></circle>
+ <polyline id="Path-3" stroke-linecap="round" points="29.76 52.8 41.0023819 64.32 71.04 34.56"></polyline>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/app/images/search.svg b/app/images/search.svg
new file mode 100644
index 000000000..44fea12aa
--- /dev/null
+++ b/app/images/search.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="17px" height="17px" viewBox="0 0 17 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
+ <title>search</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Add-Tokens" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="Metamascara---add-from-token-list-Copy-3" transform="translate(-345.000000, -350.000000)" fill="#9B9B9B" fill-rule="nonzero">
+ <g id="search" transform="translate(345.000000, 350.000000)">
+ <path d="M2.01875,6.90625 C2.01875,4.25 4.25,2.01875 6.90625,2.01875 C9.5625,2.01875 11.6875,4.14375 11.6875,6.90625 C11.6875,9.5625 9.5625,11.6875 6.90625,11.6875 C4.14375,11.6875 2.01875,9.5625 2.01875,6.90625 Z M16.575,15.0875 L12.325,10.8375 C13.175,9.66875 13.6,8.2875 13.6,6.8 C13.70625,3.08125 10.625,0 6.90625,0 C3.08125,0 0,3.08125 0,6.90625 C0,10.73125 3.08125,13.8125 6.90625,13.8125 C8.18125,13.8125 9.45625,13.3875 10.4125,12.75 L14.6625,17 L16.575,15.0875 Z" id="Page-1"></path>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/app/images/tokensearch.svg b/app/images/tokensearch.svg
new file mode 100644
index 000000000..cd0b03bf2
--- /dev/null
+++ b/app/images/tokensearch.svg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="65px" height="58px" viewBox="0 0 65 58" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: sketchtool 50.2 (55047) - http://www.bohemiancoding.com/sketch -->
+ <title>7FDB75AD-BD4D-497C-B391-69EEB31A0561</title>
+ <desc>Created with sketchtool.</desc>
+ <defs></defs>
+ <g id="Add-Tokens" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="Add-tokens" transform="translate(-267.000000, -284.000000)" fill="#B8BAC1">
+ <g id="tokensearch" transform="translate(267.000000, 284.000000)">
+ <path d="M28.5322581,2.80645161 C42.4391613,2.80645161 54.1925806,9.22854839 54.2552581,16.8433871 C54.1925806,24.4591613 42.4391613,30.8821935 28.5322581,30.8821935 C14.6253548,30.8821935 2.87193548,24.4600968 2.80925806,16.8443226 C2.87193548,9.22854839 14.6253548,2.80645161 28.5322581,2.80645161 M28.5322581,36.7289677 C15.7432581,36.7289677 4.78125806,31.2975484 3.05154839,24.5012581 C7.70932258,29.9981613 17.2559355,33.6886452 28.5322581,33.6886452 C39.8085806,33.6886452 49.3551935,29.9981613 54.0129677,24.5012581 C52.2832581,31.2975484 41.3212581,36.7289677 28.5322581,36.7289677 M28.5322581,54.2692903 C15.7432581,54.2692903 4.78125806,48.837871 3.05154839,42.0415806 C7.70932258,47.5384839 17.2559355,51.2289677 28.5322581,51.2289677 C33.2237097,51.2289677 37.6083226,50.5844194 41.471871,49.4403226 C42.1379355,49.243871 42.5270968,48.5675161 42.4110968,47.8818065 C42.4045484,47.8453226 42.398,47.8079032 42.3923871,47.7704839 C42.2642258,46.9603548 41.439129,46.458 40.6533226,46.6946774 C37.0180323,47.792 32.8822581,48.4225161 28.5322581,48.4225161 C15.7432581,48.4225161 4.78125806,42.9910968 3.05154839,36.1948065 C7.70932258,41.6917097 17.2559355,45.3821935 28.5322581,45.3821935 C33.3649677,45.3821935 37.8730645,44.6983548 41.8217419,43.4906452 C42.2763871,43.3503226 42.6066129,42.976129 42.7422581,42.5196129 L42.7534839,42.4812581 C43.0752903,41.4082581 42.0733871,40.4063548 41.004129,40.7403226 C37.2846452,41.9040645 33.0225806,42.5757419 28.5322581,42.5757419 C15.7432581,42.5757419 4.78125806,37.1443226 3.05154839,30.3480323 C7.70932258,35.8449355 17.2559355,39.5354194 28.5322581,39.5354194 C39.8085806,39.5354194 49.3551935,35.8449355 54.0129677,30.3480323 L54.0129677,33.5492581 C54.0129677,34.3846452 54.6902581,35.0619355 55.5256452,35.0619355 C56.3610323,35.0619355 57.0383226,34.3902581 57.0392581,33.5558065 C57.0467419,26.4900968 57.0645161,16.9257097 57.0645161,16.905129 C57.0645161,16.8845484 57.0617097,16.8649032 57.0617097,16.8443226 C57.0617097,16.8237419 57.0645161,16.8031613 57.0645161,16.7825806 L57.0598387,16.7825806 C56.9513226,7.36225806 44.4616774,0 28.5322581,0 C12.6028387,0 0.113193548,7.36225806 0.00467741935,16.7825806 L0,16.7825806 C0,16.8031613 0.00280645161,16.8237419 0.00280645161,16.8443226 C0.00280645161,16.8649032 0,16.8845484 0,16.905129 C0,16.9322581 0.00467741935,19.3420645 0.0102903226,22.6293548 L0,22.6293548 C0,22.7154194 0.00841935484,22.7996129 0.0102903226,22.8838065 C0.0140322581,24.5957419 0.0177741935,26.5247097 0.0196451613,28.476129 L0,28.476129 C0,28.650129 0.0130967742,28.8222581 0.0205806452,28.9953226 C0.0243225806,30.828871 0.0280645161,32.6586774 0.0308709677,34.3229032 L0,34.3229032 C0,34.5857742 0.0140322581,34.8467742 0.0318064516,35.1059032 C0.036483871,37.3108387 0.0392903226,39.1406452 0.0411612903,40.1696774 L0,40.1696774 C0,40.4905484 0.0177741935,40.8086129 0.0458387097,41.123871 L0.0495806452,41.2033871 C0.0645483871,41.3699032 0.0935483871,41.5345484 0.116935484,41.700129 C0.130032258,41.7861935 0.137516129,41.8731935 0.152483871,41.9583226 C0.183354839,42.1416774 0.225451613,42.3240968 0.266612903,42.5055806 C0.29,42.6103548 0.308709677,42.7160645 0.334903226,42.8199032 C0.358290323,42.9078387 0.387290323,42.9939032 0.411612903,43.0818387 C2.00006452,48.7134516 8.12841935,53.3160323 16.5777097,55.5705484 C16.6010968,55.5770968 16.6254194,55.5836452 16.6488065,55.5892581 C16.9350645,55.6650323 17.2213226,55.739871 17.5122581,55.8109677 C20.9099355,56.6538387 24.6322258,57.1215806 28.5322581,57.1215806 C32.4322903,57.1215806 36.1545806,56.6538387 39.5522581,55.8109677 C39.8431935,55.739871 40.1294516,55.6650323 40.4157097,55.5892581 C40.4390968,55.5836452 40.4634194,55.5770968 40.4868065,55.5705484 C41.5766452,55.2796129 42.6253226,54.9475161 43.6319032,54.579871 C44.4682258,54.2739677 44.7675806,53.2627097 44.2652258,52.5274194 C44.2437097,52.4956129 44.2212581,52.462871 44.1997419,52.430129 C43.8423871,51.8950323 43.1688387,51.6873548 42.5645161,51.9090645 C38.4998387,53.3955484 33.6624516,54.2692903 28.5322581,54.2692903" id="Fill-1"></path>
+ <path d="M64.3227484,54.3991355 L60.4535871,50.5299742 C61.4526839,49.1566839 61.9522323,47.5345548 61.9522323,45.7880065 C62.1009742,40.5661355 56.8996839,36.4144581 51.4654581,38.2367806 C48.6131677,39.1928452 46.4821355,41.7401677 46.0611677,44.7187484 C45.3530065,49.7460387 49.205329,54.0249419 54.0894903,54.0249419 C55.5872,54.0249419 57.0849097,53.5244581 58.2074903,52.7770065 L62.0766516,56.6452323 C62.6968774,57.2654581 63.7025226,57.2654581 64.3227484,56.6452323 C64.9429742,56.0250065 64.9429742,55.0193613 64.3227484,54.3991355 M48.3484258,45.9124258 C48.3484258,42.7925871 50.9696516,40.1713613 54.0894903,40.1713613 C57.209329,40.1713613 59.7052,42.6681677 59.7052,45.9124258 C59.7052,49.0332 57.209329,51.529071 54.0894903,51.529071 C50.8452323,51.529071 48.3484258,49.0332 48.3484258,45.9124258" id="Fill-3"></path>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/app/manifest.json b/app/manifest.json
index 141026d10..52ce8cc0a 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "4.6.1",
+ "version": "4.7.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
@@ -68,6 +68,8 @@
"matches": [
"https://metamask.io/*"
],
- "ids": ["*"]
+ "ids": [
+ "*"
+ ]
}
} \ No newline at end of file
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 37e2b68fc..56e190f97 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -323,7 +323,7 @@ function setupController (initState, initLangCode) {
/**
* A runtime.Port object, as provided by the browser:
- * @link https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port
+ * @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/Port
* @typedef Port
* @type Object
*/
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index dbf1c6d4c..555902ddf 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -166,7 +166,7 @@ function documentElementCheck () {
/**
* Checks if the current domain is blacklisted
- *
+ *
* @returns {boolean} {@code true} if the current domain is blacklisted
*/
function blacklistedDomainCheck () {
@@ -174,6 +174,8 @@ function blacklistedDomainCheck () {
'uscourts.gov',
'dropbox.com',
'webbyawards.com',
+ 'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html',
+ 'adyen.com',
]
var currentUrl = window.location.href
var currentRegex
diff --git a/app/scripts/controllers/address-book.js b/app/scripts/controllers/address-book.js
index c91e6b2e4..4697e074c 100644
--- a/app/scripts/controllers/address-book.js
+++ b/app/scripts/controllers/address-book.js
@@ -13,19 +13,17 @@ class AddressBookController {
* @param {object} opts Overrides the defaults for the initial state of this.store
* @property {array} opts.initState initializes the the state of the AddressBookController. Can contain an
* addressBook property to initialize the addressBook array
- * @param {KeyringController} keyringController (Soon to be deprecated) The keyringController used in the current
- * MetamaskController. Contains the identities used in this AddressBookController.
+ * @property {object} opts.preferencesStore the {@code PreferencesController} store
* @property {object} store The the store of the current users address book
* @property {array} store.addressBook An array of addresses and nicknames. These are set by the user when sending
* to a new address.
*
*/
- constructor (opts = {}, keyringController) {
- const initState = extend({
+ constructor ({initState, preferencesStore}) {
+ this.store = new ObservableStore(extend({
addressBook: [],
- }, opts.initState)
- this.store = new ObservableStore(initState)
- this.keyringController = keyringController
+ }, initState))
+ this._preferencesStore = preferencesStore
}
//
@@ -62,7 +60,7 @@ class AddressBookController {
*/
_addToAddressBook (address, name) {
const addressBook = this._getAddressBook()
- const identities = this._getIdentities()
+ const {identities} = this._preferencesStore.getState()
const addressBookIndex = addressBook.findIndex((element) => { return element.address.toLowerCase() === address.toLowerCase() || element.name === name })
const identitiesIndex = Object.keys(identities).findIndex((element) => { return element.toLowerCase() === address.toLowerCase() })
@@ -95,19 +93,6 @@ class AddressBookController {
_getAddressBook () {
return this.store.getState().addressBook
}
-
- /**
- * Retrieves identities from the keyring controller in order to avoid
- * duplication
- *
- * @deprecated
- * @returns {array} Returns the identies array from the keyringContoller's state
- *
- */
- _getIdentities () {
- return this.keyringController.memStore.getState().identities
- }
-
}
module.exports = AddressBookController
diff --git a/app/scripts/controllers/network/enums.js b/app/scripts/controllers/network/enums.js
index 4f29e301b..9da7f309c 100644
--- a/app/scripts/controllers/network/enums.js
+++ b/app/scripts/controllers/network/enums.js
@@ -13,20 +13,6 @@ const RINKEBY_DISPLAY_NAME = 'Rinkeby'
const KOVAN_DISPLAY_NAME = 'Kovan'
const MAINNET_DISPLAY_NAME = 'Main Ethereum Network'
-const MAINNET_RPC_URL = 'https://mainnet.infura.io/metamask'
-const ROPSTEN_RPC_URL = 'https://ropsten.infura.io/metamask'
-const KOVAN_RPC_URL = 'https://kovan.infura.io/metamask'
-const RINKEBY_RPC_URL = 'https://rinkeby.infura.io/metamask'
-const LOCALHOST_RPC_URL = 'http://localhost:8545'
-
-const MAINNET_RPC_URL_BETA = 'https://mainnet.infura.io/metamask2'
-const ROPSTEN_RPC_URL_BETA = 'https://ropsten.infura.io/metamask2'
-const KOVAN_RPC_URL_BETA = 'https://kovan.infura.io/metamask2'
-const RINKEBY_RPC_URL_BETA = 'https://rinkeby.infura.io/metamask2'
-
-const DEFAULT_NETWORK = 'rinkeby'
-const OLD_UI_NETWORK_TYPE = 'network'
-const BETA_UI_NETWORK_TYPE = 'networkBeta'
module.exports = {
ROPSTEN,
@@ -41,16 +27,4 @@ module.exports = {
RINKEBY_DISPLAY_NAME,
KOVAN_DISPLAY_NAME,
MAINNET_DISPLAY_NAME,
- MAINNET_RPC_URL,
- ROPSTEN_RPC_URL,
- KOVAN_RPC_URL,
- RINKEBY_RPC_URL,
- LOCALHOST_RPC_URL,
- MAINNET_RPC_URL_BETA,
- ROPSTEN_RPC_URL_BETA,
- KOVAN_RPC_URL_BETA,
- RINKEBY_RPC_URL_BETA,
- DEFAULT_NETWORK,
- OLD_UI_NETWORK_TYPE,
- BETA_UI_NETWORK_TYPE,
}
diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js
index 2f5b81cd2..93fde7c57 100644
--- a/app/scripts/controllers/network/network.js
+++ b/app/scripts/controllers/network/network.js
@@ -14,52 +14,40 @@ const {
RINKEBY,
KOVAN,
MAINNET,
- OLD_UI_NETWORK_TYPE,
- DEFAULT_NETWORK,
+ LOCALHOST,
} = require('./enums')
-const { getNetworkEndpoints } = require('./util')
+const LOCALHOST_RPC_URL = 'http://localhost:8545'
const INFURA_PROVIDER_TYPES = [ROPSTEN, RINKEBY, KOVAN, MAINNET]
+const env = process.env.METAMASK_ENV
+const METAMASK_DEBUG = process.env.METAMASK_DEBUG
+const testMode = (METAMASK_DEBUG || env === 'test')
+
+const defaultProviderConfig = {
+ type: testMode ? RINKEBY : MAINNET,
+}
+
module.exports = class NetworkController extends EventEmitter {
- constructor (config) {
+ constructor (opts = {}) {
super()
- this._networkEndpointVersion = OLD_UI_NETWORK_TYPE
- this._networkEndpoints = getNetworkEndpoints(OLD_UI_NETWORK_TYPE)
- this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
-
- config.provider.rpcTarget = this.getRpcAddressForType(config.provider.type, config.provider)
+ // parse options
+ const providerConfig = opts.provider || defaultProviderConfig
+ // create stores
+ this.providerStore = new ObservableStore(providerConfig)
this.networkStore = new ObservableStore('loading')
- this.providerStore = new ObservableStore(config.provider)
this.store = new ComposedStore({ provider: this.providerStore, network: this.networkStore })
+ // create event emitter proxy
this._proxy = createEventEmitterProxy()
this.on('networkDidChange', this.lookupNetwork)
}
- async setNetworkEndpoints (version) {
- if (version === this._networkEndpointVersion) {
- return
- }
-
- this._networkEndpointVersion = version
- this._networkEndpoints = getNetworkEndpoints(version)
- this._defaultRpc = this._networkEndpoints[DEFAULT_NETWORK]
- const { type } = this.getProviderConfig()
-
- return this.setProviderType(type, true)
- }
-
initializeProvider (_providerParams) {
this._baseProviderParams = _providerParams
const { type, rpcTarget } = this.providerStore.getState()
- // map rpcTarget to rpcUrl
- const opts = {
- type,
- rpcUrl: rpcTarget,
- }
- this._configureProvider(opts)
+ this._configureProvider({ type, rpcTarget })
this._proxy.on('block', this._logBlock.bind(this))
this._proxy.on('error', this.verifyNetwork.bind(this))
this.ethQuery = new EthQuery(this._proxy)
@@ -96,45 +84,27 @@ module.exports = class NetworkController extends EventEmitter {
})
}
- setRpcTarget (rpcUrl) {
- this.providerStore.updateState({
+ setRpcTarget (rpcTarget) {
+ const providerConfig = {
type: 'rpc',
- rpcTarget: rpcUrl,
- })
- this._switchNetwork({ rpcUrl })
- }
-
- getCurrentRpcAddress () {
- const provider = this.getProviderConfig()
- if (!provider) return null
- return this.getRpcAddressForType(provider.type)
- }
-
- async setProviderType (type, forceUpdate = false) {
- assert(type !== 'rpc', `NetworkController.setProviderType - cannot connect by type "rpc"`)
- // skip if type already matches
- if (type === this.getProviderConfig().type && !forceUpdate) {
- return
+ rpcTarget,
}
+ this.providerStore.updateState(providerConfig)
+ this._switchNetwork(providerConfig)
+ }
- const rpcTarget = this.getRpcAddressForType(type)
- assert(rpcTarget, `NetworkController - unknown rpc address for type "${type}"`)
- this.providerStore.updateState({ type, rpcTarget })
- this._switchNetwork({ type })
+ async setProviderType (type) {
+ assert.notEqual(type, 'rpc', `NetworkController - cannot call "setProviderType" with type 'rpc'. use "setRpcTarget"`)
+ assert(INFURA_PROVIDER_TYPES.includes(type) || type === LOCALHOST, `NetworkController - Unknown rpc type "${type}"`)
+ const providerConfig = { type }
+ this.providerStore.updateState(providerConfig)
+ this._switchNetwork(providerConfig)
}
getProviderConfig () {
return this.providerStore.getState()
}
- getRpcAddressForType (type, provider = this.getProviderConfig()) {
- if (this._networkEndpoints[type]) {
- return this._networkEndpoints[type]
- }
-
- return provider && provider.rpcTarget ? provider.rpcTarget : this._defaultRpc
- }
-
//
// Private
//
@@ -146,32 +116,27 @@ module.exports = class NetworkController extends EventEmitter {
}
_configureProvider (opts) {
- // type-based rpc endpoints
- const { type } = opts
- if (type) {
- // type-based infura rpc endpoints
- const isInfura = INFURA_PROVIDER_TYPES.includes(type)
- opts.rpcUrl = this.getRpcAddressForType(type)
- if (isInfura) {
- this._configureInfuraProvider(opts)
- // other type-based rpc endpoints
- } else {
- this._configureStandardProvider(opts)
- }
+ const { type, rpcTarget } = opts
+ // infura type-based endpoints
+ const isInfura = INFURA_PROVIDER_TYPES.includes(type)
+ if (isInfura) {
+ this._configureInfuraProvider(opts)
+ // other type-based rpc endpoints
+ } else if (type === LOCALHOST) {
+ this._configureStandardProvider({ rpcUrl: LOCALHOST_RPC_URL })
// url-based rpc endpoints
+ } else if (type === 'rpc'){
+ this._configureStandardProvider({ rpcUrl: rpcTarget })
} else {
- this._configureStandardProvider(opts)
+ throw new Error(`NetworkController - _configureProvider - unknown type "${type}"`)
}
}
- _configureInfuraProvider (opts) {
- log.info('_configureInfuraProvider', opts)
- const infuraProvider = createInfuraProvider({
- network: opts.type,
- })
+ _configureInfuraProvider ({ type }) {
+ log.info('_configureInfuraProvider', type)
+ const infuraProvider = createInfuraProvider({ network: type })
const infuraSubprovider = new SubproviderFromProvider(infuraProvider)
const providerParams = extend(this._baseProviderParams, {
- rpcUrl: opts.rpcUrl,
engineParams: {
pollingInterval: 8000,
blockTrackerProvider: infuraProvider,
diff --git a/app/scripts/controllers/network/util.js b/app/scripts/controllers/network/util.js
index 4f38ccda4..261dae721 100644
--- a/app/scripts/controllers/network/util.js
+++ b/app/scripts/controllers/network/util.js
@@ -3,7 +3,6 @@ const {
RINKEBY,
KOVAN,
MAINNET,
- LOCALHOST,
ROPSTEN_CODE,
RINKEYBY_CODE,
KOVAN_CODE,
@@ -11,17 +10,6 @@ const {
RINKEBY_DISPLAY_NAME,
KOVAN_DISPLAY_NAME,
MAINNET_DISPLAY_NAME,
- MAINNET_RPC_URL,
- ROPSTEN_RPC_URL,
- KOVAN_RPC_URL,
- RINKEBY_RPC_URL,
- LOCALHOST_RPC_URL,
- MAINNET_RPC_URL_BETA,
- ROPSTEN_RPC_URL_BETA,
- KOVAN_RPC_URL_BETA,
- RINKEBY_RPC_URL_BETA,
- OLD_UI_NETWORK_TYPE,
- BETA_UI_NETWORK_TYPE,
} = require('./enums')
const networkToNameMap = {
@@ -34,32 +22,8 @@ const networkToNameMap = {
[KOVAN_CODE]: KOVAN_DISPLAY_NAME,
}
-const networkEndpointsMap = {
- [OLD_UI_NETWORK_TYPE]: {
- [LOCALHOST]: LOCALHOST_RPC_URL,
- [MAINNET]: MAINNET_RPC_URL,
- [ROPSTEN]: ROPSTEN_RPC_URL,
- [KOVAN]: KOVAN_RPC_URL,
- [RINKEBY]: RINKEBY_RPC_URL,
- },
- [BETA_UI_NETWORK_TYPE]: {
- [LOCALHOST]: LOCALHOST_RPC_URL,
- [MAINNET]: MAINNET_RPC_URL_BETA,
- [ROPSTEN]: ROPSTEN_RPC_URL_BETA,
- [KOVAN]: KOVAN_RPC_URL_BETA,
- [RINKEBY]: RINKEBY_RPC_URL_BETA,
- },
-}
-
const getNetworkDisplayName = key => networkToNameMap[key]
-const getNetworkEndpoints = (networkType = OLD_UI_NETWORK_TYPE) => {
- return {
- ...networkEndpointsMap[networkType],
- }
-}
-
module.exports = {
getNetworkDisplayName,
- getNetworkEndpoints,
}
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js
index 1d3308d36..a4ff1207e 100644
--- a/app/scripts/controllers/preferences.js
+++ b/app/scripts/controllers/preferences.js
@@ -27,6 +27,7 @@ class PreferencesController {
useBlockie: false,
featureFlags: {},
currentLocale: opts.initLangCode,
+ identities: {},
}, opts.initState)
this.store = new ObservableStore(initState)
}
@@ -62,6 +63,16 @@ class PreferencesController {
this.store.updateState({ currentLocale: key })
}
+ setAddresses (addresses) {
+ const oldIdentities = this.store.getState().identities
+ const identities = addresses.reduce((ids, address, index) => {
+ const oldId = oldIdentities[address] || {}
+ ids[address] = {name: `Account ${index + 1}`, address, ...oldId}
+ return ids
+ }, {})
+ this.store.updateState({ identities })
+ }
+
/**
* Setter for the `selectedAddress` property
*
@@ -156,6 +167,21 @@ class PreferencesController {
}
/**
+ * Sets a custom label for an account
+ * @param {string} account the account to set a label for
+ * @param {string} label the custom label for the account
+ * @return {Promise<string>}
+ */
+ setAccountLabel (account, label) {
+ const address = normalizeAddress(account)
+ const {identities} = this.store.getState()
+ identities[address] = identities[address] || {}
+ identities[address].name = label
+ this.store.updateState({ identities })
+ return Promise.resolve(label)
+ }
+
+ /**
* Gets an updated rpc list from this.addToFrequentRpcList() and sets the `frequentRpcList` to this update list.
*
* @param {string} _url The the new rpc url to add to the updated list
@@ -189,8 +215,8 @@ class PreferencesController {
* The returned list will have a max length of 2. If the _url currently exists it the list, it will be moved to the
* end of the list. The current list is modified and returned as a promise.
*
- * @param {string} _url The rpc url to add to the frequentRpcList.
- * @returns {Promise<array>} The updated frequentRpcList.
+ * @param {string} _url The rpc url to add to the frequentRpcList.
+ * @returns {Promise<array>} The updated frequentRpcList.
*
*/
addToFrequentRpcList (_url) {
diff --git a/app/scripts/controllers/recent-blocks.js b/app/scripts/controllers/recent-blocks.js
index 1377c1ba9..033ef1d7e 100644
--- a/app/scripts/controllers/recent-blocks.js
+++ b/app/scripts/controllers/recent-blocks.js
@@ -119,29 +119,21 @@ class RecentBlocksController {
*/
async backfill() {
this.blockTracker.once('block', async (block) => {
- let blockNum = block.number
- let recentBlocks
- let state = this.store.getState()
- recentBlocks = state.recentBlocks
-
- while (recentBlocks.length < this.historyLength) {
+ const currentBlockNumber = Number.parseInt(block.number, 16)
+ const blocksToFetch = Math.min(currentBlockNumber, this.historyLength)
+ const prevBlockNumber = currentBlockNumber - 1
+ const targetBlockNumbers = Array(blocksToFetch).fill().map((_, index) => prevBlockNumber - index)
+ await Promise.all(targetBlockNumbers.map(async (targetBlockNumber) => {
try {
- let blockNumBn = new BN(blockNum.substr(2), 16)
- const newNum = blockNumBn.subn(1).toString(10)
- const newBlock = await this.getBlockByNumber(newNum)
+ const newBlock = await this.getBlockByNumber(targetBlockNumber)
if (newBlock) {
this.backfillBlock(newBlock)
- blockNum = newBlock.number
}
-
- state = this.store.getState()
- recentBlocks = state.recentBlocks
} catch (e) {
log.error(e)
}
- await this.wait()
- }
+ }))
})
}
diff --git a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js
index 59a4b562c..4562568e9 100644
--- a/app/scripts/controllers/transactions/lib/tx-state-history-helper.js
+++ b/app/scripts/controllers/transactions/lib/tx-state-history-helper.js
@@ -25,26 +25,31 @@ function migrateFromSnapshotsToDiffs (longHistory) {
}
/**
- generates an array of history objects sense the previous state.
- The object has the keys opp(the operation preformed),
- path(the key and if a nested object then each key will be seperated with a `/`)
- value
- with the first entry having the note
+ Generates an array of history objects sense the previous state.
+ The object has the keys
+ op (the operation performed),
+ path (the key and if a nested object then each key will be seperated with a `/`)
+ value
+ with the first entry having the note and a timestamp when the change took place
@param previousState {object} - the previous state of the object
@param newState {object} - the update object
@param note {string} - a optional note for the state change
- @reurns {array}
+ @returns {array}
*/
function generateHistoryEntry (previousState, newState, note) {
const entry = jsonDiffer.compare(previousState, newState)
// Add a note to the first op, since it breaks if we append it to the entry
- if (note && entry[0]) entry[0].note = note
+ if (entry[0]) {
+ if (note) entry[0].note = note
+
+ entry[0].timestamp = Date.now()
+ }
return entry
}
/**
Recovers previous txMeta state obj
- @return {object}
+ @returns {object}
*/
function replayHistory (_shortHistory) {
const shortHistory = clone(_shortHistory)
diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js
index f05c7d095..0aae4774b 100644
--- a/app/scripts/controllers/transactions/tx-state-manager.js
+++ b/app/scripts/controllers/transactions/tx-state-manager.js
@@ -159,7 +159,7 @@ class TransactionStateManager extends EventEmitter {
/**
updates the txMeta in the list and adds a history entry
@param txMeta {Object} - the txMeta to update
- @param [note] {string} - a not about the update for history
+ @param [note] {string} - a note about the update for history
*/
updateTx (txMeta, note) {
// validate txParams
@@ -263,7 +263,7 @@ class TransactionStateManager extends EventEmitter {
*/
getTxsByMetaData (key, value, txList = this.getTxList()) {
return txList.filter((txMeta) => {
- if (txMeta.txParams[key]) {
+ if (key in txMeta.txParams) {
return txMeta.txParams[key] === value
} else {
return txMeta[key] === value
diff --git a/app/scripts/first-time-state.js b/app/scripts/first-time-state.js
index c49d89288..13119bc04 100644
--- a/app/scripts/first-time-state.js
+++ b/app/scripts/first-time-state.js
@@ -1,7 +1,3 @@
-// test and development environment variables
-const env = process.env.METAMASK_ENV
-const METAMASK_DEBUG = process.env.METAMASK_DEBUG
-const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums')
/**
* @typedef {Object} FirstTimeState
@@ -14,11 +10,6 @@ const { DEFAULT_NETWORK, MAINNET } = require('./controllers/network/enums')
*/
const initialState = {
config: {},
- NetworkController: {
- provider: {
- type: (METAMASK_DEBUG || env === 'test') ? DEFAULT_NETWORK : MAINNET,
- },
- },
}
module.exports = initialState
diff --git a/app/scripts/lib/createErrorMiddleware.js b/app/scripts/lib/createErrorMiddleware.js
new file mode 100644
index 000000000..baed99e45
--- /dev/null
+++ b/app/scripts/lib/createErrorMiddleware.js
@@ -0,0 +1,66 @@
+const log = require('loglevel')
+
+/**
+ * JSON-RPC error object
+ *
+ * @typedef {Object} RpcError
+ * @property {number} code - Indicates the error type that occurred
+ * @property {Object} [data] - Contains additional information about the error
+ * @property {string} [message] - Short description of the error
+ */
+
+/**
+ * Middleware configuration object
+ *
+ * @typedef {Object} MiddlewareConfig
+ * @property {boolean} [override] - Use RPC_ERRORS message in place of provider message
+ */
+
+/**
+ * Map of standard and non-standard RPC error codes to messages
+ */
+const RPC_ERRORS = {
+ 1: 'An unauthorized action was attempted.',
+ 2: 'A disallowed action was attempted.',
+ 3: 'An execution error occurred.',
+ [-32600]: 'The JSON sent is not a valid Request object.',
+ [-32601]: 'The method does not exist / is not available.',
+ [-32602]: 'Invalid method parameter(s).',
+ [-32603]: 'Internal JSON-RPC error.',
+ [-32700]: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.',
+ internal: 'Internal server error.',
+ unknown: 'Unknown JSON-RPC error.',
+}
+
+/**
+ * Modifies a JSON-RPC error object in-place to add a human-readable message,
+ * optionally overriding any provider-supplied message
+ *
+ * @param {RpcError} error - JSON-RPC error object
+ * @param {boolean} override - Use RPC_ERRORS message in place of provider message
+ */
+function sanitizeRPCError (error, override) {
+ if (error.message && !override) { return error }
+ const message = error.code > -31099 && error.code < -32100 ? RPC_ERRORS.internal : RPC_ERRORS[error.code]
+ error.message = message || RPC_ERRORS.unknown
+}
+
+/**
+ * json-rpc-engine middleware that both logs standard and non-standard error
+ * messages and ends middleware stack traversal if an error is encountered
+ *
+ * @param {MiddlewareConfig} [config={override:true}] - Middleware configuration
+ * @returns {Function} json-rpc-engine middleware function
+ */
+function createErrorMiddleware ({ override = true } = {}) {
+ return (req, res, next) => {
+ next(done => {
+ const { error } = res
+ if (!error) { return done() }
+ sanitizeRPCError(error)
+ log.error(`MetaMask - RPC Error: ${error.message}`, error)
+ })
+ }
+}
+
+module.exports = createErrorMiddleware \ No newline at end of file
diff --git a/app/scripts/lib/get-first-preferred-lang-code.js b/app/scripts/lib/get-first-preferred-lang-code.js
index 5473fccf0..1e6a83ba6 100644
--- a/app/scripts/lib/get-first-preferred-lang-code.js
+++ b/app/scripts/lib/get-first-preferred-lang-code.js
@@ -2,6 +2,12 @@ const extension = require('extensionizer')
const promisify = require('pify')
const allLocales = require('../../_locales/index.json')
+const isSupported = extension.i18n && extension.i18n.getAcceptLanguages
+const getPreferredLocales = isSupported ? promisify(
+ extension.i18n.getAcceptLanguages,
+ { errorFirst: false }
+) : async () => []
+
const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().replace('_', '-'))
/**
@@ -12,10 +18,7 @@ const existingLocaleCodes = allLocales.map(locale => locale.code.toLowerCase().r
*
*/
async function getFirstPreferredLangCode () {
- const userPreferredLocaleCodes = await promisify(
- extension.i18n.getAcceptLanguages,
- { errorFirst: false }
- )()
+ const userPreferredLocaleCodes = await getPreferredLocales()
const firstPreferredLangCode = userPreferredLocaleCodes
.map(code => code.toLowerCase())
.find(code => existingLocaleCodes.includes(code))
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index 99cc5d2cf..4e65f0a23 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -1,5 +1,6 @@
const pump = require('pump')
const RpcEngine = require('json-rpc-engine')
+const createErrorMiddleware = require('./createErrorMiddleware')
const createIdRemapMiddleware = require('json-rpc-engine/src/idRemapMiddleware')
const createStreamMiddleware = require('json-rpc-middleware-stream')
const LocalStorageStore = require('obs-store')
@@ -44,6 +45,7 @@ function MetamaskInpageProvider (connectionStream) {
// handle sendAsync requests via dapp-side rpc engine
const rpcEngine = new RpcEngine()
rpcEngine.push(createIdRemapMiddleware())
+ rpcEngine.push(createErrorMiddleware())
rpcEngine.push(streamMiddleware)
self.rpcEngine = rpcEngine
}
diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js
index b1b67f771..d164827ab 100644
--- a/app/scripts/lib/setupRaven.js
+++ b/app/scripts/lib/setupRaven.js
@@ -25,7 +25,7 @@ function setupRaven(opts) {
const report = opts.data
try {
// handle error-like non-error exceptions
- nonErrorException(report)
+ rewriteErrorLikeExceptions(report)
// simplify certain complex error messages (e.g. Ethjs)
simplifyErrorMessages(report)
// modify report urls
@@ -42,27 +42,35 @@ function setupRaven(opts) {
return Raven
}
-function nonErrorException(report) {
- // handle errors that lost their error-ness in serialization
- if (report.message.includes('Non-Error exception captured with keys: message')) {
- if (!(report.extra && report.extra.__serialized__)) return
- report.message = `Non-Error Exception: ${report.extra.__serialized__.message}`
- }
+function rewriteErrorLikeExceptions(report) {
+ // handle errors that lost their error-ness in serialization (e.g. dnode)
+ rewriteErrorMessages(report, (errorMessage) => {
+ if (!errorMessage.includes('Non-Error exception captured with keys:')) return errorMessage
+ if (!(report.extra && report.extra.__serialized__ && report.extra.__serialized__.message)) return errorMessage
+ return `Non-Error Exception: ${report.extra.__serialized__.message}`
+ })
}
function simplifyErrorMessages(report) {
+ rewriteErrorMessages(report, (errorMessage) => {
+ // simplify ethjs error messages
+ errorMessage = extractEthjsErrorMessage(errorMessage)
+ // simplify 'Transaction Failed: known transaction'
+ if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) {
+ // cut the hash from the error message
+ errorMessage = 'Transaction Failed: known transaction'
+ }
+ return errorMessage
+ })
+}
+
+function rewriteErrorMessages(report, rewriteFn) {
+ // rewrite top level message
+ report.message = rewriteFn(report.message)
+ // rewrite each exception message
if (report.exception && report.exception.values) {
report.exception.values.forEach(item => {
- let errorMessage = item.value
- // simplify ethjs error messages
- errorMessage = extractEthjsErrorMessage(errorMessage)
- // simplify 'Transaction Failed: known transaction'
- if (errorMessage.indexOf('Transaction Failed: known transaction') === 0) {
- // cut the hash from the error message
- errorMessage = 'Transaction Failed: known transaction'
- }
- // finalize
- item.value = errorMessage
+ item.value = rewriteFn(item.value)
})
}
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index b0666d9f9..a570f2567 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -145,7 +145,8 @@ module.exports = class MetamaskController extends EventEmitter {
// address book controller
this.addressBookController = new AddressBookController({
initState: initState.AddressBookController,
- }, this.keyringController)
+ preferencesStore: this.preferencesController.store,
+ })
// tx mgmt
this.txController = new TransactionController({
@@ -350,13 +351,12 @@ module.exports = class MetamaskController extends EventEmitter {
verifySeedPhrase: nodeify(this.verifySeedPhrase, this),
clearSeedWordCache: this.clearSeedWordCache.bind(this),
resetAccount: nodeify(this.resetAccount, this),
- importAccountWithStrategy: this.importAccountWithStrategy.bind(this),
+ importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this),
// vault management
submitPassword: nodeify(keyringController.submitPassword, keyringController),
// network management
- setNetworkEndpoints: nodeify(networkController.setNetworkEndpoints, networkController),
setProviderType: nodeify(networkController.setProviderType, networkController),
setCustomRpc: nodeify(this.setCustomRpc, this),
@@ -365,6 +365,7 @@ module.exports = class MetamaskController extends EventEmitter {
addToken: nodeify(preferencesController.addToken, preferencesController),
removeToken: nodeify(preferencesController.removeToken, preferencesController),
setCurrentAccountTab: nodeify(preferencesController.setCurrentAccountTab, preferencesController),
+ setAccountLabel: nodeify(preferencesController.setAccountLabel, preferencesController),
setFeatureFlag: nodeify(preferencesController.setFeatureFlag, preferencesController),
// AddressController
@@ -375,7 +376,6 @@ module.exports = class MetamaskController extends EventEmitter {
createNewVaultAndKeychain: nodeify(this.createNewVaultAndKeychain, this),
createNewVaultAndRestore: nodeify(this.createNewVaultAndRestore, this),
addNewKeyring: nodeify(keyringController.addNewKeyring, keyringController),
- saveAccountLabel: nodeify(keyringController.saveAccountLabel, keyringController),
exportAccount: nodeify(keyringController.exportAccount, keyringController),
// txController
@@ -383,6 +383,7 @@ module.exports = class MetamaskController extends EventEmitter {
updateTransaction: nodeify(txController.updateTransaction, txController),
updateAndApproveTransaction: nodeify(txController.updateAndApproveTransaction, txController),
retryTransaction: nodeify(this.retryTransaction, this),
+ getFilteredTxList: nodeify(txController.getFilteredTxList, txController),
// messageManager
signMessage: nodeify(this.signMessage, this),
@@ -434,7 +435,9 @@ module.exports = class MetamaskController extends EventEmitter {
} else {
vault = await this.keyringController.createNewVaultAndKeychain(password)
- this.selectFirstIdentity(vault)
+ const accounts = await this.keyringController.getAccounts()
+ this.preferencesController.setAddresses(accounts)
+ this.selectFirstIdentity()
}
release()
} catch (err) {
@@ -454,7 +457,9 @@ module.exports = class MetamaskController extends EventEmitter {
const release = await this.createVaultMutex.acquire()
try {
const vault = await this.keyringController.createNewVaultAndRestore(password, seed)
- this.selectFirstIdentity(vault)
+ const accounts = await this.keyringController.getAccounts()
+ this.preferencesController.setAddresses(accounts)
+ this.selectFirstIdentity()
release()
return vault
} catch (err) {
@@ -472,12 +477,10 @@ module.exports = class MetamaskController extends EventEmitter {
*/
/**
- * Retrieves the first Identiy from the passed Vault and selects the related address
- *
- * @param {} vault
+ * Sets the first address in the state to the selected address
*/
- selectFirstIdentity (vault) {
- const { identities } = vault
+ selectFirstIdentity () {
+ const { identities } = this.preferencesController.store.getState()
const address = Object.keys(identities)[0]
this.preferencesController.setSelectedAddress(address)
}
@@ -503,13 +506,15 @@ module.exports = class MetamaskController extends EventEmitter {
await this.verifySeedPhrase()
+ this.preferencesController.setAddresses(newAccounts)
newAccounts.forEach((address) => {
if (!oldAccounts.includes(address)) {
this.preferencesController.setSelectedAddress(address)
}
})
- return keyState
+ const {identities} = this.preferencesController.store.getState()
+ return {...keyState, identities}
}
/**
@@ -604,15 +609,15 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {any} args - The data required by that strategy to import an account.
* @param {Function} cb - A callback function called with a state update on success.
*/
- importAccountWithStrategy (strategy, args, cb) {
- accountImporter.importAccount(strategy, args)
- .then((privateKey) => {
- return this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
- })
- .then(keyring => keyring.getAccounts())
- .then((accounts) => this.preferencesController.setSelectedAddress(accounts[0]))
- .then(() => { cb(null, this.keyringController.fullUpdate()) })
- .catch((reason) => { cb(reason) })
+ async importAccountWithStrategy (strategy, args) {
+ const privateKey = await accountImporter.importAccount(strategy, args)
+ const keyring = await this.keyringController.addNewKeyring('Simple Key Pair', [ privateKey ])
+ const accounts = await keyring.getAccounts()
+ // update accounts in preferences controller
+ const allAccounts = await this.keyringController.getAccounts()
+ this.preferencesController.setAddresses(allAccounts)
+ // set new account as selected
+ await this.preferencesController.setSelectedAddress(accounts[0])
}
// ---------------------------------------------------------------------------
diff --git a/app/scripts/migrations/026.js b/app/scripts/migrations/026.js
new file mode 100644
index 000000000..1b8a91a45
--- /dev/null
+++ b/app/scripts/migrations/026.js
@@ -0,0 +1,47 @@
+const version = 26
+
+/*
+
+This migration moves the identities stored in the KeyringController
+ into the PreferencesController
+
+*/
+
+const clone = require('clone')
+
+module.exports = {
+ version,
+ migrate (originalVersionedData) {
+ const versionedData = clone(originalVersionedData)
+ versionedData.meta.version = version
+ try {
+ const state = versionedData.data
+ versionedData.data = transformState(state)
+ } catch (err) {
+ console.warn(`MetaMask Migration #${version}` + err.stack)
+ return Promise.reject(err)
+ }
+ return Promise.resolve(versionedData)
+ },
+}
+
+function transformState (state) {
+ if (!state.KeyringController || !state.PreferencesController) {
+ return
+ }
+
+ if (!state.KeyringController.walletNicknames) {
+ return state
+ }
+
+ state.PreferencesController.identities = Object.keys(state.KeyringController.walletNicknames)
+ .reduce((identities, address) => {
+ identities[address] = {
+ name: state.KeyringController.walletNicknames[address],
+ address,
+ }
+ return identities
+ }, {})
+ delete state.KeyringController.walletNicknames
+ return state
+}
diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js
index 6c4a51b32..04d90bfff 100644
--- a/app/scripts/migrations/index.js
+++ b/app/scripts/migrations/index.js
@@ -36,4 +36,5 @@ module.exports = [
require('./023'),
require('./024'),
require('./025'),
+ require('./026'),
]