aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/_locales/en/messages.json30
-rw-r--r--app/_locales/zh_CN/messages.json9
-rw-r--r--app/images/ledger-logo.svg34
-rw-r--r--app/manifest.json4
-rw-r--r--app/phishing.html7
-rw-r--r--app/scripts/background.js1
-rw-r--r--app/scripts/contentscript.js2
-rw-r--r--app/scripts/controllers/network/createMetamaskMiddleware.js2
-rw-r--r--app/scripts/controllers/transactions/index.js5
-rw-r--r--app/scripts/inpage.js6
-rw-r--r--app/scripts/lib/auto-reload.js6
-rw-r--r--app/scripts/lib/ipfsContent.js2
-rw-r--r--app/scripts/lib/typed-message-manager.js74
-rw-r--r--app/scripts/metamask-controller.js92
-rw-r--r--app/scripts/phishing-detect.js5
15 files changed, 224 insertions, 55 deletions
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index 2cfd15f50..f6cf7cf69 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -61,6 +61,12 @@
"attemptingConnect": {
"message": "Attempting to connect to blockchain."
},
+ "attemptToCancel": {
+ "message": "Attempt to Cancel?"
+ },
+ "attemptToCancelDescription": {
+ "message": "Attempting to cancel does not guarantee your original transaction will be cancelled. If cancelled, you are still required to pay a transaction fee to the network."
+ },
"attributions": {
"message": "Attributions"
},
@@ -116,6 +122,12 @@
"cancel": {
"message": "Cancel"
},
+ "cancelAttempt": {
+ "message": "Cancel Attempt"
+ },
+ "cancellationGasFee": {
+ "message": "Cancellation Gas Fee"
+ },
"classicInterface": {
"message": "Use classic interface"
},
@@ -216,6 +228,9 @@
"currentConversion": {
"message": "Current Conversion"
},
+ "currentLanguage": {
+ "message": "Current Language"
+ },
"currentNetwork": {
"message": "Current Network"
},
@@ -596,6 +611,9 @@
"metamaskSeedWords": {
"message": "MetaMask Seed Words"
},
+ "metamaskVersion": {
+ "message": "MetaMask Version"
+ },
"min": {
"message": "Minimum"
},
@@ -906,6 +924,9 @@
"selectCurrency": {
"message": "Select Currency"
},
+ "selectLocale": {
+ "message": "Select Locale"
+ },
"selectService": {
"message": "Select Service"
},
@@ -1100,6 +1121,9 @@
"transactionCreated": {
"message": "Transaction created with a value of $1 on $2."
},
+ "transactionWithNonce": {
+ "message": "Transaction $1"
+ },
"transactionDropped": {
"message": "Transaction dropped on $2."
},
@@ -1188,6 +1212,9 @@
"unlockMessage": {
"message": "The decentralized web awaits"
},
+ "updatedWithDate": {
+ "message": "Updated $1"
+ },
"uriErrorMsg": {
"message": "URIs require the appropriate HTTP/HTTPS prefix."
},
@@ -1228,6 +1255,9 @@
"whatsThis": {
"message": "What's this?"
},
+ "yesLetsTry": {
+ "message": "Yes, let's try"
+ },
"youNeedToAllowCameraAccess": {
"message": "You need to allow camera access to use this feature."
},
diff --git a/app/_locales/zh_CN/messages.json b/app/_locales/zh_CN/messages.json
index a39bba9da..8ce0671a4 100644
--- a/app/_locales/zh_CN/messages.json
+++ b/app/_locales/zh_CN/messages.json
@@ -23,6 +23,9 @@
"addTokens": {
"message": "添加代币"
},
+ "addAcquiredTokens": {
+ "message": "在Metamask上添加已用的代币"
+ },
"amount": {
"message": "数量"
},
@@ -116,6 +119,9 @@
"confirmTransaction": {
"message": "确认交易"
},
+ "connectHardwareWallet": {
+ "message": "链接硬件钱包"
+ },
"continue": {
"message": "继续"
},
@@ -714,6 +720,9 @@
"search": {
"message": "搜索"
},
+ "searchResults": {
+ "message": "搜索结果"
+ },
"secretPhrase": {
"message": "输入12位助记词以恢复金库."
},
diff --git a/app/images/ledger-logo.svg b/app/images/ledger-logo.svg
index 21b99d0e5..5ca90e16e 100644
--- a/app/images/ledger-logo.svg
+++ b/app/images/ledger-logo.svg
@@ -1 +1,33 @@
-<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1916.3 516.8" width="2500" height="674"><style>.st0{fill:#333745}</style><g id="squares_1_"><path class="st0" d="M578.2 392.7V24.3h25.6v344.1h175.3v24.3H578.2zm327.5 5.1c-39.7 0-70.4-12.8-93.4-37.1-21.7-24.3-33.3-58.8-33.3-103.6 0-43.5 10.2-79.3 32-104.9 21.7-26.9 49.9-39.7 87-39.7 32 0 57.6 11.5 76.8 33.3 19.2 23 28.1 53.7 28.1 92.1v20.5H804.6c0 37.1 9 66.5 26.9 85.7 16.6 20.5 42.2 29.4 74.2 29.4 15.3 0 29.4-1.3 40.9-3.8 11.5-2.6 26.9-6.4 44.8-14.1v24.3c-15.3 6.4-29.4 11.5-42.2 14.1-14.3 2.6-28.9 3.9-43.5 3.8zM898 135.6c-26.9 0-47.3 9-64 25.6-15.3 17.9-25.6 42.2-28.1 75.5h168.9c0-32-6.4-56.3-20.5-74.2-12.8-18-32-26.9-56.3-26.9zm238-21.8c19.2 0 37.1 3.8 51.2 10.2 14.1 7.7 26.9 19.2 38.4 37.1h1.3c-1.3-21.7-1.3-42.2-1.3-62.7V0h24.3v392.7h-16.6l-6.4-42.2c-20.5 30.7-51.2 47.3-89.6 47.3s-66.5-11.5-87-35.8c-20.5-23-29.4-57.6-29.4-102.3 0-47.3 10.2-83.2 29.4-108.7 19.2-25.6 48.6-37.2 85.7-37.2zm0 21.8c-29.4 0-52.4 10.2-67.8 32-15.3 20.5-23 51.2-23 92.1 0 78 30.7 116.4 90.8 116.4 30.7 0 53.7-9 67.8-26.9 14.1-17.9 21.7-47.3 21.7-89.6v-3.8c0-42.2-7.7-72.9-21.7-90.8-12.8-20.5-35.8-29.4-67.8-29.4zm379.9-16.6v17.9l-56.3 3.8c15.3 19.2 23 39.7 23 61.4 0 26.9-9 47.3-26.9 64-17.9 16.6-40.9 24.3-70.4 24.3-12.8 0-21.7 0-25.6-1.3-10.2 5.1-17.9 11.5-23 17.9-5.1 7.7-7.7 14.1-7.7 23s3.8 15.3 10.2 19.2c6.4 3.8 17.9 6.4 33.3 6.4h47.3c29.4 0 52.4 6.4 67.8 17.9s24.3 29.4 24.3 53.7c0 29.4-11.5 51.2-34.5 66.5-23 15.3-56.3 23-99.8 23-34.5 0-61.4-6.4-80.6-20.5-19.2-12.8-28.1-32-28.1-55 0-19.2 6.4-34.5 17.9-47.3s28.1-20.5 47.3-25.6c-7.7-3.8-15.3-9-19.2-15.3-5-6.2-7.7-13.8-7.7-21.7 0-17.9 11.5-34.5 34.5-48.6-15.3-6.4-28.1-16.6-37.1-30.7-9-14.1-12.8-30.7-12.8-48.6 0-26.9 9-49.9 25.6-66.5 17.9-16.6 40.9-24.3 70.4-24.3 17.9 0 32 1.3 42.2 5.1h85.7v1.3h.2zm-222.6 319.8c0 37.1 28.1 56.3 84.4 56.3 71.6 0 107.5-23 107.5-69.1 0-16.6-5.1-28.1-16.6-35.8-11.5-7.7-29.4-11.5-55-11.5h-44.8c-49.9 1.2-75.5 20.4-75.5 60.1zm21.8-235.4c0 21.7 6.4 37.1 19.2 49.9 12.8 11.5 29.4 17.9 51.2 17.9 23 0 40.9-6.4 52.4-17.9 12.8-11.5 17.9-28.1 17.9-49.9 0-23-6.4-40.9-19.2-52.4-12.8-11.5-29.4-17.9-52.4-17.9-21.7 0-39.7 6.4-51.2 19.2-12.8 11.4-17.9 29.3-17.9 51.1z"/><path class="st0" d="M1640 397.8c-39.7 0-70.4-12.8-93.4-37.1-21.7-24.3-33.3-58.8-33.3-103.6 0-43.5 10.2-79.3 32-104.9 21.7-26.9 49.9-39.7 87-39.7 32 0 57.6 11.5 76.8 33.3 19.2 23 28.1 53.7 28.1 92.1v20.5h-197c0 37.1 9 66.5 26.9 85.7 16.6 20.5 42.2 29.4 74.2 29.4 15.3 0 29.4-1.3 40.9-3.8 11.5-2.6 26.9-6.4 44.8-14.1v24.3c-15.3 6.4-29.4 11.5-42.2 14.1-14.1 2.6-28.2 3.8-44.8 3.8zm-6.4-262.2c-26.9 0-47.3 9-64 25.6-15.3 17.9-25.6 42.2-28.1 75.5h168.9c0-32-6.4-56.3-20.5-74.2-12.8-18-32-26.9-56.3-26.9zm245.6-21.8c11.5 0 24.3 1.3 37.1 3.8l-5.1 24.3c-11.8-2.6-23.8-3.9-35.8-3.8-23 0-42.2 10.2-57.6 29.4-15.3 20.5-23 44.8-23 75.5v149.7h-25.6V119h21.7l2.6 49.9h1.3c11.5-20.5 23-34.5 35.8-42.2 15.4-9 30.7-12.9 48.6-12.9zM333.9 12.8h-183v245.6h245.6V76.7c.1-34.5-28.1-63.9-62.6-63.9zm-239.2 0H64c-34.5 0-64 28.1-64 64v30.7h94.7V12.8zM0 165h94.7v94.7H0V165zm301.9 245.6h30.7c34.5 0 64-28.1 64-64V316h-94.7v94.6zm-151-94.6h94.7v94.7h-94.7V316zM0 316v30.7c0 34.5 28.1 64 64 64h30.7V316H0z"/></g></svg> \ No newline at end of file
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 197.4 48.6" style="enable-background:new 0 0 197.4 48.6;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#1D2028;}
+</style>
+<title>Fichier 8</title>
+<g>
+ <path class="st0" d="M34.1,0H15.5v25.1h25.1V6.5C40.6,2.9,37.7,0,34.1,0z"/>
+ <path class="st0" d="M9.7,0H6.5C2.9,0,0,2.9,0,6.5v3.2h9.7V0z"/>
+ <rect y="15.5" class="st0" width="9.7" height="9.7"/>
+ <path class="st0" d="M31,40.6h3.2c3.6,0,6.5-2.9,6.5-6.5V31H31V40.6z"/>
+ <rect x="15.5" y="31" class="st0" width="9.7" height="9.7"/>
+ <path class="st0" d="M0,31v3.2c0,3.6,2.9,6.5,6.5,6.5h3.2V31H0z"/>
+ <g>
+ <polygon class="st0" points="65.4,2.6 61.6,2.6 61.6,38.1 81.7,38.1 81.7,34.7 65.4,34.7 "/>
+ <path class="st0" d="M93.9,12c-7.4,0-12.6,5.5-12.6,13.4c0,0.3,0,0.6,0,0.9c0.1,3.4,1.6,6.6,4.1,9c2.4,2.2,5.5,3.5,8.8,3.5
+ c0.2,0,0.3,0,0.5,0c3.5,0,6.8-1.3,9.4-3.5l0.1-0.1l-1.7-2.8l-0.2,0.1c-2.1,1.9-4.7,3-7.5,3c-4.6,0-9.3-3-9.6-9.7h19.2v-0.2
+ c0,0,0.1-1.2,0.1-1.8C104.5,16.6,100.3,12,93.9,12z M85.3,22.6c0.8-4.5,4.1-7.4,8.4-7.4c3.2,0,6.7,1.9,7,7.4H85.3z"/>
+ <path class="st0" d="M126.5,15c0,0.4,0,0.9,0,1.3c-1.6-2.7-4.6-4.4-7.7-4.4c-0.1,0-0.2,0-0.3,0c-6.8,0-11.5,5.4-11.5,13.3
+ c0,8,4.5,13.4,11.2,13.4c5.3,0,7.7-3.2,8.5-4.6c0,0.4,0,0.8,0,1.1v2.8h3.6V2.6h-3.7V15H126.5z M118.7,35.3c-4.7,0-7.8-4-7.8-10
+ c0-5.8,3.3-9.9,7.9-9.9c3.9,0,7.8,3.1,7.8,9.9C126.6,32.7,122.5,35.3,118.7,35.3z"/>
+ <path class="st0" d="M152.2,15.5c0,0.1,0,0.2,0,0.2c-0.7-1.2-2.9-3.8-8.2-3.8c-6.7,0-11.1,5.1-11.1,12.9s4.6,13.1,11.4,13.1
+ c3.7,0,6.2-1.3,7.9-4c0,0.4,0,0.8,0,1.2v2.3c0,4.9-3.1,7.7-8.6,7.7c-2.3,0-4.7-0.6-6.8-1.7l-0.2-0.1l-1.4,3.1l0.2,0.1
+ c2.6,1.3,5.5,2,8.3,2c5.9,0,12.2-3,12.2-11.3V12.6h-3.7L152.2,15.5L152.2,15.5z M144.8,34.6c-4.9,0-8.1-3.8-8.1-9.7
+ c0-6,2.8-9.4,7.6-9.4c5.3,0,7.8,3.1,7.8,9.4C152.2,31.1,149.6,34.6,144.8,34.6z"/>
+ <path class="st0" d="M171,12c-7.4,0-12.5,5.5-12.5,13.3c0,0.3,0,0.6,0,0.9c0.1,3.4,1.6,6.6,4.1,9c2.4,2.2,5.5,3.5,8.8,3.5
+ c0.2,0,0.3,0,0.5,0c3.5,0,6.8-1.3,9.4-3.5l0.1-0.1l-1.8-2.8l-0.2,0.1c-2.1,1.9-4.7,3-7.5,3c-4.6,0-9.3-3-9.6-9.7h19.3v-0.2
+ c0,0,0.1-1.2,0.1-1.8C181.7,16.6,177.5,12,171,12z M162.5,22.6c0.8-4.5,4.1-7.4,8.4-7.4c3.2,0,6.7,1.9,7,7.4H162.5z"/>
+ <path class="st0" d="M197.3,12.5c-0.5-0.1-0.9-0.1-1.4-0.2c-3.5,0-6.4,2.2-7.9,5.9c0-0.3,0-0.7,0-1.1v-4.6h-3.7l0.1,25.3V38h3.8
+ V27.3c0-1.6,0.2-3.3,0.7-4.8c1.2-3.9,3.9-6.4,7.1-6.4c0.4,0,0.8,0,1.2,0.1h0.2v-3.7L197.3,12.5z"/>
+ </g>
+</g>
+</svg>
diff --git a/app/manifest.json b/app/manifest.json
index 3718f5c8a..7c554f1ac 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -1,7 +1,7 @@
{
"name": "__MSG_appName__",
"short_name": "__MSG_appName__",
- "version": "4.9.3",
+ "version": "4.12.0",
"manifest_version": 2,
"author": "https://metamask.io",
"description": "__MSG_appDescription__",
@@ -77,4 +77,4 @@
"*"
]
}
-}
+} \ No newline at end of file
diff --git a/app/phishing.html b/app/phishing.html
index e20c9ac9c..eabb363b3 100644
--- a/app/phishing.html
+++ b/app/phishing.html
@@ -25,9 +25,9 @@
a {
color: white;
}
-
</style>
+
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
@@ -43,9 +43,9 @@
ga('create', 'UA-68598031-1', 'auto' {'allowLinker':true});
ga('send', 'pageview');
ga('require', 'linker');
- ga('linker:autoLink', ['harrydenley.com', 'metamask.io'], false, true);
+ ga('linker:autoLink', ['metamask.io'], false, true);
</script>
-
+ <script src="phishing-detect.js"></script>
</head>
<body>
@@ -55,6 +55,7 @@
<h3>ATTENTION</h3>
<p>MetaMask believes this domain could currently compromise your security and has prevented you from interacting with it.</p>
<p>This is because the site tested positive on the <a href="https://github.com/metamask/eth-phishing-detect">Ethereum Phishing Detector</a>. This includes outright malicious websites and legitimate websites that have been compromised by a malicious actor.</p>
+ <p id="esdbLink"></p>
<p>You can turn MetaMask off to interact with this site, but it is advised not to.</p>
<p>If you think this domain is incorrectly flagged or if a blocked legitimate website has resolved its security issues, <a href="https://github.com/metamask/eth-phishing-detect/issues/new">please file an issue</a>.</p>
diff --git a/app/scripts/background.js b/app/scripts/background.js
index 546fef569..ae450352e 100644
--- a/app/scripts/background.js
+++ b/app/scripts/background.js
@@ -406,6 +406,7 @@ function setupController (initState, initLangCode) {
controller.txController.on('update:badge', updateBadge)
controller.messageManager.on('updateBadge', updateBadge)
controller.personalMessageManager.on('updateBadge', updateBadge)
+ controller.typedMessageManager.on('updateBadge', updateBadge)
/**
* Updates the Web Extension's "badge" number, on the little fox in the toolbar.
diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js
index 6eee1987a..2cbfb811e 100644
--- a/app/scripts/contentscript.js
+++ b/app/scripts/contentscript.js
@@ -199,5 +199,5 @@ function blacklistedDomainCheck () {
function redirectToPhishingWarning () {
console.log('MetaMask - routing to Phishing Warning component')
const extensionURL = extension.runtime.getURL('phishing.html')
- window.location.href = extensionURL
+ window.location.href = extensionURL + '#' + window.location.hostname
}
diff --git a/app/scripts/controllers/network/createMetamaskMiddleware.js b/app/scripts/controllers/network/createMetamaskMiddleware.js
index 8b17829b7..9e6a45888 100644
--- a/app/scripts/controllers/network/createMetamaskMiddleware.js
+++ b/app/scripts/controllers/network/createMetamaskMiddleware.js
@@ -38,6 +38,6 @@ function createPendingNonceMiddleware ({ getPendingNonce }) {
const address = req.params[0]
const blockRef = req.params[1]
if (blockRef !== 'pending') return next()
- req.result = await getPendingNonce(address)
+ res.result = await getPendingNonce(address)
})
}
diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js
index 59e30cdde..e2965ceb6 100644
--- a/app/scripts/controllers/transactions/index.js
+++ b/app/scripts/controllers/transactions/index.js
@@ -18,7 +18,7 @@ const {
TRANSACTION_STATUS_APPROVED,
} = require('./enums')
-const { hexToBn, bnToHex } = require('../../lib/util')
+const { hexToBn, bnToHex, BnMultiplyByFraction } = require('../../lib/util')
/**
Transaction Controller is an aggregate of sub-controllers and trackers
@@ -244,7 +244,8 @@ class TransactionController extends EventEmitter {
const originalTxMeta = this.txStateManager.getTx(originalTxId)
const { txParams } = originalTxMeta
const { gasPrice: lastGasPrice, from, nonce } = txParams
- const newGasPrice = customGasPrice || bnToHex(hexToBn(lastGasPrice).mul(1.1))
+
+ const newGasPrice = customGasPrice || bnToHex(BnMultiplyByFraction(hexToBn(lastGasPrice), 11, 10))
const newTxMeta = this.txStateManager.generateTxMeta({
txParams: {
from,
diff --git a/app/scripts/inpage.js b/app/scripts/inpage.js
index d9fda1feb..d924be516 100644
--- a/app/scripts/inpage.js
+++ b/app/scripts/inpage.js
@@ -9,6 +9,11 @@ restoreContextAfterImports()
log.setDefaultLevel(process.env.METAMASK_DEBUG ? 'debug' : 'warn')
+console.warn('ATTENTION: In an effort to improve user privacy, MetaMask will ' +
+'stop exposing user accounts to dapps by default beginning November 2nd, 2018. ' +
+'Dapps should call provider.enable() in order to view and use accounts. Please see ' +
+'https://bit.ly/2QQHXvF for complete information and up-to-date example code.')
+
//
// setup plugin communication
//
@@ -52,6 +57,7 @@ if (typeof window.web3 !== 'undefined') {
or MetaMask and another web3 extension. Please remove one
and try again.`)
}
+
var web3 = new Web3(inpageProvider)
web3.setProvider = function () {
log.debug('MetaMask - overrode web3.setProvider')
diff --git a/app/scripts/lib/auto-reload.js b/app/scripts/lib/auto-reload.js
index cce31c3d2..558391a06 100644
--- a/app/scripts/lib/auto-reload.js
+++ b/app/scripts/lib/auto-reload.js
@@ -2,18 +2,12 @@ module.exports = setupDappAutoReload
function setupDappAutoReload (web3, observable) {
// export web3 as a global, checking for usage
- let hasBeenWarned = false
let reloadInProgress = false
let lastTimeUsed
let lastSeenNetwork
global.web3 = new Proxy(web3, {
get: (_web3, key) => {
- // show warning once on web3 access
- if (!hasBeenWarned && key !== 'currentProvider') {
- console.warn('MetaMask: web3 will be deprecated in the near future in favor of the ethereumProvider \nhttps://github.com/MetaMask/faq/blob/master/detecting_metamask.md#web3-deprecation')
- hasBeenWarned = true
- }
// get the time of use
lastTimeUsed = Date.now()
// return value normally
diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js
index 38682b916..62a808b90 100644
--- a/app/scripts/lib/ipfsContent.js
+++ b/app/scripts/lib/ipfsContent.js
@@ -5,6 +5,8 @@ module.exports = function (provider) {
function ipfsContent (details) {
const name = details.url.substring(7, details.url.length - 1)
let clearTime = null
+ if (/^.+\.eth$/.test(name) === false) return
+
extension.tabs.query({active: true}, tab => {
extension.tabs.update(tab.id, { url: 'loading.html' })
diff --git a/app/scripts/lib/typed-message-manager.js b/app/scripts/lib/typed-message-manager.js
index e5e1c94b3..b10145f3b 100644
--- a/app/scripts/lib/typed-message-manager.js
+++ b/app/scripts/lib/typed-message-manager.js
@@ -4,6 +4,7 @@ const createId = require('./random-id')
const assert = require('assert')
const sigUtil = require('eth-sig-util')
const log = require('loglevel')
+const jsonschema = require('jsonschema')
/**
* Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
@@ -17,7 +18,7 @@ const log = require('loglevel')
* @property {Object} msgParams.from The address that is making the signature request.
* @property {string} msgParams.data A hex string conversion of the raw buffer data of the signature request
* @property {number} time The epoch time at which the this message was created
- * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed' or 'rejected'
+ * @property {string} status Indicates whether the signature request is 'unapproved', 'approved', 'signed', 'rejected', or 'errored'
* @property {string} type The json-prc signing method for which a signature request has been made. A 'Message' will
* always have a 'eth_signTypedData' type.
*
@@ -26,17 +27,10 @@ const log = require('loglevel')
module.exports = class TypedMessageManager extends EventEmitter {
/**
* Controller in charge of managing - storing, adding, removing, updating - TypedMessage.
- *
- * @typedef {Object} TypedMessage
- * @param {Object} opts @deprecated
- * @property {Object} memStore The observable store where TypedMessage are saved.
- * @property {Object} memStore.unapprovedTypedMessages A collection of all TypedMessages in the 'unapproved' state
- * @property {number} memStore.unapprovedTypedMessagesCount The count of all TypedMessages in this.memStore.unapprobedMsgs
- * @property {array} messages Holds all messages that have been created by this TypedMessage
- *
*/
- constructor (opts) {
+ constructor ({ networkController }) {
super()
+ this.networkController = networkController
this.memStore = new ObservableStore({
unapprovedTypedMessages: {},
unapprovedTypedMessagesCount: 0,
@@ -76,15 +70,17 @@ module.exports = class TypedMessageManager extends EventEmitter {
* @returns {promise} When the message has been signed or rejected
*
*/
- addUnapprovedMessageAsync (msgParams, req) {
+ addUnapprovedMessageAsync (msgParams, req, version) {
return new Promise((resolve, reject) => {
- const msgId = this.addUnapprovedMessage(msgParams, req)
+ const msgId = this.addUnapprovedMessage(msgParams, req, version)
this.once(`${msgId}:finished`, (data) => {
switch (data.status) {
case 'signed':
return resolve(data.rawSig)
case 'rejected':
return reject(new Error('MetaMask Message Signature: User denied message signature.'))
+ case 'errored':
+ return reject(new Error(`MetaMask Message Signature: ${data.error}`))
default:
return reject(new Error(`MetaMask Message Signature: Unknown problem: ${JSON.stringify(msgParams)}`))
}
@@ -102,7 +98,8 @@ module.exports = class TypedMessageManager extends EventEmitter {
* @returns {number} The id of the newly created TypedMessage.
*
*/
- addUnapprovedMessage (msgParams, req) {
+ addUnapprovedMessage (msgParams, req, version) {
+ msgParams.version = version
this.validateParams(msgParams)
// add origin from request
if (req) msgParams.origin = req.origin
@@ -132,14 +129,33 @@ module.exports = class TypedMessageManager extends EventEmitter {
*
*/
validateParams (params) {
- assert.equal(typeof params, 'object', 'Params should ben an object.')
- assert.ok('data' in params, 'Params must include a data field.')
- assert.ok('from' in params, 'Params must include a from field.')
- assert.ok(Array.isArray(params.data), 'Data should be an array.')
- assert.equal(typeof params.from, 'string', 'From field must be a string.')
- assert.doesNotThrow(() => {
- sigUtil.typedSignatureHash(params.data)
- }, 'Expected EIP712 typed data')
+ switch (params.version) {
+ case 'V1':
+ assert.equal(typeof params, 'object', 'Params should ben an object.')
+ assert.ok('data' in params, 'Params must include a data field.')
+ assert.ok('from' in params, 'Params must include a from field.')
+ assert.ok(Array.isArray(params.data), 'Data should be an array.')
+ assert.equal(typeof params.from, 'string', 'From field must be a string.')
+ assert.doesNotThrow(() => {
+ sigUtil.typedSignatureHash(params.data)
+ }, 'Expected EIP712 typed data')
+ break
+ case 'V3':
+ let data
+ assert.equal(typeof params, 'object', 'Params should be an object.')
+ assert.ok('data' in params, 'Params must include a data field.')
+ assert.ok('from' in params, 'Params must include a from field.')
+ assert.equal(typeof params.from, 'string', 'From field must be a string.')
+ assert.equal(typeof params.data, 'string', 'Data must be passed as a valid JSON string.')
+ assert.doesNotThrow(() => { data = JSON.parse(params.data) }, 'Data must be passed as a valid JSON string.')
+ const validation = jsonschema.validate(data, sigUtil.TYPED_MESSAGE_SCHEMA)
+ assert.ok(data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`)
+ assert.equal(validation.errors.length, 0, 'Data must conform to EIP-712 schema. See https://git.io/fNtcx.')
+ const chainId = data.domain.chainId
+ const activeChainId = parseInt(this.networkController.getNetworkState())
+ chainId && assert.equal(chainId, activeChainId, `Provided chainId (${chainId}) must match the active chainId (${activeChainId})`)
+ break
+ }
}
/**
@@ -214,6 +230,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
*/
prepMsgForSigning (msgParams) {
delete msgParams.metamaskId
+ delete msgParams.version
return Promise.resolve(msgParams)
}
@@ -227,6 +244,19 @@ module.exports = class TypedMessageManager extends EventEmitter {
this._setMsgStatus(msgId, 'rejected')
}
+ /**
+ * Sets a TypedMessage status to 'errored' via a call to this._setMsgStatus.
+ *
+ * @param {number} msgId The id of the TypedMessage to error
+ *
+ */
+ errorMessage (msgId, error) {
+ const msg = this.getMsg(msgId)
+ msg.error = error
+ this._updateMsg(msg)
+ this._setMsgStatus(msgId, 'errored')
+ }
+
//
// PRIVATE METHODS
//
@@ -250,7 +280,7 @@ module.exports = class TypedMessageManager extends EventEmitter {
msg.status = status
this._updateMsg(msg)
this.emit(`${msgId}:${status}`, msg)
- if (status === 'rejected' || status === 'signed') {
+ if (status === 'rejected' || status === 'signed' || status === 'errored') {
this.emit(`${msgId}:finished`, msg)
}
}
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index f9a12628b..123e17569 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -49,6 +49,8 @@ const log = require('loglevel')
const TrezorKeyring = require('eth-trezor-keyring')
const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
const EthQuery = require('eth-query')
+const ethUtil = require('ethereumjs-util')
+const sigUtil = require('eth-sig-util')
module.exports = class MetamaskController extends EventEmitter {
@@ -205,7 +207,7 @@ module.exports = class MetamaskController extends EventEmitter {
this.networkController.lookupNetwork()
this.messageManager = new MessageManager()
this.personalMessageManager = new PersonalMessageManager()
- this.typedMessageManager = new TypedMessageManager()
+ this.typedMessageManager = new TypedMessageManager({ networkController: this.networkController })
this.publicConfigStore = this.initPublicConfigStore()
this.store.updateStructure({
@@ -250,6 +252,7 @@ module.exports = class MetamaskController extends EventEmitter {
eth_syncing: false,
web3_clientVersion: `MetaMask/v${version}`,
},
+ version,
// account mgmt
getAccounts: async () => {
const isUnlocked = this.keyringController.memStore.getState().isUnlocked
@@ -266,7 +269,7 @@ module.exports = class MetamaskController extends EventEmitter {
// msg signing
processEthSignMessage: this.newUnsignedMessage.bind(this),
processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
- processTypedMessage: this.newUnsignedTypedMessage.bind(this),
+ getPendingNonce: this.getPendingNonce.bind(this),
}
const providerProxy = this.networkController.initializeProvider(providerOpts)
return providerProxy
@@ -975,22 +978,31 @@ module.exports = class MetamaskController extends EventEmitter {
* @param {Object} msgParams - The params passed to eth_signTypedData.
* @returns {Object} Full state update.
*/
- signTypedMessage (msgParams) {
- log.info('MetaMaskController - signTypedMessage')
+ async signTypedMessage (msgParams) {
+ log.info('MetaMaskController - eth_signTypedData')
const msgId = msgParams.metamaskId
- // sets the status op the message to 'approved'
- // and removes the metamaskId for signing
- return this.typedMessageManager.approveMessage(msgParams)
- .then((cleanMsgParams) => {
- // signs the message
- return this.keyringController.signTypedMessage(cleanMsgParams)
- })
- .then((rawSig) => {
- // tells the listener that the message has been signed
- // and can be returned to the dapp
- this.typedMessageManager.setMsgStatusSigned(msgId, rawSig)
- return this.getState()
- })
+ const version = msgParams.version
+ try {
+ const cleanMsgParams = await this.typedMessageManager.approveMessage(msgParams)
+ const address = sigUtil.normalize(cleanMsgParams.from)
+ const keyring = await this.keyringController.getKeyringForAccount(address)
+ const wallet = keyring._getWalletForAccount(address)
+ const privKey = ethUtil.toBuffer(wallet.getPrivateKey())
+ let signature
+ switch (version) {
+ case 'V1':
+ signature = sigUtil.signTypedDataLegacy(privKey, { data: cleanMsgParams.data })
+ break
+ case 'V3':
+ signature = sigUtil.signTypedData(privKey, { data: JSON.parse(cleanMsgParams.data) })
+ break
+ }
+ this.typedMessageManager.setMsgStatusSigned(msgId, signature)
+ return this.getState()
+ } catch (error) {
+ log.info('MetaMaskController - eth_signTypedData failed.', error)
+ this.typedMessageManager.errorMessage(msgId, error)
+ }
}
/**
@@ -1241,6 +1253,9 @@ module.exports = class MetamaskController extends EventEmitter {
engine.push(createLoggerMiddleware({ origin }))
engine.push(filterMiddleware)
engine.push(this.preferencesController.requestWatchAsset.bind(this.preferencesController))
+ engine.push(this.createTypedDataMiddleware('eth_signTypedData', 'V1').bind(this))
+ engine.push(this.createTypedDataMiddleware('eth_signTypedData_v1', 'V1').bind(this))
+ engine.push(this.createTypedDataMiddleware('eth_signTypedData_v3', 'V3', true).bind(this))
engine.push(createProviderMiddleware({ provider: this.provider }))
// setup connection
@@ -1349,6 +1364,19 @@ module.exports = class MetamaskController extends EventEmitter {
return '0x' + percentileNumBn.mul(GWEI_BN).toString(16)
}
+ /**
+ * Returns the nonce that will be associated with a transaction once approved
+ * @param address {string} - The hex string address for the transaction
+ * @returns Promise<number>
+ */
+ async getPendingNonce (address) {
+ const { nonceDetails, releaseLock} = await this.txController.nonceTracker.getNonceLock(address)
+ const pendingNonce = nonceDetails.params.highestSuggested
+
+ releaseLock()
+ return pendingNonce
+ }
+
//=============================================================================
// CONFIG
//=============================================================================
@@ -1474,4 +1502,34 @@ module.exports = class MetamaskController extends EventEmitter {
set isClientOpenAndUnlocked (active) {
this.tokenRatesController.isActive = active
}
+
+ /**
+ * Creates RPC engine middleware for processing eth_signTypedData requests
+ *
+ * @param {Object} req - request object
+ * @param {Object} res - response object
+ * @param {Function} - next
+ * @param {Function} - end
+ */
+ createTypedDataMiddleware (methodName, version, reverse) {
+ return async (req, res, next, end) => {
+ const { method, params } = req
+ if (method === methodName) {
+ const promise = this.typedMessageManager.addUnapprovedMessageAsync({
+ data: reverse ? params[1] : params[0],
+ from: reverse ? params[0] : params[1],
+ }, req, version)
+ this.sendUpdate()
+ this.opts.showUnconfirmedMessage()
+ try {
+ res.result = await promise
+ end()
+ } catch (error) {
+ end(error)
+ }
+ } else {
+ next()
+ }
+ }
+ }
}
diff --git a/app/scripts/phishing-detect.js b/app/scripts/phishing-detect.js
new file mode 100644
index 000000000..4168b6618
--- /dev/null
+++ b/app/scripts/phishing-detect.js
@@ -0,0 +1,5 @@
+window.onload = function() {
+ if (window.location.pathname === '/phishing.html') {
+ document.getElementById('esdbLink').innerHTML = '<b>To read more about this scam, navigate to: <a href="https://etherscamdb.info/domain/' + window.location.hash.substring(1) + '"> https://etherscamdb.info/domain/' + window.location.hash.substring(1) + '</a></b>'
+ }
+}