From 695b157e7b511a94a18ada1578ece2e4e838f869 Mon Sep 17 00:00:00 2001 From: MikeCheng1208 Date: Fri, 4 May 2018 19:57:19 +0800 Subject: Intergrate ENS with IPFS --- app/404.html | 28 ++++++++++++ app/error.html | 79 +++++++++++++++++++++++++++++++++ app/images/404.png | Bin 0 -> 39401 bytes app/images/cancel.png | Bin 0 -> 10928 bytes app/images/deadface.png | Bin 0 -> 8333 bytes app/images/loginglogo.svg | 53 ++++++++++++++++++++++ app/images/logo.png | Bin 0 -> 6589 bytes app/images/pw-128x128.png | Bin 0 -> 94988 bytes app/images/pw-48x48.png | Bin 0 -> 3279 bytes app/images/pw128x128.png | Bin 0 -> 94988 bytes app/loading.html | 35 +++++++++++++++ app/manifest.json | 15 ++++--- app/no_mainnet.html | 59 ++++++++++++++++++++++++ app/scripts/background.js | 10 +++++ app/scripts/lib/contracts/registrar.js | 1 + app/scripts/lib/contracts/resolver.js | 2 + app/scripts/lib/portalnetwork.js | 33 ++++++++++++++ app/scripts/lib/resolver.js | 46 +++++++++++++++++++ 18 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 app/404.html create mode 100644 app/error.html create mode 100644 app/images/404.png create mode 100644 app/images/cancel.png create mode 100644 app/images/deadface.png create mode 100644 app/images/loginglogo.svg create mode 100644 app/images/logo.png create mode 100644 app/images/pw-128x128.png create mode 100644 app/images/pw-48x48.png create mode 100644 app/images/pw128x128.png create mode 100644 app/loading.html create mode 100644 app/no_mainnet.html create mode 100644 app/scripts/lib/contracts/registrar.js create mode 100644 app/scripts/lib/contracts/resolver.js create mode 100644 app/scripts/lib/portalnetwork.js create mode 100644 app/scripts/lib/resolver.js (limited to 'app') diff --git a/app/404.html b/app/404.html new file mode 100644 index 000000000..2fbf1e311 --- /dev/null +++ b/app/404.html @@ -0,0 +1,28 @@ + + + 404 + + + + + + \ No newline at end of file diff --git a/app/error.html b/app/error.html new file mode 100644 index 000000000..d4484d8e3 --- /dev/null +++ b/app/error.html @@ -0,0 +1,79 @@ + + + Portal Network + + + + +
+ +

not found

+

Powered by Portal Network

+
+ + + diff --git a/app/images/404.png b/app/images/404.png new file mode 100644 index 000000000..b1a767dde Binary files /dev/null and b/app/images/404.png differ diff --git a/app/images/cancel.png b/app/images/cancel.png new file mode 100644 index 000000000..4e0eb1143 Binary files /dev/null and b/app/images/cancel.png differ diff --git a/app/images/deadface.png b/app/images/deadface.png new file mode 100644 index 000000000..e12476c03 Binary files /dev/null and b/app/images/deadface.png differ diff --git a/app/images/loginglogo.svg b/app/images/loginglogo.svg new file mode 100644 index 000000000..ca8b0a2ee --- /dev/null +++ b/app/images/loginglogo.svg @@ -0,0 +1,53 @@ + + + + logo2 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/images/logo.png b/app/images/logo.png new file mode 100644 index 000000000..db6ba7d6c Binary files /dev/null and b/app/images/logo.png differ diff --git a/app/images/pw-128x128.png b/app/images/pw-128x128.png new file mode 100644 index 000000000..a0eb1b730 Binary files /dev/null and b/app/images/pw-128x128.png differ diff --git a/app/images/pw-48x48.png b/app/images/pw-48x48.png new file mode 100644 index 000000000..a96c59e15 Binary files /dev/null and b/app/images/pw-48x48.png differ diff --git a/app/images/pw128x128.png b/app/images/pw128x128.png new file mode 100644 index 000000000..a0eb1b730 Binary files /dev/null and b/app/images/pw128x128.png differ diff --git a/app/loading.html b/app/loading.html new file mode 100644 index 000000000..bbf1c1b4e --- /dev/null +++ b/app/loading.html @@ -0,0 +1,35 @@ + + + Portal Network + + + + + + diff --git a/app/manifest.json b/app/manifest.json index 3e5eed205..0f5a2566f 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_appName__", "short_name": "__MSG_appName__", - "version": "4.6.0", + "version": "4.6.2", "manifest_version": 2, "author": "https://metamask.io", "description": "__MSG_appDescription__", @@ -59,14 +59,19 @@ "unlimitedStorage", "clipboardWrite", "http://localhost:8545/", - "https://*.infura.io/" + "https://*.infura.io/", + "activeTab", + "webRequest", + "*://*.eth/" ], "web_accessible_resources": [ - "inpage.js" + "inpage.js", + "*" ], "externally_connectable": { "matches": [ "https://metamask.io/*" ] - } -} \ No newline at end of file + }, + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" +} diff --git a/app/no_mainnet.html b/app/no_mainnet.html new file mode 100644 index 000000000..f90e6d397 --- /dev/null +++ b/app/no_mainnet.html @@ -0,0 +1,59 @@ + + + + + no_mainnet + + + +
+ +

ENS resolver only support on Ethereum mainnet

+
+ + \ No newline at end of file diff --git a/app/scripts/background.js b/app/scripts/background.js index 686296329..fc2ad5773 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -25,6 +25,8 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics') const EdgeEncryptor = require('./edge-encryptor') const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code') const getObjStructure = require('./lib/getObjStructure') +const pw = require('./lib/portalnetwork.js') + const { ENVIRONMENT_TYPE_POPUP, ENVIRONMENT_TYPE_NOTIFICATION, @@ -59,12 +61,17 @@ const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) const localStore = new LocalStore() let versionedData +console.log('localStore', localStore); +console.log('diskStore', diskStore); + // initialization flow initialize().catch(log.error) // setup metamask mesh testing container setupMetamaskMeshMetrics() + + /** * An object representing a transaction, in whatever state it is in. * @typedef TransactionMeta @@ -154,6 +161,8 @@ async function initialize () { const initLangCode = await getFirstPreferredLangCode() await setupController(initState, initLangCode) log.debug('MetaMask initialization complete.') + // porto network init + pw(initState.NetworkController.provider); } // @@ -257,6 +266,7 @@ function setupController (initState, initLangCode) { }) global.metamaskController = controller + // report failed transactions to Sentry controller.txController.on(`tx:status-update`, (txId, status) => { if (status !== 'failed') return diff --git a/app/scripts/lib/contracts/registrar.js b/app/scripts/lib/contracts/registrar.js new file mode 100644 index 000000000..980a64d7f --- /dev/null +++ b/app/scripts/lib/contracts/registrar.js @@ -0,0 +1 @@ +module.exports = [{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"name":"","type":"uint64"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"}] \ No newline at end of file diff --git a/app/scripts/lib/contracts/resolver.js b/app/scripts/lib/contracts/resolver.js new file mode 100644 index 000000000..f42777cc7 --- /dev/null +++ b/app/scripts/lib/contracts/resolver.js @@ -0,0 +1,2 @@ +module.exports = +[{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"name","outputs":[{"name":"ret","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"x","type":"bytes32"},{"indexed":false,"name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"}] \ No newline at end of file diff --git a/app/scripts/lib/portalnetwork.js b/app/scripts/lib/portalnetwork.js new file mode 100644 index 000000000..5ff4f5ecf --- /dev/null +++ b/app/scripts/lib/portalnetwork.js @@ -0,0 +1,33 @@ +const extension = require('extensionizer') +const resolver = require('./resolver.js'); +module.exports = function (provider) { + extension.webRequest.onBeforeRequest.addListener(details => { + let name = details.url.substring(7, details.url.length - 1); + extension.tabs.getSelected(null, tab => { + extension.tabs.update(tab.id, { url: "loading.html" }); + + setTimeout(() => { + return extension.tabs.update(tab.id, { url: "404.html" }); + }, 60000); + + resolver.resolve(name, provider).then(ipfsHash => { + let url = "https://gateway.ipfs.io/ipfs/" + ipfsHash; + return fetch(url, {method: "HEAD"}).then(response => response.status).then(statusCode => { + if (statusCode !== 200) return "Local" + extension.tabs.update(tab.id, { url: url }) + }) + .catch(err => { + url = "https://gateway.ipfs.io/ipfs/" + ipfsHash + extension.tabs.update(tab.id, {url: url}) + return err + }) + }) + .catch(err => { + let nameWithoutTld = name.substring(0, name.lastIndexOf('.')) + let url = err === "no_mainnet" ? "no_mainnet" : "error" + extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) + }) + }) + return { cancel: true } + }, {urls: ["*://*.eth/"]}) +} \ No newline at end of file diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js new file mode 100644 index 000000000..43ccec0cc --- /dev/null +++ b/app/scripts/lib/resolver.js @@ -0,0 +1,46 @@ +const namehash = require('eth-ens-namehash') +const multihash = require('multihashes') +const REGISTRAR_ENS_MAIN_NET = "0x314159265dd8dbb310642f98f50c066173c1259b" +const HttpProvider = require('ethjs-provider-http') +const Eth = require('ethjs-query') +const EthContract = require('ethjs-contract') +const registrarAbi = require('./contracts/registrar') +const resolverAbi = require('./contracts/resolver') +function ens(name, provider) { + // provider need mainnet + let eth = new Eth(new HttpProvider(provider.rpcTarget)) + let hash = namehash.hash(name) + let contract = new EthContract(eth) + let Registrar = contract(registrarAbi).at(REGISTRAR_ENS_MAIN_NET) + return new Promise((resolve, reject) => { + if (provider.type !== "mainnet") reject('no_mainnet') + Registrar.resolver(hash).then((address) => { + if (address === '0x0000000000000000000000000000000000000000') { + reject(null) + } else { + let Resolver = contract(resolverAbi).at(address["0"]) + return Resolver.content(hash) + } + }).then((contentHash) => { + if (contentHash["0"] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null) + if (contentHash.ret !== "0x") { + let hex = contentHash["0"].substring(2) + let buf = multihash.fromHexString(hex) + resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256'))) + } else { + reject('fisk') + } + }) + }) +} +module.exports.resolve = function (name, provider) { + let path = name.split("."); + let tld = path[path.length - 1]; + if (tld === 'eth') { + return ens(name, provider); + } else { + return new Promise((resolve, reject) => { + reject(null) + }) + } +} -- cgit v1.2.3 From 88a6b4edc79a27e9d94dc4ec25e9de02153abc5d Mon Sep 17 00:00:00 2001 From: MikeCheng1208 Date: Mon, 7 May 2018 18:19:17 +0800 Subject: setTimeout bug fix --- app/scripts/lib/portalnetwork.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/portalnetwork.js b/app/scripts/lib/portalnetwork.js index 5ff4f5ecf..960871ebc 100644 --- a/app/scripts/lib/portalnetwork.js +++ b/app/scripts/lib/portalnetwork.js @@ -3,16 +3,18 @@ const resolver = require('./resolver.js'); module.exports = function (provider) { extension.webRequest.onBeforeRequest.addListener(details => { let name = details.url.substring(7, details.url.length - 1); + let clearTime = null; extension.tabs.getSelected(null, tab => { extension.tabs.update(tab.id, { url: "loading.html" }); - setTimeout(() => { + clearTime = setTimeout(() => { return extension.tabs.update(tab.id, { url: "404.html" }); }, 60000); resolver.resolve(name, provider).then(ipfsHash => { + clearTimeout(clearTime); let url = "https://gateway.ipfs.io/ipfs/" + ipfsHash; - return fetch(url, {method: "HEAD"}).then(response => response.status).then(statusCode => { + return fetch(url, { method: "HEAD" }).then(response => response.status).then(statusCode => { if (statusCode !== 200) return "Local" extension.tabs.update(tab.id, { url: url }) }) @@ -23,6 +25,7 @@ module.exports = function (provider) { }) }) .catch(err => { + clearTimeout(clearTime); let nameWithoutTld = name.substring(0, name.lastIndexOf('.')) let url = err === "no_mainnet" ? "no_mainnet" : "error" extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) -- cgit v1.2.3 From 8b8cc94f6f8cb949ee214e814d9d8949d17ddd16 Mon Sep 17 00:00:00 2001 From: MikeCheng1208 Date: Fri, 4 May 2018 19:57:19 +0800 Subject: Intergrate ENS with IPFS --- app/manifest.json | 4 ++++ app/scripts/lib/portalnetwork.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/manifest.json b/app/manifest.json index 0f5a2566f..ad500e40a 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -74,4 +74,8 @@ ] }, "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" +<<<<<<< HEAD } +======= +} +>>>>>>> Intergrate ENS with IPFS diff --git a/app/scripts/lib/portalnetwork.js b/app/scripts/lib/portalnetwork.js index 960871ebc..977f17926 100644 --- a/app/scripts/lib/portalnetwork.js +++ b/app/scripts/lib/portalnetwork.js @@ -33,4 +33,4 @@ module.exports = function (provider) { }) return { cancel: true } }, {urls: ["*://*.eth/"]}) -} \ No newline at end of file +} -- cgit v1.2.3 From 86ead431c3e07eff3ff9deae42ab0994ca998229 Mon Sep 17 00:00:00 2001 From: Yung chieh Tsai Date: Thu, 24 May 2018 22:37:41 +0700 Subject: Update manifest.json --- app/manifest.json | 4 ---- 1 file changed, 4 deletions(-) (limited to 'app') diff --git a/app/manifest.json b/app/manifest.json index ad500e40a..0f5a2566f 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -74,8 +74,4 @@ ] }, "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" -<<<<<<< HEAD } -======= -} ->>>>>>> Intergrate ENS with IPFS -- cgit v1.2.3 From 829deacb57a23ec8027269c93cdef3f3735d1710 Mon Sep 17 00:00:00 2001 From: Yung chieh Tsai Date: Thu, 24 May 2018 23:08:02 +0700 Subject: Rename files --- app/404.html | 2 +- app/error.html | 2 +- app/loading.html | 2 +- app/no_mainnet.html | 59 ---------------------------------------- app/scripts/background.js | 8 ++---- app/scripts/lib/ipfsContent.js | 36 ++++++++++++++++++++++++ app/scripts/lib/portalnetwork.js | 36 ------------------------ app/scripts/lib/resolver.js | 37 +++++++++++++------------ app/unsupport.html | 59 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 119 insertions(+), 122 deletions(-) delete mode 100644 app/no_mainnet.html create mode 100644 app/scripts/lib/ipfsContent.js delete mode 100644 app/scripts/lib/portalnetwork.js create mode 100644 app/unsupport.html (limited to 'app') diff --git a/app/404.html b/app/404.html index 2fbf1e311..0dbab69a7 100644 --- a/app/404.html +++ b/app/404.html @@ -1,6 +1,6 @@ - 404 + MetaMask - -
- -

ENS resolver only support on Ethereum mainnet

-
- - \ No newline at end of file diff --git a/app/scripts/background.js b/app/scripts/background.js index fc2ad5773..672a2dcac 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -25,7 +25,7 @@ const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics') const EdgeEncryptor = require('./edge-encryptor') const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code') const getObjStructure = require('./lib/getObjStructure') -const pw = require('./lib/portalnetwork.js') +const ipfsContent = require('./lib/ipfsContent.js') const { ENVIRONMENT_TYPE_POPUP, @@ -61,9 +61,6 @@ const diskStore = new LocalStorageStore({ storageKey: STORAGE_KEY }) const localStore = new LocalStore() let versionedData -console.log('localStore', localStore); -console.log('diskStore', diskStore); - // initialization flow initialize().catch(log.error) @@ -161,8 +158,7 @@ async function initialize () { const initLangCode = await getFirstPreferredLangCode() await setupController(initState, initLangCode) log.debug('MetaMask initialization complete.') - // porto network init - pw(initState.NetworkController.provider); + ipfsContent(initState.NetworkController.provider) } // diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js new file mode 100644 index 000000000..4d66745e9 --- /dev/null +++ b/app/scripts/lib/ipfsContent.js @@ -0,0 +1,36 @@ +const extension = require('extensionizer') +const resolver = require('./resolver.js') + +module.exports = function (provider) { + extension.webRequest.onBeforeRequest.addListener(details => { + const name = details.url.substring(7, details.url.length - 1) + let clearTime = null + extension.tabs.getSelected(null, tab => { + extension.tabs.update(tab.id, { url: 'loading.html' }) + + clearTime = setTimeout(() => { + return extension.tabs.update(tab.id, { url: '404.html' }) + }, 60000) + + resolver.resolve(name, provider).then(ipfsHash => { + clearTimeout(clearTime) + let url = 'https://gateway.ipfs.io/ipfs/' + ipfsHash + return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { + if (statusCode !== 200) return 'Local' + extension.tabs.update(tab.id, { url: url }) + }) + .catch(err => { + url = 'https://gateway.ipfs.io/ipfs/' + ipfsHash + extension.tabs.update(tab.id, {url: url}) + return err + }) + }) + .catch(err => { + clearTimeout(clearTime) + const url = err === 'unsupport' ? 'unsupport' : 'error' + extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) + }) + }) + return { cancel: true } + }, {urls: ['*://*.eth/']}) +} diff --git a/app/scripts/lib/portalnetwork.js b/app/scripts/lib/portalnetwork.js deleted file mode 100644 index 977f17926..000000000 --- a/app/scripts/lib/portalnetwork.js +++ /dev/null @@ -1,36 +0,0 @@ -const extension = require('extensionizer') -const resolver = require('./resolver.js'); -module.exports = function (provider) { - extension.webRequest.onBeforeRequest.addListener(details => { - let name = details.url.substring(7, details.url.length - 1); - let clearTime = null; - extension.tabs.getSelected(null, tab => { - extension.tabs.update(tab.id, { url: "loading.html" }); - - clearTime = setTimeout(() => { - return extension.tabs.update(tab.id, { url: "404.html" }); - }, 60000); - - resolver.resolve(name, provider).then(ipfsHash => { - clearTimeout(clearTime); - let url = "https://gateway.ipfs.io/ipfs/" + ipfsHash; - return fetch(url, { method: "HEAD" }).then(response => response.status).then(statusCode => { - if (statusCode !== 200) return "Local" - extension.tabs.update(tab.id, { url: url }) - }) - .catch(err => { - url = "https://gateway.ipfs.io/ipfs/" + ipfsHash - extension.tabs.update(tab.id, {url: url}) - return err - }) - }) - .catch(err => { - clearTimeout(clearTime); - let nameWithoutTld = name.substring(0, name.lastIndexOf('.')) - let url = err === "no_mainnet" ? "no_mainnet" : "error" - extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) - }) - }) - return { cancel: true } - }, {urls: ["*://*.eth/"]}) -} diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js index 43ccec0cc..dec43c481 100644 --- a/app/scripts/lib/resolver.js +++ b/app/scripts/lib/resolver.js @@ -1,43 +1,44 @@ const namehash = require('eth-ens-namehash') const multihash = require('multihashes') -const REGISTRAR_ENS_MAIN_NET = "0x314159265dd8dbb310642f98f50c066173c1259b" +const REGISTRAR_ENS_MAIN_NET = '0x314159265dd8dbb310642f98f50c066173c1259b' const HttpProvider = require('ethjs-provider-http') const Eth = require('ethjs-query') const EthContract = require('ethjs-contract') const registrarAbi = require('./contracts/registrar') const resolverAbi = require('./contracts/resolver') -function ens(name, provider) { - // provider need mainnet - let eth = new Eth(new HttpProvider(provider.rpcTarget)) - let hash = namehash.hash(name) - let contract = new EthContract(eth) - let Registrar = contract(registrarAbi).at(REGISTRAR_ENS_MAIN_NET) + +function ens (name, provider) { + const eth = new Eth(new HttpProvider(provider.rpcTarget)) + const hash = namehash.hash(name) + const contract = new EthContract(eth) + const Registrar = contract(registrarAbi).at(REGISTRAR_ENS_MAIN_NET) return new Promise((resolve, reject) => { - if (provider.type !== "mainnet") reject('no_mainnet') + if (provider.type !== 'mainnet') reject('unsupport') Registrar.resolver(hash).then((address) => { if (address === '0x0000000000000000000000000000000000000000') { reject(null) } else { - let Resolver = contract(resolverAbi).at(address["0"]) + const Resolver = contract(resolverAbi).at(address['0']) return Resolver.content(hash) } }).then((contentHash) => { - if (contentHash["0"] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null) - if (contentHash.ret !== "0x") { - let hex = contentHash["0"].substring(2) - let buf = multihash.fromHexString(hex) - resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256'))) + if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null) + if (contentHash.ret !== '0x') { + const hex = contentHash['0'].substring(2) + const buf = multihash.fromHexString(hex) + resolve(multihash.toB58String(buf)) } else { - reject('fisk') + reject(null) } }) }) } + module.exports.resolve = function (name, provider) { - let path = name.split("."); - let tld = path[path.length - 1]; + const path = name.split('.') + const tld = path[path.length - 1] if (tld === 'eth') { - return ens(name, provider); + return ens(name, provider) } else { return new Promise((resolve, reject) => { reject(null) diff --git a/app/unsupport.html b/app/unsupport.html new file mode 100644 index 000000000..6f514eb17 --- /dev/null +++ b/app/unsupport.html @@ -0,0 +1,59 @@ + + + + + MetaMask + + + +
+ +

ENS resolver only support on Ethereum mainnet

+
+ + \ No newline at end of file -- cgit v1.2.3 From 384cb126dd3cdf0d1dcf67525d7a390436ec6ea2 Mon Sep 17 00:00:00 2001 From: Yung chieh Tsai Date: Tue, 29 May 2018 19:29:12 +0800 Subject: Update lib --- app/404.html | 26 ++++++++++++++++- app/scripts/lib/ipfsContent.js | 2 +- app/scripts/lib/resolver.js | 66 ++++++++++++++++++++++++++++-------------- 3 files changed, 71 insertions(+), 23 deletions(-) (limited to 'app') diff --git a/app/404.html b/app/404.html index 0dbab69a7..f66723ad6 100644 --- a/app/404.html +++ b/app/404.html @@ -17,12 +17,36 @@ width: 100%; height: 100%; } + .app{ + position: relative; + width: 100%; + height: auto; + overflow: hidden; + } img{ display: block; + width: 100%; + height: auto; + } + h2{ + display: block; + width: 100%; + overflow: hidden; + position: absolute; + bottom: 20%; + left: 0; + color: #1b243d; + text-align: center; + } + h2 > a{ + color: #1b243d; } - +
+ +

Powered by Portal Network

+
\ No newline at end of file diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index 4d66745e9..bf04c854b 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -16,7 +16,7 @@ module.exports = function (provider) { clearTimeout(clearTime) let url = 'https://gateway.ipfs.io/ipfs/' + ipfsHash return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { - if (statusCode !== 200) return 'Local' + if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' }) extension.tabs.update(tab.id, { url: url }) }) .catch(err => { diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js index dec43c481..2bf9dac50 100644 --- a/app/scripts/lib/resolver.js +++ b/app/scripts/lib/resolver.js @@ -1,6 +1,5 @@ const namehash = require('eth-ens-namehash') const multihash = require('multihashes') -const REGISTRAR_ENS_MAIN_NET = '0x314159265dd8dbb310642f98f50c066173c1259b' const HttpProvider = require('ethjs-provider-http') const Eth = require('ethjs-query') const EthContract = require('ethjs-contract') @@ -8,32 +7,57 @@ const registrarAbi = require('./contracts/registrar') const resolverAbi = require('./contracts/resolver') function ens (name, provider) { - const eth = new Eth(new HttpProvider(provider.rpcTarget)) + const eth = new Eth(new HttpProvider(getProvider(provider.type))) const hash = namehash.hash(name) const contract = new EthContract(eth) - const Registrar = contract(registrarAbi).at(REGISTRAR_ENS_MAIN_NET) + const Registrar = contract(registrarAbi).at(getRegistrar(provider.type)) return new Promise((resolve, reject) => { - if (provider.type !== 'mainnet') reject('unsupport') - Registrar.resolver(hash).then((address) => { - if (address === '0x0000000000000000000000000000000000000000') { - reject(null) - } else { - const Resolver = contract(resolverAbi).at(address['0']) - return Resolver.content(hash) - } - }).then((contentHash) => { - if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null) - if (contentHash.ret !== '0x') { - const hex = contentHash['0'].substring(2) - const buf = multihash.fromHexString(hex) - resolve(multihash.toB58String(buf)) - } else { - reject(null) - } - }) + if (provider.type === 'mainnet' || provider.type === 'ropsten') { + Registrar.resolver(hash).then((address) => { + if (address === '0x0000000000000000000000000000000000000000') { + reject(null) + } else { + const Resolver = contract(resolverAbi).at(address['0']) + return Resolver.content(hash) + } + }).then((contentHash) => { + if (contentHash['0'] === '0x0000000000000000000000000000000000000000000000000000000000000000') reject(null) + if (contentHash.ret !== '0x') { + const hex = contentHash['0'].substring(2) + const buf = multihash.fromHexString(hex) + resolve(multihash.toB58String(multihash.encode(buf, 'sha2-256'))) + } else { + reject(null) + } + }) + } else { + return reject('unsupport') + } }) } +function getProvider (type) { + switch (type) { + case 'mainnet': + return 'https://mainnet.infura.io/' + case 'ropsten': + return 'https://ropsten.infura.io/' + default: + return 'http://localhost:3000/' + } +} + +function getRegistrar (type) { + switch (type) { + case 'mainnet': + return '0x314159265dd8dbb310642f98f50c066173c1259b' + case 'ropsten': + return '0x112234455c3a32fd11230c42e7bccd4a84e02010' + default: + return '0x0000000000000000000000000000000000000000' + } +} + module.exports.resolve = function (name, provider) { const path = name.split('.') const tld = path[path.length - 1] -- cgit v1.2.3 From 181a11eae2c4059a7c3f0f15bb96630c0761ae7b Mon Sep 17 00:00:00 2001 From: Yung chieh Tsai Date: Wed, 30 May 2018 09:57:31 +0800 Subject: Update default provider --- app/scripts/lib/resolver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js index 2bf9dac50..6786929d8 100644 --- a/app/scripts/lib/resolver.js +++ b/app/scripts/lib/resolver.js @@ -43,7 +43,7 @@ function getProvider (type) { case 'ropsten': return 'https://ropsten.infura.io/' default: - return 'http://localhost:3000/' + return 'http://localhost:8545/' } } -- cgit v1.2.3 From 4e14313ba8a72648be810f3e78d6e5849b363d11 Mon Sep 17 00:00:00 2001 From: Yung chieh Tsai Date: Fri, 1 Jun 2018 11:26:44 +0800 Subject: Update manifest.json --- app/manifest.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/manifest.json b/app/manifest.json index 0f5a2566f..1a4114a1b 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_appName__", "short_name": "__MSG_appName__", - "version": "4.6.2", + "version": "4.6.0", "manifest_version": 2, "author": "https://metamask.io", "description": "__MSG_appDescription__", @@ -65,13 +65,12 @@ "*://*.eth/" ], "web_accessible_resources": [ - "inpage.js", - "*" + "inpage.js" ], "externally_connectable": { "matches": [ "https://metamask.io/*" ] }, - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" + "content_security_policy": "script-src 'self'; object-src 'self'" } -- cgit v1.2.3 From 77d17f2d55a3e3ed704324020f6ec4b42cc4dde2 Mon Sep 17 00:00:00 2001 From: Yung chieh Tsai Date: Fri, 1 Jun 2018 11:27:05 +0800 Subject: Update ipfs to infura --- app/scripts/lib/ipfsContent.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index bf04c854b..46131b266 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -14,13 +14,13 @@ module.exports = function (provider) { resolver.resolve(name, provider).then(ipfsHash => { clearTimeout(clearTime) - let url = 'https://gateway.ipfs.io/ipfs/' + ipfsHash + let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' }) extension.tabs.update(tab.id, { url: url }) }) .catch(err => { - url = 'https://gateway.ipfs.io/ipfs/' + ipfsHash + url = 'https://ipfs.infura.io/ipfs/' + ipfsHash extension.tabs.update(tab.id, {url: url}) return err }) -- cgit v1.2.3 From cc5bdadbf1f12f3f8d7f24f9bcf7ea81fe93f012 Mon Sep 17 00:00:00 2001 From: Yung chieh Tsai Date: Fri, 1 Jun 2018 15:30:57 +0800 Subject: Update 404 --- app/404.html | 1 - 1 file changed, 1 deletion(-) (limited to 'app') diff --git a/app/404.html b/app/404.html index f66723ad6..5a1585318 100644 --- a/app/404.html +++ b/app/404.html @@ -46,7 +46,6 @@
-

Powered by Portal Network

\ No newline at end of file -- cgit v1.2.3 From 8a571ece367f089e9cfef45fed925736c5858e82 Mon Sep 17 00:00:00 2001 From: PhyrexTsai Date: Sat, 2 Jun 2018 00:00:11 +0800 Subject: Update 404.html --- app/404.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/404.html b/app/404.html index 5a1585318..8a6df9d7a 100644 --- a/app/404.html +++ b/app/404.html @@ -46,6 +46,7 @@
+

Powered by Portal Network

- \ No newline at end of file + -- cgit v1.2.3 From 8c4d58aa4508e3d54c3f69847347e78d09c63b97 Mon Sep 17 00:00:00 2001 From: Bruno Date: Sun, 10 Jun 2018 03:52:32 -0400 Subject: initial trezor support --- app/_locales/en/messages.json | 9 + app/images/connect-icon.svg | 11 + app/scripts/lib/trezor-connect.js | 1138 ++++++++++++++++++++++++++++++++++++ app/scripts/lib/trezorKeyring.js | 255 ++++++++ app/scripts/metamask-controller.js | 63 +- 5 files changed, 1475 insertions(+), 1 deletion(-) create mode 100644 app/images/connect-icon.svg create mode 100644 app/scripts/lib/trezor-connect.js create mode 100644 app/scripts/lib/trezorKeyring.js (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 457c3c3b1..339dd8da2 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -119,6 +119,12 @@ "confirmTransaction": { "message": "Confirm Transaction" }, + "connectHardware": { + "message": "Connect Hardware" + }, + "connect": { + "message": "Connect" + }, "continue": { "message": "Continue" }, @@ -930,6 +936,9 @@ "unknownNetworkId": { "message": "Unknown network ID" }, + "unlock": { + "message": "Unlock" + }, "unlockMessage": { "message": "The decentralized web awaits" }, diff --git a/app/images/connect-icon.svg b/app/images/connect-icon.svg new file mode 100644 index 000000000..84540999a --- /dev/null +++ b/app/images/connect-icon.svg @@ -0,0 +1,11 @@ + + + + background + + + + Layer 1 + + + \ No newline at end of file diff --git a/app/scripts/lib/trezor-connect.js b/app/scripts/lib/trezor-connect.js new file mode 100644 index 000000000..574e88104 --- /dev/null +++ b/app/scripts/lib/trezor-connect.js @@ -0,0 +1,1138 @@ +/* eslint-disable */ +/* prettier-ignore */ + +/** + * (C) 2017 SatoshiLabs + * + * GPLv3 + */ +var TREZOR_CONNECT_VERSION = 4; + +if (!Array.isArray) { + Array.isArray = function(arg) { + return Object.prototype.toString.call(arg) === "[object Array]"; + }; +} + +var HD_HARDENED = 0x80000000; + +// react sometimes adds some other parameters that should not be there +function _fwStrFix(obj, fw) { + if (typeof fw === "string") { + obj.requiredFirmware = fw; + } + return obj; +} + +("use strict"); + +var chrome = window.chrome; +var IS_CHROME_APP = chrome && chrome.app && chrome.app.window; + +var ERR_TIMED_OUT = "Loading timed out"; +var ERR_WINDOW_CLOSED = "Window closed"; +var ERR_WINDOW_BLOCKED = "Window blocked"; +var ERR_ALREADY_WAITING = "Already waiting for a response"; +var ERR_CHROME_NOT_CONNECTED = "Internal Chrome popup is not responding."; + +var DISABLE_LOGIN_BUTTONS = window.TREZOR_DISABLE_LOGIN_BUTTONS || false; +var CHROME_URL = window.TREZOR_CHROME_URL || "./chrome/wrapper.html"; +var POPUP_ORIGIN = window.TREZOR_POPUP_ORIGIN || "https://connect.trezor.io"; +var POPUP_PATH = + window.TREZOR_POPUP_PATH || POPUP_ORIGIN + "/" + TREZOR_CONNECT_VERSION; +var POPUP_URL = + window.TREZOR_POPUP_URL || + POPUP_PATH + "/popup/popup.html?v=" + new Date().getTime(); + +var POPUP_INIT_TIMEOUT = 15000; + +/** + * Public API. + */ +function TrezorConnect() { + var manager = new PopupManager(); + + /** + * Popup errors. + */ + this.ERR_TIMED_OUT = ERR_TIMED_OUT; + this.ERR_WINDOW_CLOSED = ERR_WINDOW_CLOSED; + this.ERR_WINDOW_BLOCKED = ERR_WINDOW_BLOCKED; + this.ERR_ALREADY_WAITING = ERR_ALREADY_WAITING; + this.ERR_CHROME_NOT_CONNECTED = ERR_CHROME_NOT_CONNECTED; + + /** + * Open the popup for further communication. All API functions open the + * popup automatically, but if you need to generate some parameters + * asynchronously, use `open` first to avoid popup blockers. + * @param {function(?Error)} callback + */ + this.open = function(callback) { + var onchannel = function(result) { + if (result instanceof Error) { + callback(result); + } else { + callback(); + } + }; + manager.waitForChannel(onchannel); + }; + + /** + * Close the opened popup, if any. + */ + this.close = function() { + manager.close(); + }; + + /** + * Enable or disable closing the opened popup after a successful call. + * @param {boolean} value + */ + this.closeAfterSuccess = function(value) { + manager.closeAfterSuccess = value; + }; + + /** + * Enable or disable closing the opened popup after a failed call. + * @param {boolean} value + */ + this.closeAfterFailure = function(value) { + manager.closeAfterFailure = value; + }; + + /** + * Set bitcore server + * @param {string|Array} value + */ + this.setBitcoreURLS = function(value) { + if (typeof value === "string") { + manager.bitcoreURLS = [value]; + } else if (value instanceof Array) { + manager.bitcoreURLS = value; + } + }; + + /** + * Set currency. Human readable coin name + * @param {string|Array} value + */ + this.setCurrency = function(value) { + if (typeof value === "string") { + manager.currency = value; + } + }; + + /** + * Set currency units (mBTC, BTC) + * @param {string|Array} value + */ + this.setCurrencyUnits = function(value) { + if (typeof value === "string") { + manager.currencyUnits = value; + } + }; + + /** + * Set coin info json url + * @param {string|Array} value + */ + this.setCoinInfoURL = function(value) { + if (typeof value === "string") { + manager.coinInfoURL = value; + } + }; + + /** + * Set max. limit for account discovery + * @param {number} value + */ + this.setAccountDiscoveryLimit = function(value) { + if (!isNaN(value)) manager.accountDiscoveryLimit = value; + }; + + /** + * Set max. gap for account discovery + * @param {number} value + */ + this.setAccountDiscoveryGapLength = function(value) { + if (!isNaN(value)) manager.accountDiscoveryGapLength = value; + }; + + /** + * Set discovery BIP44 coin type + * @param {number} value + */ + this.setAccountDiscoveryBip44CoinType = function(value) { + if (!isNaN(value)) manager.accountDiscoveryBip44CoinType = value; + }; + + /** + * @typedef XPubKeyResult + * @param {boolean} success + * @param {?string} error + * @param {?string} xpubkey serialized extended public key + * @param {?string} path BIP32 serializd path of the key + */ + + /** + * Load BIP32 extended public key by path. + * + * Path can be specified either in the string form ("m/44'/1/0") or as + * raw integer array. In case you omit the path, user is asked to select + * a BIP32 account to export, and the result contains m/44'/0'/x' node + * of the account. + * + * @param {?(string|array)} path + * @param {function(XPubKeyResult)} callback + * @param {?(string|array)} requiredFirmware + */ + this.getXPubKey = function(path, callback, requiredFirmware) { + if (typeof path === "string") { + path = parseHDPath(path); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "xpubkey", + path: path + }, + requiredFirmware + ), + callback + ); + }; + + this.getFreshAddress = function(callback, requiredFirmware) { + var wrapperCallback = function(result) { + if (result.success) { + callback({ success: true, address: result.freshAddress }); + } else { + callback(result); + } + }; + + manager.sendWithChannel( + _fwStrFix( + { + type: "accountinfo" + }, + requiredFirmware + ), + wrapperCallback + ); + }; + + this.getAccountInfo = function(input, callback, requiredFirmware) { + try { + manager.sendWithChannel( + _fwStrFix( + { + type: "accountinfo", + description: input + }, + requiredFirmware + ), + callback + ); + } catch (e) { + callback({ success: false, error: e }); + } + }; + + this.getAllAccountsInfo = function(callback, requiredFirmware) { + try { + manager.sendWithChannel( + _fwStrFix( + { + type: "allaccountsinfo", + description: "all" + }, + requiredFirmware + ), + callback + ); + } catch (e) { + callback({ success: false, error: e }); + } + }; + + this.getBalance = function(callback, requiredFirmware) { + manager.sendWithChannel( + _fwStrFix( + { + type: "accountinfo" + }, + requiredFirmware + ), + callback + ); + }; + + /** + * @typedef SignTxResult + * @param {boolean} success + * @param {?string} error + * @param {?string} serialized_tx serialized tx, in hex, including signatures + * @param {?array} signatures array of input signatures, in hex + */ + + /** + * Sign a transaction in the device and return both serialized + * transaction and the signatures. + * + * @param {array} inputs + * @param {array} outputs + * @param {function(SignTxResult)} callback + * @param {?(string|array)} requiredFirmware + * + * @see https://github.com/trezor/trezor-common/blob/master/protob/types.proto + */ + this.signTx = function(inputs, outputs, callback, requiredFirmware, coin) { + manager.sendWithChannel( + _fwStrFix( + { + type: "signtx", + inputs: inputs, + outputs: outputs, + coin: coin + }, + requiredFirmware + ), + callback + ); + }; + + // new implementation with ethereum at beginnig + this.ethereumSignTx = function() { + this.signEthereumTx.apply(this, arguments); + }; + + // old fallback + this.signEthereumTx = function( + address_n, + nonce, + gas_price, + gas_limit, + to, + value, + data, + chain_id, + callback, + requiredFirmware + ) { + if (requiredFirmware == null) { + requiredFirmware = "1.4.0"; // first firmware that supports ethereum + } + if (typeof address_n === "string") { + address_n = parseHDPath(address_n); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "signethtx", + address_n: address_n, + nonce: nonce, + gas_price: gas_price, + gas_limit: gas_limit, + to: to, + value: value, + data: data, + chain_id: chain_id + }, + requiredFirmware + ), + callback + ); + }; + + /** + * @typedef TxRecipient + * @param {number} amount the amount to send, in satoshis + * @param {string} address the address of the recipient + */ + + /** + * Compose a transaction by doing BIP-0044 discovery, letting the user + * select an account, and picking UTXO by internal preferences. + * Transaction is then signed and returned in the same format as + * `signTx`. Only supports BIP-0044 accounts (single-signature). + * + * @param {array} recipients + * @param {function(SignTxResult)} callback + * @param {?(string|array)} requiredFirmware + */ + this.composeAndSignTx = function(recipients, callback, requiredFirmware) { + manager.sendWithChannel( + _fwStrFix( + { + type: "composetx", + recipients: recipients + }, + requiredFirmware + ), + callback + ); + }; + + /** + * @typedef RequestLoginResult + * @param {boolean} success + * @param {?string} error + * @param {?string} public_key public key used for signing, in hex + * @param {?string} signature signature, in hex + */ + + /** + * Sign a login challenge for active origin. + * + * @param {?string} hosticon + * @param {string} challenge_hidden + * @param {string} challenge_visual + * @param {string|function(RequestLoginResult)} callback + * @param {?(string|array)} requiredFirmware + * + * @see https://github.com/trezor/trezor-common/blob/master/protob/messages.proto + */ + this.requestLogin = function( + hosticon, + challenge_hidden, + challenge_visual, + callback, + requiredFirmware + ) { + if (typeof callback === "string") { + // special case for a login through button. + // `callback` is name of global var + callback = window[callback]; + } + if (!callback) { + throw new TypeError("TrezorConnect: login callback not found"); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "login", + icon: hosticon, + challenge_hidden: challenge_hidden, + challenge_visual: challenge_visual + }, + requiredFirmware + ), + callback + ); + }; + + /** + * @typedef SignMessageResult + * @param {boolean} success + * @param {?string} error + * @param {?string} address address (in base58check) + * @param {?string} signature signature, in base64 + */ + + /** + * Sign a message + * + * @param {string|array} path + * @param {string} message to sign (ascii) + * @param {string|function(SignMessageResult)} callback + * @param {?string} opt_coin - (optional) name of coin (default Bitcoin) + * @param {?(string|array)} requiredFirmware + * + */ + this.signMessage = function( + path, + message, + callback, + opt_coin, + requiredFirmware + ) { + if (typeof path === "string") { + path = parseHDPath(path); + } + if (!opt_coin) { + opt_coin = "Bitcoin"; + } + if (!callback) { + throw new TypeError("TrezorConnect: callback not found"); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "signmsg", + path: path, + message: message, + coin: opt_coin + }, + requiredFirmware + ), + callback + ); + }; + + /** + * Sign an Ethereum message + * + * @param {string|array} path + * @param {string} message to sign (ascii) + * @param {string|function(SignMessageResult)} callback + * @param {?(string|array)} requiredFirmware + * + */ + this.ethereumSignMessage = function( + path, + message, + callback, + requiredFirmware + ) { + if (typeof path === "string") { + path = parseHDPath(path); + } + if (!callback) { + throw new TypeError("TrezorConnect: callback not found"); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "signethmsg", + path: path, + message: message + }, + requiredFirmware + ), + callback + ); + }; + + /** + * Verify message + * + * @param {string} address + * @param {string} signature (base64) + * @param {string} message (string) + * @param {string|function()} callback + * @param {?string} opt_coin - (optional) name of coin (default Bitcoin) + * @param {?(string|array)} requiredFirmware + * + */ + this.verifyMessage = function( + address, + signature, + message, + callback, + opt_coin, + requiredFirmware + ) { + if (!opt_coin) { + opt_coin = "Bitcoin"; + } + if (!callback) { + throw new TypeError("TrezorConnect: callback not found"); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "verifymsg", + address: address, + signature: signature, + message: message, + coin: { coin_name: opt_coin } + }, + requiredFirmware + ), + callback + ); + }; + + /** + * Verify ethereum message + * + * @param {string} address + * @param {string} signature (base64) + * @param {string} message (string) + * @param {string|function()} callback + * @param {?(string|array)} requiredFirmware + * + */ + this.ethereumVerifyMessage = function( + address, + signature, + message, + callback, + requiredFirmware + ) { + if (!callback) { + throw new TypeError("TrezorConnect: callback not found"); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "verifyethmsg", + address: address, + signature: signature, + message: message + }, + requiredFirmware + ), + callback + ); + }; + + /** + * Symmetric key-value encryption + * + * @param {string|array} path + * @param {string} key to show on device display + * @param {string} value hexadecimal value, length a multiple of 16 bytes + * @param {boolean} encrypt / decrypt direction + * @param {boolean} ask_on_encrypt (should user confirm on encrypt?) + * @param {boolean} ask_on_decrypt (should user confirm on decrypt?) + * @param {string|function()} callback + * @param {?(string|array)} requiredFirmware + * + */ + this.cipherKeyValue = function( + path, + key, + value, + encrypt, + ask_on_encrypt, + ask_on_decrypt, + callback, + requiredFirmware + ) { + if (typeof path === "string") { + path = parseHDPath(path); + } + if (typeof value !== "string") { + throw new TypeError("TrezorConnect: Value must be a string"); + } + if (!/^[0-9A-Fa-f]*$/.test(value)) { + throw new TypeError("TrezorConnect: Value must be hexadecimal"); + } + if (value.length % 32 !== 0) { + // 1 byte == 2 hex strings + throw new TypeError( + "TrezorConnect: Value length must be multiple of 16 bytes" + ); + } + if (!callback) { + throw new TypeError("TrezorConnect: callback not found"); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "cipherkeyvalue", + path: path, + key: key, + value: value, + encrypt: !!encrypt, + ask_on_encrypt: !!ask_on_encrypt, + ask_on_decrypt: !!ask_on_decrypt + }, + requiredFirmware + ), + callback + ); + }; + + this.nemGetAddress = function( + address_n, + network, + callback, + requiredFirmware + ) { + if (requiredFirmware == null) { + requiredFirmware = "1.6.0"; // first firmware that supports NEM + } + if (typeof address_n === "string") { + address_n = parseHDPath(address_n); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "nemGetAddress", + address_n: address_n, + network: network + }, + requiredFirmware + ), + callback + ); + }; + + this.nemSignTx = function( + address_n, + transaction, + callback, + requiredFirmware + ) { + if (requiredFirmware == null) { + requiredFirmware = "1.6.0"; // first firmware that supports NEM + } + if (typeof address_n === "string") { + address_n = parseHDPath(address_n); + } + manager.sendWithChannel( + _fwStrFix( + { + type: "nemSignTx", + address_n: address_n, + transaction: transaction + }, + requiredFirmware + ), + callback + ); + }; + + this.pushTransaction = function(rawTx, callback) { + if (!/^[0-9A-Fa-f]*$/.test(rawTx)) { + throw new TypeError("TrezorConnect: Transaction must be hexadecimal"); + } + if (!callback) { + throw new TypeError("TrezorConnect: callback not found"); + } + + manager.sendWithChannel( + { + type: "pushtx", + rawTx: rawTx + }, + callback + ); + }; + + /** + * Display address on device + * + * @param {array} address + * @param {string} coin + * @param {boolean} segwit + * @param {?(string|array)} requiredFirmware + * + */ + this.getAddress = function( + address, + coin, + segwit, + callback, + requiredFirmware + ) { + if (typeof address === "string") { + address = parseHDPath(address); + } + + manager.sendWithChannel( + _fwStrFix( + { + type: "getaddress", + address_n: address, + coin: coin, + segwit: segwit + }, + requiredFirmware + ), + callback + ); + }; + + /** + * Display ethereum address on device + * + * @param {array} address + * @param {?(string|array)} requiredFirmware + * + */ + this.ethereumGetAddress = function(address, callback, requiredFirmware) { + if (typeof address === "string") { + address = parseHDPath(address); + } + + manager.sendWithChannel( + _fwStrFix( + { + type: "ethgetaddress", + address_n: address + }, + requiredFirmware + ), + callback + ); + }; + + var LOGIN_CSS = + ''; + + var LOGIN_ONCLICK = + "TrezorConnect.requestLogin(" + + "'@hosticon@','@challenge_hidden@','@challenge_visual@','@callback@'" + + ")"; + + var LOGIN_HTML = + '
' + + ' ' + + ' ' + + ' @text@' + + " " + + ' ' + + ' What is TREZOR?' + + " " + + "
"; + + /** + * Find elements and replace them with login buttons. + * It's not required to use these special elements, feel free to call + * `TrezorConnect.requestLogin` directly. + */ + this.renderLoginButtons = function() { + var elements = document.getElementsByTagName("trezor:login"); + + for (var i = 0; i < elements.length; i++) { + var e = elements[i]; + var text = e.getAttribute("text") || "Sign in with TREZOR"; + var callback = e.getAttribute("callback") || ""; + var hosticon = e.getAttribute("icon") || ""; + var challenge_hidden = e.getAttribute("challenge_hidden") || ""; + var challenge_visual = e.getAttribute("challenge_visual") || ""; + + // it's not valid to put markup into attributes, so let users + // supply a raw text and make TREZOR bold + text = text.replace("TREZOR", "TREZOR"); + e.outerHTML = (LOGIN_CSS + LOGIN_HTML) + .replace("@text@", text) + .replace("@callback@", callback) + .replace("@hosticon@", hosticon) + .replace("@challenge_hidden@", challenge_hidden) + .replace("@challenge_visual@", challenge_visual) + .replace("@connect_path@", POPUP_PATH); + } + }; +} + +/* + * `getXPubKey()` + */ + +function parseHDPath(string) { + return string + .toLowerCase() + .split("/") + .filter(function(p) { + return p !== "m"; + }) + .map(function(p) { + var hardened = false; + if (p[p.length - 1] === "'") { + hardened = true; + p = p.substr(0, p.length - 1); + } + if (isNaN(p)) { + throw new Error("Not a valid path."); + } + var n = parseInt(p); + if (hardened) { + // hardened index + n = (n | 0x80000000) >>> 0; + } + return n; + }); +} + +/* + * Popup management + */ + +function ChromePopup(url, name, width, height) { + var left = (screen.width - width) / 2; + var top = (screen.height - height) / 2; + var opts = { + id: name, + innerBounds: { + width: width, + height: height, + left: left, + top: top + } + }; + + var closed = function() { + if (this.onclose) { + this.onclose(false); // never report as blocked + } + }.bind(this); + + var opened = function(w) { + this.window = w; + this.window.onClosed.addListener(closed); + }.bind(this); + + chrome.app.window.create(url, opts, opened); + + this.name = name; + this.window = null; + this.onclose = null; +} + +function ChromeChannel(popup, waiting) { + var port = null; + + var respond = function(data) { + if (waiting) { + var w = waiting; + waiting = null; + w(data); + } + }; + + var setup = function(p) { + if (p.name === popup.name) { + port = p; + port.onMessage.addListener(respond); + chrome.runtime.onConnect.removeListener(setup); + } + }; + + chrome.runtime.onConnect.addListener(setup); + + this.respond = respond; + + this.close = function() { + chrome.runtime.onConnect.removeListener(setup); + port.onMessage.removeListener(respond); + port.disconnect(); + port = null; + }; + + this.send = function(value, callback) { + if (waiting === null) { + waiting = callback; + + if (port) { + port.postMessage(value); + } else { + throw new Error(ERR_CHROME_NOT_CONNECTED); + } + } else { + throw new Error(ERR_ALREADY_WAITING); + } + }; +} + +function Popup(url, origin, name, width, height) { + var left = (screen.width - width) / 2; + var top = (screen.height - height) / 2; + var opts = + "width=" + + width + + ",height=" + + height + + ",left=" + + left + + ",top=" + + top + + ",menubar=no" + + ",toolbar=no" + + ",location=no" + + ",personalbar=no" + + ",status=no"; + var w = window.open(url, name, opts); + + var interval; + var blocked = w.closed; + var iterate = function() { + if (w.closed) { + clearInterval(interval); + if (this.onclose) { + this.onclose(blocked); + } + } + }.bind(this); + interval = setInterval(iterate, 100); + + this.window = w; + this.origin = origin; + this.onclose = null; +} + +function Channel(popup, waiting) { + var respond = function(data) { + if (waiting) { + var w = waiting; + waiting = null; + w(data); + } + }; + + var receive = function(event) { + var org1 = event.origin.match(/^.+\:\/\/[^\‌​/]+/)[0]; + var org2 = popup.origin.match(/^.+\:\/\/[^\‌​/]+/)[0]; + //if (event.source === popup.window && event.origin === popup.origin) { + if (event.source === popup.window && org1 === org2) { + respond(event.data); + } + }; + + window.addEventListener("message", receive); + + this.respond = respond; + + this.close = function() { + window.removeEventListener("message", receive); + }; + + this.send = function(value, callback) { + if (waiting === null) { + waiting = callback; + popup.window.postMessage(value, popup.origin); + } else { + throw new Error(ERR_ALREADY_WAITING); + } + }; +} + +function ConnectedChannel(p) { + var ready = function() { + clearTimeout(this.timeout); + this.popup.onclose = null; + this.ready = true; + this.onready(); + }.bind(this); + + var closed = function(blocked) { + clearTimeout(this.timeout); + this.channel.close(); + if (blocked) { + this.onerror(new Error(ERR_WINDOW_BLOCKED)); + } else { + this.onerror(new Error(ERR_WINDOW_CLOSED)); + } + }.bind(this); + + var timedout = function() { + this.popup.onclose = null; + if (this.popup.window) { + this.popup.window.close(); + } + this.channel.close(); + this.onerror(new Error(ERR_TIMED_OUT)); + }.bind(this); + + if (IS_CHROME_APP) { + this.popup = new ChromePopup(p.chromeUrl, p.name, p.width, p.height); + this.channel = new ChromeChannel(this.popup, ready); + } else { + this.popup = new Popup(p.url, p.origin, p.name, p.width, p.height); + this.channel = new Channel(this.popup, ready); + } + + this.timeout = setTimeout(timedout, POPUP_INIT_TIMEOUT); + + this.popup.onclose = closed; + + this.ready = false; + this.onready = null; + this.onerror = null; +} + +function PopupManager() { + var cc = null; + + var closed = function() { + cc.channel.respond(new Error(ERR_WINDOW_CLOSED)); + cc.channel.close(); + cc = null; + }; + + var open = function(callback) { + cc = new ConnectedChannel({ + name: "trezor-connect", + width: 600, + height: 500, + origin: POPUP_ORIGIN, + path: POPUP_PATH, + url: POPUP_URL, + chromeUrl: CHROME_URL + }); + cc.onready = function() { + cc.popup.onclose = closed; + callback(cc.channel); + }; + cc.onerror = function(error) { + cc = null; + callback(error); + }; + }.bind(this); + + this.closeAfterSuccess = true; + this.closeAfterFailure = true; + + this.close = function() { + if (cc && cc.popup.window) { + cc.popup.window.close(); + } + }; + + this.waitForChannel = function(callback) { + if (cc) { + if (cc.ready) { + callback(cc.channel); + } else { + callback(new Error(ERR_ALREADY_WAITING)); + } + } else { + try { + open(callback); + } catch (e) { + callback(new Error(ERR_WINDOW_BLOCKED)); + } + } + }; + + this.sendWithChannel = function(message, callback) { + message.bitcoreURLS = this.bitcoreURLS || null; + message.currency = this.currency || null; + message.currencyUnits = this.currencyUnits || null; + message.coinInfoURL = this.coinInfoURL || null; + message.accountDiscoveryLimit = this.accountDiscoveryLimit || null; + message.accountDiscoveryGapLength = this.accountDiscoveryGapLength || null; + message.accountDiscoveryBip44CoinType = + this.accountDiscoveryBip44CoinType || null; + + var respond = function(response) { + var succ = response.success && this.closeAfterSuccess; + var fail = !response.success && this.closeAfterFailure; + if (succ || fail) { + this.close(); + } + callback(response); + }.bind(this); + + var onresponse = function(response) { + if (response instanceof Error) { + var error = response; + respond({ success: false, error: error.message }); + } else { + respond(response); + } + }; + + var onchannel = function(channel) { + if (channel instanceof Error) { + var error = channel; + respond({ success: false, error: error.message }); + } else { + channel.send(message, onresponse); + } + }; + + this.waitForChannel(onchannel); + }; +} + +const connect = new TrezorConnect(); +module.exports = connect; \ No newline at end of file diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js new file mode 100644 index 000000000..d99f384f2 --- /dev/null +++ b/app/scripts/lib/trezorKeyring.js @@ -0,0 +1,255 @@ +const { EventEmitter } = require('events') +const ethUtil = require('ethereumjs-util') +// const sigUtil = require('eth-sig-util') +//const { Lock } = require('semaphore-async-await') + +const hdPathString = `m/44'/60'/0'/0` +const keyringType = 'Trezor Hardware Keyring' + +const TrezorConnect = require('./trezor-connect.js') +const HDKey = require('hdkey') +const TREZOR_FIRMWARE_VERSION = '1.4.0' +const log = require('loglevel') + +class TrezorKeyring extends EventEmitter { + constructor (opts = {}) { + super() + this.type = keyringType + //this.lock = new Lock() + this.accounts = [] + this.hdk = new HDKey() + this.deserialize(opts) + this.page = 0 + this.perPage = 5 + } + + serialize () { + return Promise.resolve({ hdPath: this.hdPath, accounts: this.accounts }) + } + + deserialize (opts = {}) { + this.hdPath = opts.hdPath || hdPathString + this.accounts = opts.accounts || [] + return Promise.resolve() + } + + unlock () { + if (this.hdk.publicKey) return Promise.resolve() + + return new Promise((resolve, reject) => { + TrezorConnect.getXPubKey( + this.hdPath, + response => { + log.debug('TREZOR CONNECT RESPONSE: ') + log.debug(response) + if (response.success) { + this.hdk.publicKey = new Buffer(response.publicKey, 'hex') + this.hdk.chainCode = new Buffer(response.chainCode, 'hex') + resolve() + } else { + reject(response.error || 'Unknown error') + } + }, + TREZOR_FIRMWARE_VERSION + ) + }) + } + + addAccounts (n = 1) { + return new Promise((resolve, reject) => { + return this.unlock() + .then(_ => { + const pathBase = 'm' + const from = n + const to = n + 1 + + this.accounts = [] + + for (let i = from; i < to; i++) { + const dkey = this.hdk.derive(`${pathBase}/${i}`) + const address = ethUtil + .publicToAddress(dkey.publicKey, true) + .toString('hex') + this.accounts.push(ethUtil.toChecksumAddress(address)) + this.page = 0 + } + resolve(this.accounts) + }) + .catch(e => { + reject(e) + }) + }) + } + + async getPage () { + return new Promise((resolve, reject) => { + return this.unlock() + .then(_ => { + const pathBase = 'm' + const from = this.page === 0 ? 0 : (this.page - 1) * this.perPage + const to = from + this.perPage + + const accounts = [] + + for (let i = from; i < to; i++) { + const dkey = this.hdk.derive(`${pathBase}/${i}`) + const address = ethUtil + .publicToAddress(dkey.publicKey, true) + .toString('hex') + accounts.push({ + address: ethUtil.toChecksumAddress(address), + balance: 0, + index: i, + }) + } + resolve(accounts) + }) + .catch(e => { + reject(e) + }) + }) + } + + async getPrevAccountSet () { + this.page-- + return await this.getPage() + } + + async getNextAccountSet () { + this.page++ + return await this.getPage() + } + + getAccounts () { + return Promise.resolve(this.accounts.slice()) + } + + // tx is an instance of the ethereumjs-transaction class. + async signTransaction (address, tx) { + throw new Error('Not supported on this device') + /* + await this.lock.acquire() + try { + + // Look before we leap + await this._checkCorrectTrezorAttached() + + let accountId = await this._findAddressId(address) + let eth = await this._getEth() + tx.v = tx._chainId + let TrezorSig = await eth.signTransaction( + this._derivePath(accountId), + tx.serialize().toString('hex') + ) + tx.v = parseInt(TrezorSig.v, 16) + tx.r = '0x' + TrezorSig.r + tx.s = '0x' + TrezorSig.s + + // Since look before we leap check is racy, also check that signature is for account expected + let addressSignedWith = ethUtil.bufferToHex(tx.getSenderAddress()) + if (addressSignedWith.toLowerCase() !== address.toLowerCase()) { + throw new Error( + `Signature is for ${addressSignedWith} but expected ${address} - is the correct Trezor device attached?` + ) + } + + return tx + + } finally { + await this.lock.release() + }*/ + } + + async signMessage (withAccount, data) { + throw new Error('Not supported on this device') + } + + // For personal_sign, we need to prefix the message: + async signPersonalMessage (withAccount, message) { + throw new Error('Not supported on this device') + /* + await this.lock.acquire() + try { + // Look before we leap + await this._checkCorrectTrezorAttached() + + let accountId = await this._findAddressId(withAccount) + let eth = await this._getEth() + let msgHex = ethUtil.stripHexPrefix(message) + let TrezorSig = await eth.signPersonalMessage( + this._derivePath(accountId), + msgHex + ) + let signature = this._personalToRawSig(TrezorSig) + + // Since look before we leap check is racy, also check that signature is for account expected + let addressSignedWith = sigUtil.recoverPersonalSignature({ + data: message, + sig: signature, + }) + if (addressSignedWith.toLowerCase() !== withAccount.toLowerCase()) { + throw new Error( + `Signature is for ${addressSignedWith} but expected ${withAccount} - is the correct Trezor device attached?` + ) + } + + return signature + + } finally { + await this.lock.release() + } */ + } + + async signTypedData (withAccount, typedData) { + throw new Error('Not supported on this device') + } + + async exportAccount (address) { + throw new Error('Not supported on this device') + } + + async _findAddressId (addr) { + const result = this.accounts.indexOf(addr) + if (result === -1) throw new Error('Unknown address') + else return result + } + + async _addressFromId (i) { + /* Must be called with lock acquired + const eth = await this._getEth() + return (await eth.getAddress(this._derivePath(i))).address*/ + const result = this.accounts[i] + if (!result) throw new Error('Unknown address') + else return result + } + + async _checkCorrectTrezorAttached () { + return true + /* Must be called with lock acquired + if (this.accounts.length > 0) { + const expectedFirstAccount = this.accounts[0] + let actualFirstAccount = await this._addressFromId(0) + if (expectedFirstAccount !== actualFirstAccount) { + throw new Error( + `Incorrect Trezor device attached - expected device containg account ${expectedFirstAccount}, but found ${actualFirstAccount}` + ) + } + }*/ + } + + _derivePath (i) { + return this.hdPath + '/' + i + } + + _personalToRawSig (TrezorSig) { + var v = TrezorSig['v'] - 27 + v = v.toString(16) + if (v.length < 2) { + v = '0' + v + } + return '0x' + TrezorSig['r'] + TrezorSig['s'] + v + } +} + +TrezorKeyring.type = keyringType +module.exports = TrezorKeyring \ No newline at end of file diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index a362e3826..dd5a5616f 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -48,6 +48,7 @@ const seedPhraseVerifier = require('./lib/seed-phrase-verifier') const cleanErrorStack = require('./lib/cleanErrorStack') const DiagnosticsReporter = require('./lib/diagnostics-reporter') const log = require('loglevel') +const TrezorKeyring = require("./lib/trezorKeyring"); module.exports = class MetamaskController extends EventEmitter { @@ -130,9 +131,11 @@ module.exports = class MetamaskController extends EventEmitter { provider: this.provider, blockTracker: this.blockTracker, }) - + // key mgmt + const additionalKeyrings = [TrezorKeyring] this.keyringController = new KeyringController({ + keyringTypes: additionalKeyrings, initState: initState.KeyringController, getNetwork: this.networkController.getNetworkState.bind(this.networkController), encryptor: opts.encryptor || undefined, @@ -363,6 +366,10 @@ module.exports = class MetamaskController extends EventEmitter { resetAccount: nodeify(this.resetAccount, this), importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this), + // trezor + connectHardware: nodeify(this.connectHardware, this), + unlockTrezorAccount: nodeify(this.unlockTrezorAccount, this), + // vault management submitPassword: nodeify(this.submitPassword, this), @@ -523,6 +530,60 @@ module.exports = class MetamaskController extends EventEmitter { this.preferencesController.setSelectedAddress(address) } + // + // Hardware + // + + /** + * Fetch account list from a trezor device. + * + * @returns [] accounts + */ + async connectHardware (deviceName, page) { + const keyringController = this.keyringController + const keyring = await keyringController.getKeyringsByType( + 'Trezor Hardware Keyring' + )[0] + if (!keyring) { + throw new Error('MetamaskController - No Trezor Hardware Keyring found') + } + + const accounts = page === -1 ? await keyring.getPrevAccountSet() : await keyring.getNextAccountSet() + + return accounts + + } + + /** + * Imports an account from a trezor device. + * + * @returns {} keyState + */ + async unlockTrezorAccount (index) { + const keyringController = this.keyringController + const keyring = await keyringController.getKeyringsByType( + 'Trezor Hardware Keyring' + )[0] + if (!keyring) { + throw new Error('MetamaskController - No Trezor Hardware Keyring found') + } + + const oldAccounts = await keyringController.getAccounts() + const keyState = await keyringController.addNewAccount(keyring) + const newAccounts = await keyringController.getAccounts() + + this.preferencesController.setAddresses(newAccounts) + newAccounts.forEach(address => { + if (!oldAccounts.includes(address)) { + this.preferencesController.setSelectedAddress(address) + } + }) + + const { identities } = this.preferencesController.store.getState() + return { ...keyState, identities } + } + + // // Account Management // -- cgit v1.2.3 From f5f66f59d7d215adf402a1e580c452e634480f69 Mon Sep 17 00:00:00 2001 From: Bruno Date: Sun, 10 Jun 2018 18:48:42 -0400 Subject: clean up --- app/scripts/lib/trezorKeyring.js | 16 +++++++++++----- app/scripts/metamask-controller.js | 7 ++++--- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js index d99f384f2..83787f3d2 100644 --- a/app/scripts/lib/trezorKeyring.js +++ b/app/scripts/lib/trezorKeyring.js @@ -1,10 +1,9 @@ const { EventEmitter } = require('events') const ethUtil = require('ethereumjs-util') // const sigUtil = require('eth-sig-util') -//const { Lock } = require('semaphore-async-await') const hdPathString = `m/44'/60'/0'/0` -const keyringType = 'Trezor Hardware Keyring' +const keyringType = 'Trezor Hardware' const TrezorConnect = require('./trezor-connect.js') const HDKey = require('hdkey') @@ -15,7 +14,6 @@ class TrezorKeyring extends EventEmitter { constructor (opts = {}) { super() this.type = keyringType - //this.lock = new Lock() this.accounts = [] this.hdk = new HDKey() this.deserialize(opts) @@ -24,16 +22,22 @@ class TrezorKeyring extends EventEmitter { } serialize () { - return Promise.resolve({ hdPath: this.hdPath, accounts: this.accounts }) + return Promise.resolve({ + hdPath: this.hdPath, + accounts: this.accounts, + page: this.page, + }) } deserialize (opts = {}) { this.hdPath = opts.hdPath || hdPathString this.accounts = opts.accounts || [] + this.page = opts.page || 0 return Promise.resolve() } unlock () { + if (this.hdk.publicKey) return Promise.resolve() return new Promise((resolve, reject) => { @@ -56,6 +60,7 @@ class TrezorKeyring extends EventEmitter { } addAccounts (n = 1) { + return new Promise((resolve, reject) => { return this.unlock() .then(_ => { @@ -82,6 +87,7 @@ class TrezorKeyring extends EventEmitter { } async getPage () { + return new Promise((resolve, reject) => { return this.unlock() .then(_ => { @@ -252,4 +258,4 @@ class TrezorKeyring extends EventEmitter { } TrezorKeyring.type = keyringType -module.exports = TrezorKeyring \ No newline at end of file +module.exports = TrezorKeyring diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index dd5a5616f..081c2e2db 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -48,7 +48,7 @@ const seedPhraseVerifier = require('./lib/seed-phrase-verifier') const cleanErrorStack = require('./lib/cleanErrorStack') const DiagnosticsReporter = require('./lib/diagnostics-reporter') const log = require('loglevel') -const TrezorKeyring = require("./lib/trezorKeyring"); +const TrezorKeyring = require('./lib/trezorKeyring') module.exports = class MetamaskController extends EventEmitter { @@ -540,9 +540,10 @@ module.exports = class MetamaskController extends EventEmitter { * @returns [] accounts */ async connectHardware (deviceName, page) { + const keyringController = this.keyringController const keyring = await keyringController.getKeyringsByType( - 'Trezor Hardware Keyring' + 'Trezor Hardware' )[0] if (!keyring) { throw new Error('MetamaskController - No Trezor Hardware Keyring found') @@ -562,7 +563,7 @@ module.exports = class MetamaskController extends EventEmitter { async unlockTrezorAccount (index) { const keyringController = this.keyringController const keyring = await keyringController.getKeyringsByType( - 'Trezor Hardware Keyring' + 'Trezor Hardware' )[0] if (!keyring) { throw new Error('MetamaskController - No Trezor Hardware Keyring found') -- cgit v1.2.3 From f6b27fa9eb542c1ac3fabdad9285e1a50baee7dc Mon Sep 17 00:00:00 2001 From: Bruno Date: Sun, 10 Jun 2018 19:02:54 -0400 Subject: add account working --- app/scripts/lib/trezorKeyring.js | 19 +++++++++++-------- app/scripts/metamask-controller.js | 2 ++ 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js index 83787f3d2..cf7436a44 100644 --- a/app/scripts/lib/trezorKeyring.js +++ b/app/scripts/lib/trezorKeyring.js @@ -8,7 +8,7 @@ const keyringType = 'Trezor Hardware' const TrezorConnect = require('./trezor-connect.js') const HDKey = require('hdkey') const TREZOR_FIRMWARE_VERSION = '1.4.0' -const log = require('loglevel') +//const log = require('loglevel') class TrezorKeyring extends EventEmitter { constructor (opts = {}) { @@ -19,10 +19,11 @@ class TrezorKeyring extends EventEmitter { this.deserialize(opts) this.page = 0 this.perPage = 5 + this.accountToUnlock = 0 } serialize () { - return Promise.resolve({ + return Promise.resolve({ hdPath: this.hdPath, accounts: this.accounts, page: this.page, @@ -44,8 +45,6 @@ class TrezorKeyring extends EventEmitter { TrezorConnect.getXPubKey( this.hdPath, response => { - log.debug('TREZOR CONNECT RESPONSE: ') - log.debug(response) if (response.success) { this.hdk.publicKey = new Buffer(response.publicKey, 'hex') this.hdk.chainCode = new Buffer(response.chainCode, 'hex') @@ -59,14 +58,18 @@ class TrezorKeyring extends EventEmitter { }) } + setAccountToUnlock (index) { + this.accountToUnlock = parseInt(index, 10) + } + addAccounts (n = 1) { return new Promise((resolve, reject) => { return this.unlock() .then(_ => { const pathBase = 'm' - const from = n - const to = n + 1 + const from = this.accountToUnlock + const to = from + 1 this.accounts = [] @@ -133,7 +136,7 @@ class TrezorKeyring extends EventEmitter { // tx is an instance of the ethereumjs-transaction class. async signTransaction (address, tx) { throw new Error('Not supported on this device') - /* + /* await this.lock.acquire() try { @@ -200,7 +203,7 @@ class TrezorKeyring extends EventEmitter { } return signature - + } finally { await this.lock.release() } */ diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 081c2e2db..3cb77b35a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -569,6 +569,8 @@ module.exports = class MetamaskController extends EventEmitter { throw new Error('MetamaskController - No Trezor Hardware Keyring found') } + keyring.setAccountToUnlock(index) + const oldAccounts = await keyringController.getAccounts() const keyState = await keyringController.addNewAccount(keyring) const newAccounts = await keyringController.getAccounts() -- cgit v1.2.3 From d1880073f678dbdc52e07e62ec66c39eea5062a6 Mon Sep 17 00:00:00 2001 From: Bruno Date: Sun, 10 Jun 2018 21:10:22 -0400 Subject: balances working --- app/scripts/lib/trezorKeyring.js | 3 ++- app/scripts/metamask-controller.js | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js index cf7436a44..fb029f82a 100644 --- a/app/scripts/lib/trezorKeyring.js +++ b/app/scripts/lib/trezorKeyring.js @@ -8,7 +8,7 @@ const keyringType = 'Trezor Hardware' const TrezorConnect = require('./trezor-connect.js') const HDKey = require('hdkey') const TREZOR_FIRMWARE_VERSION = '1.4.0' -//const log = require('loglevel') +const log = require('loglevel') class TrezorKeyring extends EventEmitter { constructor (opts = {}) { @@ -111,6 +111,7 @@ class TrezorKeyring extends EventEmitter { index: i, }) } + log.debug(accounts) resolve(accounts) }) .catch(e => { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3cb77b35a..daab5baa5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -549,10 +549,10 @@ module.exports = class MetamaskController extends EventEmitter { throw new Error('MetamaskController - No Trezor Hardware Keyring found') } - const accounts = page === -1 ? await keyring.getPrevAccountSet() : await keyring.getNextAccountSet() + const accounts = page === -1 ? await keyring.getPrevAccountSet(this.provider) : await keyring.getNextAccountSet(this.provider) + this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) return accounts - } /** @@ -570,7 +570,6 @@ module.exports = class MetamaskController extends EventEmitter { } keyring.setAccountToUnlock(index) - const oldAccounts = await keyringController.getAccounts() const keyState = await keyringController.addNewAccount(keyring) const newAccounts = await keyringController.getAccounts() -- cgit v1.2.3 From 68d97211ff468b137965df2a30c6b295a3ab5679 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 11 Jun 2018 01:52:41 -0400 Subject: sign transactions is pretty close --- app/scripts/lib/trezorKeyring.js | 126 ++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 49 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js index fb029f82a..fa5d6070c 100644 --- a/app/scripts/lib/trezorKeyring.js +++ b/app/scripts/lib/trezorKeyring.js @@ -4,10 +4,11 @@ const ethUtil = require('ethereumjs-util') const hdPathString = `m/44'/60'/0'/0` const keyringType = 'Trezor Hardware' - +const Transaction = require('ethereumjs-tx') +const pathBase = 'm' const TrezorConnect = require('./trezor-connect.js') const HDKey = require('hdkey') -const TREZOR_FIRMWARE_VERSION = '1.4.0' +const TREZOR_MIN_FIRMWARE_VERSION = '1.5.2' const log = require('loglevel') class TrezorKeyring extends EventEmitter { @@ -19,7 +20,7 @@ class TrezorKeyring extends EventEmitter { this.deserialize(opts) this.page = 0 this.perPage = 5 - this.accountToUnlock = 0 + this.unlockedAccount = 0 } serialize () { @@ -53,13 +54,13 @@ class TrezorKeyring extends EventEmitter { reject(response.error || 'Unknown error') } }, - TREZOR_FIRMWARE_VERSION + TREZOR_MIN_FIRMWARE_VERSION ) }) } setAccountToUnlock (index) { - this.accountToUnlock = parseInt(index, 10) + this.unlockedAccount = parseInt(index, 10) } addAccounts (n = 1) { @@ -67,18 +68,13 @@ class TrezorKeyring extends EventEmitter { return new Promise((resolve, reject) => { return this.unlock() .then(_ => { - const pathBase = 'm' - const from = this.accountToUnlock + const from = this.unlockedAccount const to = from + 1 - this.accounts = [] for (let i = from; i < to; i++) { - const dkey = this.hdk.derive(`${pathBase}/${i}`) - const address = ethUtil - .publicToAddress(dkey.publicKey, true) - .toString('hex') - this.accounts.push(ethUtil.toChecksumAddress(address)) + + this.accounts.push(this.getEthAddress(pathBase, i)) this.page = 0 } resolve(this.accounts) @@ -94,19 +90,16 @@ class TrezorKeyring extends EventEmitter { return new Promise((resolve, reject) => { return this.unlock() .then(_ => { - const pathBase = 'm' + const from = this.page === 0 ? 0 : (this.page - 1) * this.perPage const to = from + this.perPage const accounts = [] for (let i = from; i < to; i++) { - const dkey = this.hdk.derive(`${pathBase}/${i}`) - const address = ethUtil - .publicToAddress(dkey.publicKey, true) - .toString('hex') + accounts.push({ - address: ethUtil.toChecksumAddress(address), + address: this.getEthAddress(pathBase, i), balance: 0, index: i, }) @@ -134,40 +127,75 @@ class TrezorKeyring extends EventEmitter { return Promise.resolve(this.accounts.slice()) } - // tx is an instance of the ethereumjs-transaction class. - async signTransaction (address, tx) { - throw new Error('Not supported on this device') - /* - await this.lock.acquire() - try { - - // Look before we leap - await this._checkCorrectTrezorAttached() + padLeftEven (hex) { + return hex.length % 2 !== 0 ? `0${hex}` : hex + } - let accountId = await this._findAddressId(address) - let eth = await this._getEth() - tx.v = tx._chainId - let TrezorSig = await eth.signTransaction( - this._derivePath(accountId), - tx.serialize().toString('hex') - ) - tx.v = parseInt(TrezorSig.v, 16) - tx.r = '0x' + TrezorSig.r - tx.s = '0x' + TrezorSig.s + cleanData (buf) { + return this.padLeftEven(ethUtil.bufferToHex(buf).substring(2).toLowerCase()) + } - // Since look before we leap check is racy, also check that signature is for account expected - let addressSignedWith = ethUtil.bufferToHex(tx.getSenderAddress()) - if (addressSignedWith.toLowerCase() !== address.toLowerCase()) { - throw new Error( - `Signature is for ${addressSignedWith} but expected ${address} - is the correct Trezor device attached?` - ) - } + getEthAddress (pathBase, i) { + const dkey = this.hdk.derive(`${pathBase}/${i}`) + const address = ethUtil + .publicToAddress(dkey.publicKey, true) + .toString('hex') + return ethUtil.toChecksumAddress(address) + } - return tx + // tx is an instance of the ethereumjs-transaction class. + async signTransaction (address, tx) { - } finally { - await this.lock.release() - }*/ + return new Promise((resolve, reject) => { + log.debug('sign transaction ', address, tx) + const account = `m/44'/60'/0'/${this.unlockedAccount}` + + const txData = { + account, + nonce: this.cleanData(tx.nonce), + gasPrice: this.cleanData(tx.gasPrice), + gasLimit: this.cleanData(tx.gasLimit), + to: this.cleanData(tx.to), + value: this.cleanData(tx.value), + data: this.cleanData(tx.data), + chainId: tx._chainId, + } + + TrezorConnect.ethereumSignTx( + txData.account, + txData.nonce, + txData.gasPrice, + txData.gasLimit, + txData.to, + txData.value, + txData.data === '' ? null : txData.data, + txData.chainId, + response => { + if (response.success) { + tx.v = `0x${response.v.toString(16)}` + tx.r = `0x${response.r}` + tx.s = `0x${response.s}` + log.debug('about to create new tx with data', tx) + + const signedTx = new Transaction(tx) + + log.debug('signature is valid?', signedTx.verifySignature()) + + const addressSignedWith = ethUtil.toChecksumAddress(`0x${signedTx.from.toString('hex')}`) + const correctAddress = ethUtil.toChecksumAddress(address) + if (addressSignedWith !== correctAddress) { + // throw new Error('signature doesnt match the right address') + log.error('signature doesnt match the right address', addressSignedWith, correctAddress) + } + + resolve(signedTx) + + } else { + throw new Error(response.error || 'Unknown error') + } + }, + TREZOR_MIN_FIRMWARE_VERSION) + }) } async signMessage (withAccount, data) { -- cgit v1.2.3 From 999b6bd24a85068209a3213a4c9a83bf67854456 Mon Sep 17 00:00:00 2001 From: Bruno Date: Mon, 11 Jun 2018 01:58:19 -0400 Subject: clean up --- app/scripts/lib/trezorKeyring.js | 113 ++++++++------------------------------- 1 file changed, 21 insertions(+), 92 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js index fa5d6070c..b0e5d31da 100644 --- a/app/scripts/lib/trezorKeyring.js +++ b/app/scripts/lib/trezorKeyring.js @@ -74,7 +74,7 @@ class TrezorKeyring extends EventEmitter { for (let i = from; i < to; i++) { - this.accounts.push(this.getEthAddress(pathBase, i)) + this.accounts.push(this._addressFromId(pathBase, i)) this.page = 0 } resolve(this.accounts) @@ -85,7 +85,7 @@ class TrezorKeyring extends EventEmitter { }) } - async getPage () { + getPage () { return new Promise((resolve, reject) => { return this.unlock() @@ -99,7 +99,7 @@ class TrezorKeyring extends EventEmitter { for (let i = from; i < to; i++) { accounts.push({ - address: this.getEthAddress(pathBase, i), + address: this._addressFromId(pathBase, i), balance: 0, index: i, }) @@ -127,22 +127,6 @@ class TrezorKeyring extends EventEmitter { return Promise.resolve(this.accounts.slice()) } - padLeftEven (hex) { - return hex.length % 2 !== 0 ? `0${hex}` : hex - } - - cleanData (buf) { - return this.padLeftEven(ethUtil.bufferToHex(buf).substring(2).toLowerCase()) - } - - getEthAddress (pathBase, i) { - const dkey = this.hdk.derive(`${pathBase}/${i}`) - const address = ethUtil - .publicToAddress(dkey.publicKey, true) - .toString('hex') - return ethUtil.toChecksumAddress(address) - } - // tx is an instance of the ethereumjs-transaction class. async signTransaction (address, tx) { @@ -152,12 +136,12 @@ class TrezorKeyring extends EventEmitter { const txData = { account, - nonce: this.cleanData(tx.nonce), - gasPrice: this.cleanData(tx.gasPrice), - gasLimit: this.cleanData(tx.gasLimit), - to: this.cleanData(tx.to), - value: this.cleanData(tx.value), - data: this.cleanData(tx.data), + nonce: this._cleanData(tx.nonce), + gasPrice: this._cleanData(tx.gasPrice), + gasLimit: this._cleanData(tx.gasLimit), + to: this._cleanData(tx.to), + value: this._cleanData(tx.value), + data: this._cleanData(tx.data), chainId: tx._chainId, } @@ -204,41 +188,12 @@ class TrezorKeyring extends EventEmitter { // For personal_sign, we need to prefix the message: async signPersonalMessage (withAccount, message) { + // Waiting on trezor to enable this throw new Error('Not supported on this device') - /* - await this.lock.acquire() - try { - // Look before we leap - await this._checkCorrectTrezorAttached() - - let accountId = await this._findAddressId(withAccount) - let eth = await this._getEth() - let msgHex = ethUtil.stripHexPrefix(message) - let TrezorSig = await eth.signPersonalMessage( - this._derivePath(accountId), - msgHex - ) - let signature = this._personalToRawSig(TrezorSig) - - // Since look before we leap check is racy, also check that signature is for account expected - let addressSignedWith = sigUtil.recoverPersonalSignature({ - data: message, - sig: signature, - }) - if (addressSignedWith.toLowerCase() !== withAccount.toLowerCase()) { - throw new Error( - `Signature is for ${addressSignedWith} but expected ${withAccount} - is the correct Trezor device attached?` - ) - } - - return signature - - } finally { - await this.lock.release() - } */ } async signTypedData (withAccount, typedData) { + // Waiting on trezor to enable this throw new Error('Not supported on this device') } @@ -246,46 +201,20 @@ class TrezorKeyring extends EventEmitter { throw new Error('Not supported on this device') } - async _findAddressId (addr) { - const result = this.accounts.indexOf(addr) - if (result === -1) throw new Error('Unknown address') - else return result - } - - async _addressFromId (i) { - /* Must be called with lock acquired - const eth = await this._getEth() - return (await eth.getAddress(this._derivePath(i))).address*/ - const result = this.accounts[i] - if (!result) throw new Error('Unknown address') - else return result - } - - async _checkCorrectTrezorAttached () { - return true - /* Must be called with lock acquired - if (this.accounts.length > 0) { - const expectedFirstAccount = this.accounts[0] - let actualFirstAccount = await this._addressFromId(0) - if (expectedFirstAccount !== actualFirstAccount) { - throw new Error( - `Incorrect Trezor device attached - expected device containg account ${expectedFirstAccount}, but found ${actualFirstAccount}` - ) - } - }*/ + _padLeftEven (hex) { + return hex.length % 2 !== 0 ? `0${hex}` : hex } - _derivePath (i) { - return this.hdPath + '/' + i + _cleanData (buf) { + return this._padLeftEven(ethUtil.bufferToHex(buf).substring(2).toLowerCase()) } - _personalToRawSig (TrezorSig) { - var v = TrezorSig['v'] - 27 - v = v.toString(16) - if (v.length < 2) { - v = '0' + v - } - return '0x' + TrezorSig['r'] + TrezorSig['s'] + v + _addressFromId(pathBase, i) { + const dkey = this.hdk.derive(`${pathBase}/${i}`) + const address = ethUtil + .publicToAddress(dkey.publicKey, true) + .toString('hex') + return ethUtil.toChecksumAddress(address) } } -- cgit v1.2.3 From d4201ae1cc990ba6b69e84586caabc0848c2c38e Mon Sep 17 00:00:00 2001 From: Bruno Date: Wed, 13 Jun 2018 00:22:04 -0400 Subject: added support for signPersonalMessage --- app/scripts/lib/trezorKeyring.js | 73 +++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 28 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js index b0e5d31da..6a483fdcd 100644 --- a/app/scripts/lib/trezorKeyring.js +++ b/app/scripts/lib/trezorKeyring.js @@ -1,6 +1,6 @@ const { EventEmitter } = require('events') const ethUtil = require('ethereumjs-util') -// const sigUtil = require('eth-sig-util') +const sigUtil = require('eth-sig-util') const hdPathString = `m/44'/60'/0'/0` const keyringType = 'Trezor Hardware' @@ -131,31 +131,21 @@ class TrezorKeyring extends EventEmitter { async signTransaction (address, tx) { return new Promise((resolve, reject) => { + log.debug('sign transaction ', address, tx) - const account = `m/44'/60'/0'/${this.unlockedAccount}` - - const txData = { - account, - nonce: this._cleanData(tx.nonce), - gasPrice: this._cleanData(tx.gasPrice), - gasLimit: this._cleanData(tx.gasLimit), - to: this._cleanData(tx.to), - value: this._cleanData(tx.value), - data: this._cleanData(tx.data), - chainId: tx._chainId, - } TrezorConnect.ethereumSignTx( - txData.account, - txData.nonce, - txData.gasPrice, - txData.gasLimit, - txData.to, - txData.value, - txData.data === '' ? null : txData.data, - txData.chainId, + this._getUnlockedAccount(), + this._normalize(tx.nonce), + this._normalize(tx.gasPrice), + this._normalize(tx.gasLimit), + this._normalize(tx.to), + this._normalize(tx.value), + this._normalize(tx.data), + tx._chainId, response => { if (response.success) { + tx.v = `0x${response.v.toString(16)}` tx.r = `0x${response.r}` tx.s = `0x${response.s}` @@ -163,13 +153,11 @@ class TrezorKeyring extends EventEmitter { const signedTx = new Transaction(tx) - log.debug('signature is valid?', signedTx.verifySignature()) - const addressSignedWith = ethUtil.toChecksumAddress(`0x${signedTx.from.toString('hex')}`) const correctAddress = ethUtil.toChecksumAddress(address) if (addressSignedWith !== correctAddress) { - // throw new Error('signature doesnt match the right address') log.error('signature doesnt match the right address', addressSignedWith, correctAddress) + throw new Error('signature doesnt match the right address') } resolve(signedTx) @@ -188,8 +176,24 @@ class TrezorKeyring extends EventEmitter { // For personal_sign, we need to prefix the message: async signPersonalMessage (withAccount, message) { - // Waiting on trezor to enable this - throw new Error('Not supported on this device') + + TrezorConnect.ethereumSignMessage(this._getUnlockedAccount(), message, response => { + if (response.success) { + + const signature = this._personalToRawSig(response.signature) + const addressSignedWith = sigUtil.recoverPersonalSignature({data: message, sig: signature}) + const correctAddress = ethUtil.toChecksumAddress(withAccount) + if (addressSignedWith !== correctAddress) { + log.error('signature doesnt match the right address', addressSignedWith, correctAddress) + throw new Error('signature doesnt match the right address') + } + return signature + + } else { + throw new Error(response.error || 'Unknown error') + } + + }, TREZOR_MIN_FIRMWARE_VERSION) } async signTypedData (withAccount, typedData) { @@ -205,17 +209,30 @@ class TrezorKeyring extends EventEmitter { return hex.length % 2 !== 0 ? `0${hex}` : hex } - _cleanData (buf) { + _normalize (buf) { return this._padLeftEven(ethUtil.bufferToHex(buf).substring(2).toLowerCase()) } - _addressFromId(pathBase, i) { + _addressFromId (pathBase, i) { const dkey = this.hdk.derive(`${pathBase}/${i}`) const address = ethUtil .publicToAddress(dkey.publicKey, true) .toString('hex') return ethUtil.toChecksumAddress(address) } + + _getUnlockedAccount () { + return `${this.hdPath}/${this.unlockedAccount}` + } + + _personalToRawSig (signature) { + var v = signature['v'] - 27 + v = v.toString(16) + if (v.length < 2) { + v = '0' + v + } + return '0x' + signature['r'] + signature['s'] + v + } } TrezorKeyring.type = keyringType -- cgit v1.2.3 From 8763ea898e7838d08315063b0e2181405a2ae3d5 Mon Sep 17 00:00:00 2001 From: Bruno Date: Wed, 13 Jun 2018 01:32:13 -0400 Subject: move TrezorKeyring to its own package --- app/scripts/lib/trezor-connect.js | 1138 ------------------------------------ app/scripts/lib/trezorKeyring.js | 239 -------- app/scripts/metamask-controller.js | 5 +- 3 files changed, 3 insertions(+), 1379 deletions(-) delete mode 100644 app/scripts/lib/trezor-connect.js delete mode 100644 app/scripts/lib/trezorKeyring.js (limited to 'app') diff --git a/app/scripts/lib/trezor-connect.js b/app/scripts/lib/trezor-connect.js deleted file mode 100644 index 574e88104..000000000 --- a/app/scripts/lib/trezor-connect.js +++ /dev/null @@ -1,1138 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ - -/** - * (C) 2017 SatoshiLabs - * - * GPLv3 - */ -var TREZOR_CONNECT_VERSION = 4; - -if (!Array.isArray) { - Array.isArray = function(arg) { - return Object.prototype.toString.call(arg) === "[object Array]"; - }; -} - -var HD_HARDENED = 0x80000000; - -// react sometimes adds some other parameters that should not be there -function _fwStrFix(obj, fw) { - if (typeof fw === "string") { - obj.requiredFirmware = fw; - } - return obj; -} - -("use strict"); - -var chrome = window.chrome; -var IS_CHROME_APP = chrome && chrome.app && chrome.app.window; - -var ERR_TIMED_OUT = "Loading timed out"; -var ERR_WINDOW_CLOSED = "Window closed"; -var ERR_WINDOW_BLOCKED = "Window blocked"; -var ERR_ALREADY_WAITING = "Already waiting for a response"; -var ERR_CHROME_NOT_CONNECTED = "Internal Chrome popup is not responding."; - -var DISABLE_LOGIN_BUTTONS = window.TREZOR_DISABLE_LOGIN_BUTTONS || false; -var CHROME_URL = window.TREZOR_CHROME_URL || "./chrome/wrapper.html"; -var POPUP_ORIGIN = window.TREZOR_POPUP_ORIGIN || "https://connect.trezor.io"; -var POPUP_PATH = - window.TREZOR_POPUP_PATH || POPUP_ORIGIN + "/" + TREZOR_CONNECT_VERSION; -var POPUP_URL = - window.TREZOR_POPUP_URL || - POPUP_PATH + "/popup/popup.html?v=" + new Date().getTime(); - -var POPUP_INIT_TIMEOUT = 15000; - -/** - * Public API. - */ -function TrezorConnect() { - var manager = new PopupManager(); - - /** - * Popup errors. - */ - this.ERR_TIMED_OUT = ERR_TIMED_OUT; - this.ERR_WINDOW_CLOSED = ERR_WINDOW_CLOSED; - this.ERR_WINDOW_BLOCKED = ERR_WINDOW_BLOCKED; - this.ERR_ALREADY_WAITING = ERR_ALREADY_WAITING; - this.ERR_CHROME_NOT_CONNECTED = ERR_CHROME_NOT_CONNECTED; - - /** - * Open the popup for further communication. All API functions open the - * popup automatically, but if you need to generate some parameters - * asynchronously, use `open` first to avoid popup blockers. - * @param {function(?Error)} callback - */ - this.open = function(callback) { - var onchannel = function(result) { - if (result instanceof Error) { - callback(result); - } else { - callback(); - } - }; - manager.waitForChannel(onchannel); - }; - - /** - * Close the opened popup, if any. - */ - this.close = function() { - manager.close(); - }; - - /** - * Enable or disable closing the opened popup after a successful call. - * @param {boolean} value - */ - this.closeAfterSuccess = function(value) { - manager.closeAfterSuccess = value; - }; - - /** - * Enable or disable closing the opened popup after a failed call. - * @param {boolean} value - */ - this.closeAfterFailure = function(value) { - manager.closeAfterFailure = value; - }; - - /** - * Set bitcore server - * @param {string|Array} value - */ - this.setBitcoreURLS = function(value) { - if (typeof value === "string") { - manager.bitcoreURLS = [value]; - } else if (value instanceof Array) { - manager.bitcoreURLS = value; - } - }; - - /** - * Set currency. Human readable coin name - * @param {string|Array} value - */ - this.setCurrency = function(value) { - if (typeof value === "string") { - manager.currency = value; - } - }; - - /** - * Set currency units (mBTC, BTC) - * @param {string|Array} value - */ - this.setCurrencyUnits = function(value) { - if (typeof value === "string") { - manager.currencyUnits = value; - } - }; - - /** - * Set coin info json url - * @param {string|Array} value - */ - this.setCoinInfoURL = function(value) { - if (typeof value === "string") { - manager.coinInfoURL = value; - } - }; - - /** - * Set max. limit for account discovery - * @param {number} value - */ - this.setAccountDiscoveryLimit = function(value) { - if (!isNaN(value)) manager.accountDiscoveryLimit = value; - }; - - /** - * Set max. gap for account discovery - * @param {number} value - */ - this.setAccountDiscoveryGapLength = function(value) { - if (!isNaN(value)) manager.accountDiscoveryGapLength = value; - }; - - /** - * Set discovery BIP44 coin type - * @param {number} value - */ - this.setAccountDiscoveryBip44CoinType = function(value) { - if (!isNaN(value)) manager.accountDiscoveryBip44CoinType = value; - }; - - /** - * @typedef XPubKeyResult - * @param {boolean} success - * @param {?string} error - * @param {?string} xpubkey serialized extended public key - * @param {?string} path BIP32 serializd path of the key - */ - - /** - * Load BIP32 extended public key by path. - * - * Path can be specified either in the string form ("m/44'/1/0") or as - * raw integer array. In case you omit the path, user is asked to select - * a BIP32 account to export, and the result contains m/44'/0'/x' node - * of the account. - * - * @param {?(string|array)} path - * @param {function(XPubKeyResult)} callback - * @param {?(string|array)} requiredFirmware - */ - this.getXPubKey = function(path, callback, requiredFirmware) { - if (typeof path === "string") { - path = parseHDPath(path); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "xpubkey", - path: path - }, - requiredFirmware - ), - callback - ); - }; - - this.getFreshAddress = function(callback, requiredFirmware) { - var wrapperCallback = function(result) { - if (result.success) { - callback({ success: true, address: result.freshAddress }); - } else { - callback(result); - } - }; - - manager.sendWithChannel( - _fwStrFix( - { - type: "accountinfo" - }, - requiredFirmware - ), - wrapperCallback - ); - }; - - this.getAccountInfo = function(input, callback, requiredFirmware) { - try { - manager.sendWithChannel( - _fwStrFix( - { - type: "accountinfo", - description: input - }, - requiredFirmware - ), - callback - ); - } catch (e) { - callback({ success: false, error: e }); - } - }; - - this.getAllAccountsInfo = function(callback, requiredFirmware) { - try { - manager.sendWithChannel( - _fwStrFix( - { - type: "allaccountsinfo", - description: "all" - }, - requiredFirmware - ), - callback - ); - } catch (e) { - callback({ success: false, error: e }); - } - }; - - this.getBalance = function(callback, requiredFirmware) { - manager.sendWithChannel( - _fwStrFix( - { - type: "accountinfo" - }, - requiredFirmware - ), - callback - ); - }; - - /** - * @typedef SignTxResult - * @param {boolean} success - * @param {?string} error - * @param {?string} serialized_tx serialized tx, in hex, including signatures - * @param {?array} signatures array of input signatures, in hex - */ - - /** - * Sign a transaction in the device and return both serialized - * transaction and the signatures. - * - * @param {array} inputs - * @param {array} outputs - * @param {function(SignTxResult)} callback - * @param {?(string|array)} requiredFirmware - * - * @see https://github.com/trezor/trezor-common/blob/master/protob/types.proto - */ - this.signTx = function(inputs, outputs, callback, requiredFirmware, coin) { - manager.sendWithChannel( - _fwStrFix( - { - type: "signtx", - inputs: inputs, - outputs: outputs, - coin: coin - }, - requiredFirmware - ), - callback - ); - }; - - // new implementation with ethereum at beginnig - this.ethereumSignTx = function() { - this.signEthereumTx.apply(this, arguments); - }; - - // old fallback - this.signEthereumTx = function( - address_n, - nonce, - gas_price, - gas_limit, - to, - value, - data, - chain_id, - callback, - requiredFirmware - ) { - if (requiredFirmware == null) { - requiredFirmware = "1.4.0"; // first firmware that supports ethereum - } - if (typeof address_n === "string") { - address_n = parseHDPath(address_n); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "signethtx", - address_n: address_n, - nonce: nonce, - gas_price: gas_price, - gas_limit: gas_limit, - to: to, - value: value, - data: data, - chain_id: chain_id - }, - requiredFirmware - ), - callback - ); - }; - - /** - * @typedef TxRecipient - * @param {number} amount the amount to send, in satoshis - * @param {string} address the address of the recipient - */ - - /** - * Compose a transaction by doing BIP-0044 discovery, letting the user - * select an account, and picking UTXO by internal preferences. - * Transaction is then signed and returned in the same format as - * `signTx`. Only supports BIP-0044 accounts (single-signature). - * - * @param {array} recipients - * @param {function(SignTxResult)} callback - * @param {?(string|array)} requiredFirmware - */ - this.composeAndSignTx = function(recipients, callback, requiredFirmware) { - manager.sendWithChannel( - _fwStrFix( - { - type: "composetx", - recipients: recipients - }, - requiredFirmware - ), - callback - ); - }; - - /** - * @typedef RequestLoginResult - * @param {boolean} success - * @param {?string} error - * @param {?string} public_key public key used for signing, in hex - * @param {?string} signature signature, in hex - */ - - /** - * Sign a login challenge for active origin. - * - * @param {?string} hosticon - * @param {string} challenge_hidden - * @param {string} challenge_visual - * @param {string|function(RequestLoginResult)} callback - * @param {?(string|array)} requiredFirmware - * - * @see https://github.com/trezor/trezor-common/blob/master/protob/messages.proto - */ - this.requestLogin = function( - hosticon, - challenge_hidden, - challenge_visual, - callback, - requiredFirmware - ) { - if (typeof callback === "string") { - // special case for a login through button. - // `callback` is name of global var - callback = window[callback]; - } - if (!callback) { - throw new TypeError("TrezorConnect: login callback not found"); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "login", - icon: hosticon, - challenge_hidden: challenge_hidden, - challenge_visual: challenge_visual - }, - requiredFirmware - ), - callback - ); - }; - - /** - * @typedef SignMessageResult - * @param {boolean} success - * @param {?string} error - * @param {?string} address address (in base58check) - * @param {?string} signature signature, in base64 - */ - - /** - * Sign a message - * - * @param {string|array} path - * @param {string} message to sign (ascii) - * @param {string|function(SignMessageResult)} callback - * @param {?string} opt_coin - (optional) name of coin (default Bitcoin) - * @param {?(string|array)} requiredFirmware - * - */ - this.signMessage = function( - path, - message, - callback, - opt_coin, - requiredFirmware - ) { - if (typeof path === "string") { - path = parseHDPath(path); - } - if (!opt_coin) { - opt_coin = "Bitcoin"; - } - if (!callback) { - throw new TypeError("TrezorConnect: callback not found"); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "signmsg", - path: path, - message: message, - coin: opt_coin - }, - requiredFirmware - ), - callback - ); - }; - - /** - * Sign an Ethereum message - * - * @param {string|array} path - * @param {string} message to sign (ascii) - * @param {string|function(SignMessageResult)} callback - * @param {?(string|array)} requiredFirmware - * - */ - this.ethereumSignMessage = function( - path, - message, - callback, - requiredFirmware - ) { - if (typeof path === "string") { - path = parseHDPath(path); - } - if (!callback) { - throw new TypeError("TrezorConnect: callback not found"); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "signethmsg", - path: path, - message: message - }, - requiredFirmware - ), - callback - ); - }; - - /** - * Verify message - * - * @param {string} address - * @param {string} signature (base64) - * @param {string} message (string) - * @param {string|function()} callback - * @param {?string} opt_coin - (optional) name of coin (default Bitcoin) - * @param {?(string|array)} requiredFirmware - * - */ - this.verifyMessage = function( - address, - signature, - message, - callback, - opt_coin, - requiredFirmware - ) { - if (!opt_coin) { - opt_coin = "Bitcoin"; - } - if (!callback) { - throw new TypeError("TrezorConnect: callback not found"); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "verifymsg", - address: address, - signature: signature, - message: message, - coin: { coin_name: opt_coin } - }, - requiredFirmware - ), - callback - ); - }; - - /** - * Verify ethereum message - * - * @param {string} address - * @param {string} signature (base64) - * @param {string} message (string) - * @param {string|function()} callback - * @param {?(string|array)} requiredFirmware - * - */ - this.ethereumVerifyMessage = function( - address, - signature, - message, - callback, - requiredFirmware - ) { - if (!callback) { - throw new TypeError("TrezorConnect: callback not found"); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "verifyethmsg", - address: address, - signature: signature, - message: message - }, - requiredFirmware - ), - callback - ); - }; - - /** - * Symmetric key-value encryption - * - * @param {string|array} path - * @param {string} key to show on device display - * @param {string} value hexadecimal value, length a multiple of 16 bytes - * @param {boolean} encrypt / decrypt direction - * @param {boolean} ask_on_encrypt (should user confirm on encrypt?) - * @param {boolean} ask_on_decrypt (should user confirm on decrypt?) - * @param {string|function()} callback - * @param {?(string|array)} requiredFirmware - * - */ - this.cipherKeyValue = function( - path, - key, - value, - encrypt, - ask_on_encrypt, - ask_on_decrypt, - callback, - requiredFirmware - ) { - if (typeof path === "string") { - path = parseHDPath(path); - } - if (typeof value !== "string") { - throw new TypeError("TrezorConnect: Value must be a string"); - } - if (!/^[0-9A-Fa-f]*$/.test(value)) { - throw new TypeError("TrezorConnect: Value must be hexadecimal"); - } - if (value.length % 32 !== 0) { - // 1 byte == 2 hex strings - throw new TypeError( - "TrezorConnect: Value length must be multiple of 16 bytes" - ); - } - if (!callback) { - throw new TypeError("TrezorConnect: callback not found"); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "cipherkeyvalue", - path: path, - key: key, - value: value, - encrypt: !!encrypt, - ask_on_encrypt: !!ask_on_encrypt, - ask_on_decrypt: !!ask_on_decrypt - }, - requiredFirmware - ), - callback - ); - }; - - this.nemGetAddress = function( - address_n, - network, - callback, - requiredFirmware - ) { - if (requiredFirmware == null) { - requiredFirmware = "1.6.0"; // first firmware that supports NEM - } - if (typeof address_n === "string") { - address_n = parseHDPath(address_n); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "nemGetAddress", - address_n: address_n, - network: network - }, - requiredFirmware - ), - callback - ); - }; - - this.nemSignTx = function( - address_n, - transaction, - callback, - requiredFirmware - ) { - if (requiredFirmware == null) { - requiredFirmware = "1.6.0"; // first firmware that supports NEM - } - if (typeof address_n === "string") { - address_n = parseHDPath(address_n); - } - manager.sendWithChannel( - _fwStrFix( - { - type: "nemSignTx", - address_n: address_n, - transaction: transaction - }, - requiredFirmware - ), - callback - ); - }; - - this.pushTransaction = function(rawTx, callback) { - if (!/^[0-9A-Fa-f]*$/.test(rawTx)) { - throw new TypeError("TrezorConnect: Transaction must be hexadecimal"); - } - if (!callback) { - throw new TypeError("TrezorConnect: callback not found"); - } - - manager.sendWithChannel( - { - type: "pushtx", - rawTx: rawTx - }, - callback - ); - }; - - /** - * Display address on device - * - * @param {array} address - * @param {string} coin - * @param {boolean} segwit - * @param {?(string|array)} requiredFirmware - * - */ - this.getAddress = function( - address, - coin, - segwit, - callback, - requiredFirmware - ) { - if (typeof address === "string") { - address = parseHDPath(address); - } - - manager.sendWithChannel( - _fwStrFix( - { - type: "getaddress", - address_n: address, - coin: coin, - segwit: segwit - }, - requiredFirmware - ), - callback - ); - }; - - /** - * Display ethereum address on device - * - * @param {array} address - * @param {?(string|array)} requiredFirmware - * - */ - this.ethereumGetAddress = function(address, callback, requiredFirmware) { - if (typeof address === "string") { - address = parseHDPath(address); - } - - manager.sendWithChannel( - _fwStrFix( - { - type: "ethgetaddress", - address_n: address - }, - requiredFirmware - ), - callback - ); - }; - - var LOGIN_CSS = - ''; - - var LOGIN_ONCLICK = - "TrezorConnect.requestLogin(" + - "'@hosticon@','@challenge_hidden@','@challenge_visual@','@callback@'" + - ")"; - - var LOGIN_HTML = - '
' + - ' ' + - ' ' + - ' @text@' + - " " + - ' ' + - ' What is TREZOR?' + - " " + - "
"; - - /** - * Find elements and replace them with login buttons. - * It's not required to use these special elements, feel free to call - * `TrezorConnect.requestLogin` directly. - */ - this.renderLoginButtons = function() { - var elements = document.getElementsByTagName("trezor:login"); - - for (var i = 0; i < elements.length; i++) { - var e = elements[i]; - var text = e.getAttribute("text") || "Sign in with TREZOR"; - var callback = e.getAttribute("callback") || ""; - var hosticon = e.getAttribute("icon") || ""; - var challenge_hidden = e.getAttribute("challenge_hidden") || ""; - var challenge_visual = e.getAttribute("challenge_visual") || ""; - - // it's not valid to put markup into attributes, so let users - // supply a raw text and make TREZOR bold - text = text.replace("TREZOR", "TREZOR"); - e.outerHTML = (LOGIN_CSS + LOGIN_HTML) - .replace("@text@", text) - .replace("@callback@", callback) - .replace("@hosticon@", hosticon) - .replace("@challenge_hidden@", challenge_hidden) - .replace("@challenge_visual@", challenge_visual) - .replace("@connect_path@", POPUP_PATH); - } - }; -} - -/* - * `getXPubKey()` - */ - -function parseHDPath(string) { - return string - .toLowerCase() - .split("/") - .filter(function(p) { - return p !== "m"; - }) - .map(function(p) { - var hardened = false; - if (p[p.length - 1] === "'") { - hardened = true; - p = p.substr(0, p.length - 1); - } - if (isNaN(p)) { - throw new Error("Not a valid path."); - } - var n = parseInt(p); - if (hardened) { - // hardened index - n = (n | 0x80000000) >>> 0; - } - return n; - }); -} - -/* - * Popup management - */ - -function ChromePopup(url, name, width, height) { - var left = (screen.width - width) / 2; - var top = (screen.height - height) / 2; - var opts = { - id: name, - innerBounds: { - width: width, - height: height, - left: left, - top: top - } - }; - - var closed = function() { - if (this.onclose) { - this.onclose(false); // never report as blocked - } - }.bind(this); - - var opened = function(w) { - this.window = w; - this.window.onClosed.addListener(closed); - }.bind(this); - - chrome.app.window.create(url, opts, opened); - - this.name = name; - this.window = null; - this.onclose = null; -} - -function ChromeChannel(popup, waiting) { - var port = null; - - var respond = function(data) { - if (waiting) { - var w = waiting; - waiting = null; - w(data); - } - }; - - var setup = function(p) { - if (p.name === popup.name) { - port = p; - port.onMessage.addListener(respond); - chrome.runtime.onConnect.removeListener(setup); - } - }; - - chrome.runtime.onConnect.addListener(setup); - - this.respond = respond; - - this.close = function() { - chrome.runtime.onConnect.removeListener(setup); - port.onMessage.removeListener(respond); - port.disconnect(); - port = null; - }; - - this.send = function(value, callback) { - if (waiting === null) { - waiting = callback; - - if (port) { - port.postMessage(value); - } else { - throw new Error(ERR_CHROME_NOT_CONNECTED); - } - } else { - throw new Error(ERR_ALREADY_WAITING); - } - }; -} - -function Popup(url, origin, name, width, height) { - var left = (screen.width - width) / 2; - var top = (screen.height - height) / 2; - var opts = - "width=" + - width + - ",height=" + - height + - ",left=" + - left + - ",top=" + - top + - ",menubar=no" + - ",toolbar=no" + - ",location=no" + - ",personalbar=no" + - ",status=no"; - var w = window.open(url, name, opts); - - var interval; - var blocked = w.closed; - var iterate = function() { - if (w.closed) { - clearInterval(interval); - if (this.onclose) { - this.onclose(blocked); - } - } - }.bind(this); - interval = setInterval(iterate, 100); - - this.window = w; - this.origin = origin; - this.onclose = null; -} - -function Channel(popup, waiting) { - var respond = function(data) { - if (waiting) { - var w = waiting; - waiting = null; - w(data); - } - }; - - var receive = function(event) { - var org1 = event.origin.match(/^.+\:\/\/[^\‌​/]+/)[0]; - var org2 = popup.origin.match(/^.+\:\/\/[^\‌​/]+/)[0]; - //if (event.source === popup.window && event.origin === popup.origin) { - if (event.source === popup.window && org1 === org2) { - respond(event.data); - } - }; - - window.addEventListener("message", receive); - - this.respond = respond; - - this.close = function() { - window.removeEventListener("message", receive); - }; - - this.send = function(value, callback) { - if (waiting === null) { - waiting = callback; - popup.window.postMessage(value, popup.origin); - } else { - throw new Error(ERR_ALREADY_WAITING); - } - }; -} - -function ConnectedChannel(p) { - var ready = function() { - clearTimeout(this.timeout); - this.popup.onclose = null; - this.ready = true; - this.onready(); - }.bind(this); - - var closed = function(blocked) { - clearTimeout(this.timeout); - this.channel.close(); - if (blocked) { - this.onerror(new Error(ERR_WINDOW_BLOCKED)); - } else { - this.onerror(new Error(ERR_WINDOW_CLOSED)); - } - }.bind(this); - - var timedout = function() { - this.popup.onclose = null; - if (this.popup.window) { - this.popup.window.close(); - } - this.channel.close(); - this.onerror(new Error(ERR_TIMED_OUT)); - }.bind(this); - - if (IS_CHROME_APP) { - this.popup = new ChromePopup(p.chromeUrl, p.name, p.width, p.height); - this.channel = new ChromeChannel(this.popup, ready); - } else { - this.popup = new Popup(p.url, p.origin, p.name, p.width, p.height); - this.channel = new Channel(this.popup, ready); - } - - this.timeout = setTimeout(timedout, POPUP_INIT_TIMEOUT); - - this.popup.onclose = closed; - - this.ready = false; - this.onready = null; - this.onerror = null; -} - -function PopupManager() { - var cc = null; - - var closed = function() { - cc.channel.respond(new Error(ERR_WINDOW_CLOSED)); - cc.channel.close(); - cc = null; - }; - - var open = function(callback) { - cc = new ConnectedChannel({ - name: "trezor-connect", - width: 600, - height: 500, - origin: POPUP_ORIGIN, - path: POPUP_PATH, - url: POPUP_URL, - chromeUrl: CHROME_URL - }); - cc.onready = function() { - cc.popup.onclose = closed; - callback(cc.channel); - }; - cc.onerror = function(error) { - cc = null; - callback(error); - }; - }.bind(this); - - this.closeAfterSuccess = true; - this.closeAfterFailure = true; - - this.close = function() { - if (cc && cc.popup.window) { - cc.popup.window.close(); - } - }; - - this.waitForChannel = function(callback) { - if (cc) { - if (cc.ready) { - callback(cc.channel); - } else { - callback(new Error(ERR_ALREADY_WAITING)); - } - } else { - try { - open(callback); - } catch (e) { - callback(new Error(ERR_WINDOW_BLOCKED)); - } - } - }; - - this.sendWithChannel = function(message, callback) { - message.bitcoreURLS = this.bitcoreURLS || null; - message.currency = this.currency || null; - message.currencyUnits = this.currencyUnits || null; - message.coinInfoURL = this.coinInfoURL || null; - message.accountDiscoveryLimit = this.accountDiscoveryLimit || null; - message.accountDiscoveryGapLength = this.accountDiscoveryGapLength || null; - message.accountDiscoveryBip44CoinType = - this.accountDiscoveryBip44CoinType || null; - - var respond = function(response) { - var succ = response.success && this.closeAfterSuccess; - var fail = !response.success && this.closeAfterFailure; - if (succ || fail) { - this.close(); - } - callback(response); - }.bind(this); - - var onresponse = function(response) { - if (response instanceof Error) { - var error = response; - respond({ success: false, error: error.message }); - } else { - respond(response); - } - }; - - var onchannel = function(channel) { - if (channel instanceof Error) { - var error = channel; - respond({ success: false, error: error.message }); - } else { - channel.send(message, onresponse); - } - }; - - this.waitForChannel(onchannel); - }; -} - -const connect = new TrezorConnect(); -module.exports = connect; \ No newline at end of file diff --git a/app/scripts/lib/trezorKeyring.js b/app/scripts/lib/trezorKeyring.js deleted file mode 100644 index 6a483fdcd..000000000 --- a/app/scripts/lib/trezorKeyring.js +++ /dev/null @@ -1,239 +0,0 @@ -const { EventEmitter } = require('events') -const ethUtil = require('ethereumjs-util') -const sigUtil = require('eth-sig-util') - -const hdPathString = `m/44'/60'/0'/0` -const keyringType = 'Trezor Hardware' -const Transaction = require('ethereumjs-tx') -const pathBase = 'm' -const TrezorConnect = require('./trezor-connect.js') -const HDKey = require('hdkey') -const TREZOR_MIN_FIRMWARE_VERSION = '1.5.2' -const log = require('loglevel') - -class TrezorKeyring extends EventEmitter { - constructor (opts = {}) { - super() - this.type = keyringType - this.accounts = [] - this.hdk = new HDKey() - this.deserialize(opts) - this.page = 0 - this.perPage = 5 - this.unlockedAccount = 0 - } - - serialize () { - return Promise.resolve({ - hdPath: this.hdPath, - accounts: this.accounts, - page: this.page, - }) - } - - deserialize (opts = {}) { - this.hdPath = opts.hdPath || hdPathString - this.accounts = opts.accounts || [] - this.page = opts.page || 0 - return Promise.resolve() - } - - unlock () { - - if (this.hdk.publicKey) return Promise.resolve() - - return new Promise((resolve, reject) => { - TrezorConnect.getXPubKey( - this.hdPath, - response => { - if (response.success) { - this.hdk.publicKey = new Buffer(response.publicKey, 'hex') - this.hdk.chainCode = new Buffer(response.chainCode, 'hex') - resolve() - } else { - reject(response.error || 'Unknown error') - } - }, - TREZOR_MIN_FIRMWARE_VERSION - ) - }) - } - - setAccountToUnlock (index) { - this.unlockedAccount = parseInt(index, 10) - } - - addAccounts (n = 1) { - - return new Promise((resolve, reject) => { - return this.unlock() - .then(_ => { - const from = this.unlockedAccount - const to = from + 1 - this.accounts = [] - - for (let i = from; i < to; i++) { - - this.accounts.push(this._addressFromId(pathBase, i)) - this.page = 0 - } - resolve(this.accounts) - }) - .catch(e => { - reject(e) - }) - }) - } - - getPage () { - - return new Promise((resolve, reject) => { - return this.unlock() - .then(_ => { - - const from = this.page === 0 ? 0 : (this.page - 1) * this.perPage - const to = from + this.perPage - - const accounts = [] - - for (let i = from; i < to; i++) { - - accounts.push({ - address: this._addressFromId(pathBase, i), - balance: 0, - index: i, - }) - } - log.debug(accounts) - resolve(accounts) - }) - .catch(e => { - reject(e) - }) - }) - } - - async getPrevAccountSet () { - this.page-- - return await this.getPage() - } - - async getNextAccountSet () { - this.page++ - return await this.getPage() - } - - getAccounts () { - return Promise.resolve(this.accounts.slice()) - } - - // tx is an instance of the ethereumjs-transaction class. - async signTransaction (address, tx) { - - return new Promise((resolve, reject) => { - - log.debug('sign transaction ', address, tx) - - TrezorConnect.ethereumSignTx( - this._getUnlockedAccount(), - this._normalize(tx.nonce), - this._normalize(tx.gasPrice), - this._normalize(tx.gasLimit), - this._normalize(tx.to), - this._normalize(tx.value), - this._normalize(tx.data), - tx._chainId, - response => { - if (response.success) { - - tx.v = `0x${response.v.toString(16)}` - tx.r = `0x${response.r}` - tx.s = `0x${response.s}` - log.debug('about to create new tx with data', tx) - - const signedTx = new Transaction(tx) - - const addressSignedWith = ethUtil.toChecksumAddress(`0x${signedTx.from.toString('hex')}`) - const correctAddress = ethUtil.toChecksumAddress(address) - if (addressSignedWith !== correctAddress) { - log.error('signature doesnt match the right address', addressSignedWith, correctAddress) - throw new Error('signature doesnt match the right address') - } - - resolve(signedTx) - - } else { - throw new Error(response.error || 'Unknown error') - } - }, - TREZOR_MIN_FIRMWARE_VERSION) - }) - } - - async signMessage (withAccount, data) { - throw new Error('Not supported on this device') - } - - // For personal_sign, we need to prefix the message: - async signPersonalMessage (withAccount, message) { - - TrezorConnect.ethereumSignMessage(this._getUnlockedAccount(), message, response => { - if (response.success) { - - const signature = this._personalToRawSig(response.signature) - const addressSignedWith = sigUtil.recoverPersonalSignature({data: message, sig: signature}) - const correctAddress = ethUtil.toChecksumAddress(withAccount) - if (addressSignedWith !== correctAddress) { - log.error('signature doesnt match the right address', addressSignedWith, correctAddress) - throw new Error('signature doesnt match the right address') - } - return signature - - } else { - throw new Error(response.error || 'Unknown error') - } - - }, TREZOR_MIN_FIRMWARE_VERSION) - } - - async signTypedData (withAccount, typedData) { - // Waiting on trezor to enable this - throw new Error('Not supported on this device') - } - - async exportAccount (address) { - throw new Error('Not supported on this device') - } - - _padLeftEven (hex) { - return hex.length % 2 !== 0 ? `0${hex}` : hex - } - - _normalize (buf) { - return this._padLeftEven(ethUtil.bufferToHex(buf).substring(2).toLowerCase()) - } - - _addressFromId (pathBase, i) { - const dkey = this.hdk.derive(`${pathBase}/${i}`) - const address = ethUtil - .publicToAddress(dkey.publicKey, true) - .toString('hex') - return ethUtil.toChecksumAddress(address) - } - - _getUnlockedAccount () { - return `${this.hdPath}/${this.unlockedAccount}` - } - - _personalToRawSig (signature) { - var v = signature['v'] - 27 - v = v.toString(16) - if (v.length < 2) { - v = '0' + v - } - return '0x' + signature['r'] + signature['s'] + v - } -} - -TrezorKeyring.type = keyringType -module.exports = TrezorKeyring diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index daab5baa5..abe7ff8a2 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -48,7 +48,7 @@ const seedPhraseVerifier = require('./lib/seed-phrase-verifier') const cleanErrorStack = require('./lib/cleanErrorStack') const DiagnosticsReporter = require('./lib/diagnostics-reporter') const log = require('loglevel') -const TrezorKeyring = require('./lib/trezorKeyring') +const TrezorKeyring = require('eth-trezor-keyring') module.exports = class MetamaskController extends EventEmitter { @@ -549,7 +549,8 @@ module.exports = class MetamaskController extends EventEmitter { throw new Error('MetamaskController - No Trezor Hardware Keyring found') } - const accounts = page === -1 ? await keyring.getPrevAccountSet(this.provider) : await keyring.getNextAccountSet(this.provider) + const accounts = await keyring.getPage(page) + this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) return accounts -- cgit v1.2.3 From 704e2a21f8a3fc5f3d6245c5a924cd2df0cfd36e Mon Sep 17 00:00:00 2001 From: Bruno Date: Wed, 13 Jun 2018 02:09:25 -0400 Subject: clean up --- app/scripts/metamask-controller.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 6c380fd71..c57b643bb 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -131,7 +131,7 @@ module.exports = class MetamaskController extends EventEmitter { provider: this.provider, blockTracker: this.blockTracker, }) - + // key mgmt const additionalKeyrings = [TrezorKeyring] this.keyringController = new KeyringController({ @@ -423,7 +423,6 @@ module.exports = class MetamaskController extends EventEmitter { } - //============================================================================= // VAULT / KEYRING RELATED METHODS //============================================================================= @@ -537,19 +536,23 @@ module.exports = class MetamaskController extends EventEmitter { */ async connectHardware (deviceName, page) { - const keyringController = this.keyringController - const keyring = await keyringController.getKeyringsByType( - 'Trezor Hardware' - )[0] - if (!keyring) { - throw new Error('MetamaskController - No Trezor Hardware Keyring found') - } - - const accounts = await keyring.getPage(page) + switch (deviceName) { + case 'trezor': + const keyringController = this.keyringController + const keyring = await keyringController.getKeyringsByType( + 'Trezor Hardware' + )[0] + if (!keyring) { + throw new Error('MetamaskController - No Trezor Hardware Keyring found') + } - this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) + const accounts = await keyring.getPage(page) + this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) + return accounts - return accounts + default: + throw new Error('MetamaskController - Unknown device') + } } /** @@ -581,7 +584,7 @@ module.exports = class MetamaskController extends EventEmitter { const { identities } = this.preferencesController.store.getState() return { ...keyState, identities } } - + // // Account Management @@ -1037,7 +1040,7 @@ module.exports = class MetamaskController extends EventEmitter { * Allows a user to begin the seed phrase recovery process. * @param {Function} cb - A callback function called when complete. */ - markPasswordForgotten(cb) { + markPasswordForgotten (cb) { this.configManager.setPasswordForgotten(true) this.sendUpdate() cb() @@ -1047,7 +1050,7 @@ module.exports = class MetamaskController extends EventEmitter { * Allows a user to end the seed phrase recovery process. * @param {Function} cb - A callback function called when complete. */ - unMarkPasswordForgotten(cb) { + unMarkPasswordForgotten (cb) { this.configManager.setPasswordForgotten(false) this.sendUpdate() cb() -- cgit v1.2.3 From 87dfca07676f7a4745f68d2331a78f3ae53c558f Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Sat, 23 Jun 2018 02:52:11 -0400 Subject: fixes --- app/scripts/metamask-controller.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index c57b643bb..943904e4c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -539,14 +539,14 @@ module.exports = class MetamaskController extends EventEmitter { switch (deviceName) { case 'trezor': const keyringController = this.keyringController - const keyring = await keyringController.getKeyringsByType( + let keyring = await keyringController.getKeyringsByType( 'Trezor Hardware' )[0] if (!keyring) { - throw new Error('MetamaskController - No Trezor Hardware Keyring found') + keyring = await this.keyringController.addNewKeyring('Trezor Hardware') } - const accounts = await keyring.getPage(page) + const accounts = page === 1 ? await keyring.getNextPage() : await keyring.getPreviousPage() this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) return accounts -- cgit v1.2.3 From 7d3da0ae96a761461ee12bf20d8386a5ba7aade2 Mon Sep 17 00:00:00 2001 From: frankiebee Date: Mon, 25 Jun 2018 11:45:00 -0700 Subject: migration 27 - remove rejected transactions from state --- app/scripts/migrations/027.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 app/scripts/migrations/027.js (limited to 'app') diff --git a/app/scripts/migrations/027.js b/app/scripts/migrations/027.js new file mode 100644 index 000000000..d6ebef580 --- /dev/null +++ b/app/scripts/migrations/027.js @@ -0,0 +1,35 @@ +// next version number +const version = 27 + +/* + +normalizes txParams on unconfirmed txs + +*/ +const clone = require('clone') + +module.exports = { + version, + + migrate: async function (originalVersionedData) { + const versionedData = clone(originalVersionedData) + versionedData.meta.version = version + const state = versionedData.data + const newState = transformState(state) + versionedData.data = newState + return versionedData + }, +} + +function transformState (state) { + const newState = state + + if (newState.TransactionController) { + if (newState.TransactionController.transactions) { + const transactions = newState.TransactionController.transactions + newState.TransactionController.transactions = transactions.filter((txMeta) => txMeta.status !== 'rejected') + } + } + + return newState +} -- cgit v1.2.3 From dd18684a5d682f48528f278284619983f13bf82f Mon Sep 17 00:00:00 2001 From: tmashuang Date: Mon, 25 Jun 2018 15:01:14 -0700 Subject: Remove unsued nonce tracker function --- app/scripts/controllers/transactions/nonce-tracker.js | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/transactions/nonce-tracker.js b/app/scripts/controllers/transactions/nonce-tracker.js index 35ca08d6c..06f336eaa 100644 --- a/app/scripts/controllers/transactions/nonce-tracker.js +++ b/app/scripts/controllers/transactions/nonce-tracker.js @@ -129,19 +129,6 @@ class NonceTracker { return Number.isInteger(highest) ? highest + 1 : 0 } - _reduceTxListToUniqueNonces (txList) { - const reducedTxList = txList.reduce((reducedList, txMeta, index) => { - if (!index) return [txMeta] - const nonceMatches = txList.filter((txData) => { - return txMeta.txParams.nonce === txData.txParams.nonce - }) - if (nonceMatches.length > 1) return reducedList - reducedList.push(txMeta) - return reducedList - }, []) - return reducedTxList - } - _getHighestNonce (txList) { const nonces = txList.map((txMeta) => { const nonce = txMeta.txParams.nonce -- cgit v1.2.3 From 9b92ba4c47d5489c5c0f822045360be708110cac Mon Sep 17 00:00:00 2001 From: frankiebee Date: Mon, 25 Jun 2018 15:07:54 -0700 Subject: trandsactions - remove rejected transactions from history --- app/scripts/controllers/transactions/tx-state-manager.js | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'app') diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 0aae4774b..28a18ca2e 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -288,6 +288,7 @@ class TransactionStateManager extends EventEmitter { */ setTxStatusRejected (txId) { this._setTxStatus(txId, 'rejected') + this._removeTx(txId) } /** @@ -422,6 +423,11 @@ class TransactionStateManager extends EventEmitter { _saveTxList (transactions) { this.store.updateState({ transactions }) } + + _removeTx (txId) { + const transactionList = this.getFullTxList() + this._saveTxList(transactionList.filter((txMeta) => txMeta.id !== txId)) + } } module.exports = TransactionStateManager -- cgit v1.2.3 From bfac9c2c2dfd0c5f55cb794214791cf78cca75c0 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 27 Jun 2018 16:29:24 -0400 Subject: detect tokens polling --- app/scripts/controllers/detect-tokens.js | 97 ++++++++++++++++++++++++++++++++ app/scripts/metamask-controller.js | 8 +++ 2 files changed, 105 insertions(+) create mode 100644 app/scripts/controllers/detect-tokens.js (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js new file mode 100644 index 000000000..a7ddace19 --- /dev/null +++ b/app/scripts/controllers/detect-tokens.js @@ -0,0 +1,97 @@ +const ObservableStore = require('obs-store') +const { warn } = require('loglevel') +const contracts = require('eth-contract-metadata') +const { + ROPSTEN, + RINKEBY, + KOVAN, + MAINNET, + OCALHOST, + } = require('./network/enums') + +// By default, poll every 3 minutes +const DEFAULT_INTERVAL = 180 * 1000 + +/** + * A controller that polls for token exchange + * rates based on a user's current token list + */ +class DetectTokensController { + /** + * Creates a DetectTokensController + * + * @param {Object} [config] - Options to configure controller + */ + constructor ({ interval = DEFAULT_INTERVAL, preferences, network } = {}) { + this.preferences = preferences + this.interval = interval + this.network = network + } + + /** + * For each token in eth-contract=metada, find check selectedAddress balance. + * + */ + async exploreNewTokens () { + if (!this.isActive) { return } + if (this._network.getProviderConfig().type !== MAINNET) { return } + var tokens = this._preferences.store.getState().tokens + let detectedTokenAddress, token + for (const address in contracts) { + const contract = contracts[address] + if (contract.erc20 && !(address in tokens)) { + detectedTokenAddress = await this.fetchContractAccountBalance(address) + if (detectedTokenAddress) { + token = contracts[detectedTokenAddress] + this._preferences.addToken(detectedTokenAddress, token['symbol'], token['decimals']) + } + } + // etherscan restriction, 5 request/second, lazy scan + setTimeout(() => {}, 200) + } + } + + /** + * Find if selectedAddress has tokens with contract in contractAddress. + * + * @param {string} contractAddress Hex address of the token contract to explore. + * @returns {string} Contract address to be added to tokens. + * + */ + async fetchContractAccountBalance (contractAddress) { + const address = this._preferences.store.getState().selectedAddress + const response = await fetch(`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${contractAddress}&address=${address}&tag=latest&apikey=NCKS6GTY41KPHWRJB62ES1MDNRBIT174PV`) + const parsedResponse = await response.json() + if (parsedResponse.result !== '0') { + return contractAddress + } + return null + } + + /** + * @type {Number} + */ + set interval (interval) { + this._handle && clearInterval(this._handle) + if (!interval) { return } + this._handle = setInterval(() => { this.exploreNewTokens() }, interval) + } + + /** + * @type {Object} + */ + set preferences (preferences) { + if (!preferences) { return } + this._preferences = preferences + } + + /** + * @type {Object} + */ + set network (network) { + if (!network) { return } + this._network = network + } +} + +module.exports = DetectTokensController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d40a351a5..37a31a28c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -35,6 +35,7 @@ const TypedMessageManager = require('./lib/typed-message-manager') const TransactionController = require('./controllers/transactions') const BalancesController = require('./controllers/computed-balances') const TokenRatesController = require('./controllers/token-rates') +const DetectTokensController = require('./controllers/detect-tokens') const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') @@ -112,6 +113,12 @@ module.exports = class MetamaskController extends EventEmitter { preferences: this.preferencesController.store, }) + // detect tokens controller + this.detectTokensController = new DetectTokensController({ + preferences: this.preferencesController, + network: this.networkController, + }) + this.recentBlocksController = new RecentBlocksController({ blockTracker: this.blockTracker, provider: this.provider, @@ -1276,5 +1283,6 @@ module.exports = class MetamaskController extends EventEmitter { */ set isClientOpenAndUnlocked (active) { this.tokenRatesController.isActive = active + this.detectTokensController.isActive = active } } -- cgit v1.2.3 From 0e863d5fab00eb83c908fb49f2939534bdfe1162 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 27 Jun 2018 19:54:43 -0400 Subject: network store to detect token --- app/scripts/controllers/detect-tokens.js | 18 ++++++++++-------- app/scripts/metamask-controller.js | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index a7ddace19..4d364bd37 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -10,7 +10,7 @@ const { } = require('./network/enums') // By default, poll every 3 minutes -const DEFAULT_INTERVAL = 180 * 1000 +const DEFAULT_INTERVAL = 15 * 1000 /** * A controller that polls for token exchange @@ -26,23 +26,23 @@ class DetectTokensController { this.preferences = preferences this.interval = interval this.network = network + this.contracts = contracts } /** - * For each token in eth-contract=metada, find check selectedAddress balance. + * For each token in eth-contract-metada, find check selectedAddress balance. * */ async exploreNewTokens () { if (!this.isActive) { return } - if (this._network.getProviderConfig().type !== MAINNET) { return } - var tokens = this._preferences.store.getState().tokens + if (this._network.getState().provider.type !== MAINNET) { return } let detectedTokenAddress, token - for (const address in contracts) { - const contract = contracts[address] - if (contract.erc20 && !(address in tokens)) { + for (const address in this.contracts) { + const contract = this.contracts[address] + if (contract.erc20 && !(address in this.tokens)) { detectedTokenAddress = await this.fetchContractAccountBalance(address) if (detectedTokenAddress) { - token = contracts[detectedTokenAddress] + token = this.contracts[detectedTokenAddress] this._preferences.addToken(detectedTokenAddress, token['symbol'], token['decimals']) } } @@ -83,6 +83,8 @@ class DetectTokensController { set preferences (preferences) { if (!preferences) { return } this._preferences = preferences + this.tokens = preferences.store.getState().tokens + } /** diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 37a31a28c..7b60a6c6f 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -116,7 +116,7 @@ module.exports = class MetamaskController extends EventEmitter { // detect tokens controller this.detectTokensController = new DetectTokensController({ preferences: this.preferencesController, - network: this.networkController, + network: this.networkController.store, }) this.recentBlocksController = new RecentBlocksController({ -- cgit v1.2.3 From 6284e664810fe0dcdcb35e55cc57bc11b9298dbb Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 27 Jun 2018 22:18:06 -0400 Subject: tests for spec --- app/scripts/controllers/detect-tokens.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index 4d364bd37..8a6ba9c9a 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -10,7 +10,7 @@ const { } = require('./network/enums') // By default, poll every 3 minutes -const DEFAULT_INTERVAL = 15 * 1000 +const DEFAULT_INTERVAL = 180 * 1000 /** * A controller that polls for token exchange -- cgit v1.2.3 From de01a6f112716cad8ebc1fd56dd304f1818704f4 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 28 Jun 2018 13:17:44 -0230 Subject: Use background gas price estimation method in new ui. --- app/scripts/metamask-controller.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d40a351a5..2055a2dc2 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -338,6 +338,7 @@ module.exports = class MetamaskController extends EventEmitter { markAccountsFound: this.markAccountsFound.bind(this), markPasswordForgotten: this.markPasswordForgotten.bind(this), unMarkPasswordForgotten: this.unMarkPasswordForgotten.bind(this), + getGasPrice: (cb) => cb(null, this.getGasPrice()), // coinbase buyEth: this.buyEth.bind(this), -- cgit v1.2.3 From 11736e6318182ab5b43430410a46059e5f46ad52 Mon Sep 17 00:00:00 2001 From: PhyrexTsai Date: Fri, 29 Jun 2018 13:57:41 +0800 Subject: Update to support pathnames on IPFS hash As @Georgi87 mentioned about pathnames on the domain like `http://phyrextsai.eth/home` will pass to IPFS hash and append `/home` on redirection url. You can try the example below: http://phyrextsai.eth/index.html http://phyrextsai.eth/images/phyrex.jpg --- app/scripts/lib/ipfsContent.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index 46131b266..2ddbe5558 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -3,8 +3,12 @@ const resolver = require('./resolver.js') module.exports = function (provider) { extension.webRequest.onBeforeRequest.addListener(details => { - const name = details.url.substring(7, details.url.length - 1) + const urlhttpreplace = details.url.replace(/\w+?:\/\//, "") + const url = urlhttpreplace.replace(/[\\\/].*/g, "") + let domainhtml = urlhttpreplace.match(/[\\\/].*/g) let clearTime = null + let name = url.replace(/\/$/g, "") + if (domainhtml === null) domainhtml = [""] extension.tabs.getSelected(null, tab => { extension.tabs.update(tab.id, { url: 'loading.html' }) @@ -14,13 +18,13 @@ module.exports = function (provider) { resolver.resolve(name, provider).then(ipfsHash => { clearTimeout(clearTime) - let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + domainhtml[0] return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' }) extension.tabs.update(tab.id, { url: url }) }) .catch(err => { - url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + domainhtml[0] extension.tabs.update(tab.id, {url: url}) return err }) @@ -32,5 +36,5 @@ module.exports = function (provider) { }) }) return { cancel: true } - }, {urls: ['*://*.eth/']}) + }, {urls: ['*://*.eth/', '*://*.eth/*']}) } -- cgit v1.2.3 From 451c05bcbb2a9612cf242caa52c034c0056807c8 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 2 Jul 2018 15:14:05 -0400 Subject: fix environment detection regex --- app/scripts/lib/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/lib/util.js b/app/scripts/lib/util.js index 431d1e59c..51e9036cc 100644 --- a/app/scripts/lib/util.js +++ b/app/scripts/lib/util.js @@ -28,7 +28,7 @@ function getStack () { * */ const getEnvironmentType = (url = window.location.href) => { - if (url.match(/popup.html(?:\?.+)*$/)) { + if (url.match(/popup.html(?:#.*)*$/)) { return ENVIRONMENT_TYPE_POPUP } else if (url.match(/home.html(?:\?.+)*$/) || url.match(/home.html(?:#.*)*$/)) { return ENVIRONMENT_TYPE_FULLSCREEN -- cgit v1.2.3 From 317c3084df9d81d372c3326aa8db1e1e6f0255e3 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 2 Jul 2018 15:14:31 -0400 Subject: allow to open specific route in fullscreen mode --- app/scripts/platforms/extension.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index f5cc255d1..f8dd767dc 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -17,8 +17,11 @@ class ExtensionPlatform { return extension.runtime.getManifest().version } - openExtensionInBrowser () { - const extensionURL = extension.runtime.getURL('home.html') + openExtensionInBrowser (route = null) { + let extensionURL = extension.runtime.getURL('home.html') + if (route) { + extensionURL += `#${route}` + } this.openWindow({ url: extensionURL }) } -- cgit v1.2.3 From f19ffaf08d49f33c395a25faf3eeb6b08d5285a4 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 2 Jul 2018 15:16:05 -0400 Subject: move hardcoded strings to localization file --- app/_locales/en/messages.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 37189ab7f..f10ba01f3 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -11,6 +11,9 @@ "accountName": { "message": "Account Name" }, + "accountSelectionRequired": { + "message": "You need to select an account!" + }, "address": { "message": "Address" }, @@ -125,6 +128,12 @@ "connect": { "message": "Connect" }, + "connecting": { + "message": "Connecting..." + }, + "connectToTrezor": { + "message": "Connect to Trezor" + }, "continue": { "message": "Continue" }, @@ -618,6 +627,9 @@ "popularTokens": { "message": "Popular Tokens" }, + "prev": { + "message": "Prev" + }, "privacyMsg": { "message": "Privacy Policy" }, @@ -793,6 +805,9 @@ "searchTokens": { "message": "Search Tokens" }, + "selectAnAddress": { + "message": "Select an Address" + }, "sendTokensAnywhere": { "message": "Send Tokens to anyone with an Ethereum account" }, -- cgit v1.2.3 From 03fd4355af8ceae9a1d2cad04e12a4f86a9f36b7 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Tue, 3 Jul 2018 12:53:06 -0400 Subject: passtest-lint --- app/scripts/controllers/detect-tokens.js | 6 ------ 1 file changed, 6 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index 8a6ba9c9a..1ea855356 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -1,12 +1,6 @@ -const ObservableStore = require('obs-store') -const { warn } = require('loglevel') const contracts = require('eth-contract-metadata') const { - ROPSTEN, - RINKEBY, - KOVAN, MAINNET, - OCALHOST, } = require('./network/enums') // By default, poll every 3 minutes -- cgit v1.2.3 From 13b03ec090df70512d43e0d6acbe6bf60040a892 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 3 Jul 2018 14:29:36 -0400 Subject: fix lint errors --- app/scripts/background.js | 1 - app/scripts/lib/contracts/registrar.js | 2 +- app/scripts/lib/contracts/resolver.js | 2 +- app/scripts/lib/ipfsContent.js | 10 +++++----- 4 files changed, 7 insertions(+), 8 deletions(-) (limited to 'app') diff --git a/app/scripts/background.js b/app/scripts/background.js index f751867cc..1479d9f72 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -69,7 +69,6 @@ initialize().catch(log.error) setupMetamaskMeshMetrics() - /** * An object representing a transaction, in whatever state it is in. * @typedef TransactionMeta diff --git a/app/scripts/lib/contracts/registrar.js b/app/scripts/lib/contracts/registrar.js index 980a64d7f..99ca24458 100644 --- a/app/scripts/lib/contracts/registrar.js +++ b/app/scripts/lib/contracts/registrar.js @@ -1 +1 @@ -module.exports = [{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"name":"","type":"uint64"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"}] \ No newline at end of file +module.exports = [{'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'resolver', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'owner', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'label', 'type': 'bytes32'}, {'name': 'owner', 'type': 'address'}], 'name': 'setSubnodeOwner', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'ttl', 'type': 'uint64'}], 'name': 'setTTL', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'ttl', 'outputs': [{'name': '', 'type': 'uint64'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'resolver', 'type': 'address'}], 'name': 'setResolver', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'owner', 'type': 'address'}], 'name': 'setOwner', 'outputs': [], 'payable': false, 'type': 'function'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'owner', 'type': 'address'}], 'name': 'Transfer', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'label', 'type': 'bytes32'}, {'indexed': false, 'name': 'owner', 'type': 'address'}], 'name': 'NewOwner', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'resolver', 'type': 'address'}], 'name': 'NewResolver', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'ttl', 'type': 'uint64'}], 'name': 'NewTTL', 'type': 'event'}] diff --git a/app/scripts/lib/contracts/resolver.js b/app/scripts/lib/contracts/resolver.js index f42777cc7..1bf3f90ce 100644 --- a/app/scripts/lib/contracts/resolver.js +++ b/app/scripts/lib/contracts/resolver.js @@ -1,2 +1,2 @@ module.exports = -[{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"name","outputs":[{"name":"ret","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"x","type":"bytes32"},{"indexed":false,"name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"}] \ No newline at end of file +[{'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': 'ret', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': 'ret', 'type': 'address'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': 'ret', 'type': 'string'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes32'}], 'name': 'ContentChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}] diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index 2ddbe5558..a6b99b2f9 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -3,12 +3,12 @@ const resolver = require('./resolver.js') module.exports = function (provider) { extension.webRequest.onBeforeRequest.addListener(details => { - const urlhttpreplace = details.url.replace(/\w+?:\/\//, "") - const url = urlhttpreplace.replace(/[\\\/].*/g, "") - let domainhtml = urlhttpreplace.match(/[\\\/].*/g) + const urlhttpreplace = details.url.replace(/\w+?:\/\//, '') + const url = urlhttpreplace.replace(/[\\/].*/g, '') // eslint-disable-line no-useless-escape + let domainhtml = urlhttpreplace.match(/[\\/].*/g) // eslint-disable-line no-useless-escape let clearTime = null - let name = url.replace(/\/$/g, "") - if (domainhtml === null) domainhtml = [""] + const name = url.replace(/\/$/g, '') + if (domainhtml === null) domainhtml = [''] extension.tabs.getSelected(null, tab => { extension.tabs.update(tab.id, { url: 'loading.html' }) -- cgit v1.2.3 From 9d3f2435e58e2454506ea1a5f7b85452a10edffa Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 3 Jul 2018 15:46:15 -0400 Subject: lint fix --- app/scripts/metamask-controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 6e743d030..962611758 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -532,10 +532,10 @@ module.exports = class MetamaskController extends EventEmitter { 'Trezor Hardware' )[0] if (!keyring) { - keyring = await this.keyringController.addNewKeyring('Trezor Hardware') + keyring = await this.keyringController.addNewKeyring('Trezor Hardware') } - const accounts = page === 1 ? await keyring.getNextPage() : await keyring.getPreviousPage() + const accounts = page === 1 ? await keyring.getNextPage() : await keyring.getPreviousPage() this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) return accounts -- cgit v1.2.3 From f262f0ea64e1e803d8dcd9a31f92cffe81d27b3d Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 22 Jun 2018 14:41:18 -0230 Subject: Update import from seed screen on new ui. --- app/_locales/en/messages.json | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 46fbdc1a7..621775592 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -664,6 +664,9 @@ "restoreVault": { "message": "Restore Vault" }, + "restoreAccountWithSeed": { + "message": "Restore your Account with Seed Phrase" + }, "required": { "message": "Required" }, @@ -673,6 +676,9 @@ "walletSeed": { "message": "Wallet Seed" }, + "restore": { + "message": "Restore" + }, "revealSeedWords": { "message": "Reveal Seed Words" }, @@ -777,6 +783,9 @@ "sendTokens": { "message": "Send Tokens" }, + "separateEachWord": { + "message": "Separate each word with a single space" + }, "onlySendToEtherAddress": { "message": "Only send ETH to an Ethereum address." }, -- cgit v1.2.3 From ba5cde0995f956fb22825d604fe7d664677abaaa Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Thu, 5 Jul 2018 17:04:36 -0400 Subject: move strings to localization file --- app/_locales/en/messages.json | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index f10ba01f3..9c3e43803 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -80,6 +80,9 @@ "borrowDharma": { "message": "Borrow With Dharma (Beta)" }, + "browserNotSupported": { + "message": "Bummer! Your Browser is not supported..." + }, "builtInCalifornia": { "message": "MetaMask is designed and built in California." }, @@ -107,6 +110,9 @@ "close": { "message": "Close" }, + "chromeRequiredForTrezor":{ + "message": "You need to use Metamask on Google Chrome in order to connect to your TREZOR device." + }, "confirm": { "message": "Confirm" }, @@ -259,6 +265,9 @@ "done": { "message": "Done" }, + "downloadGoogleChrome": { + "message": "Download Google Chrome" + }, "downloadStateLogs": { "message": "Download State Logs" }, -- cgit v1.2.3 From 6b2511f94f436a30c6c683f9da2c3142d9a6461c Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Thu, 5 Jul 2018 20:59:31 -0400 Subject: UI refactor --- app/scripts/metamask-controller.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 962611758..1246629be 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -534,8 +534,10 @@ module.exports = class MetamaskController extends EventEmitter { if (!keyring) { keyring = await this.keyringController.addNewKeyring('Trezor Hardware') } - - const accounts = page === 1 ? await keyring.getNextPage() : await keyring.getPreviousPage() + if (page === 0) { + keyring.page = 0 + } + const accounts = page === -1 ? await keyring.getPreviousPage() : await keyring.getNextPage() this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) return accounts -- cgit v1.2.3 From ea9d51e427b8e607e612a01629bebf153e516ad9 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Fri, 22 Jun 2018 23:52:45 -0700 Subject: Refactor and redesign confirm transaction views --- app/_locales/en/messages.json | 21 +++++++++++++++++++++ app/images/alert-red.svg | 14 ++++++++++++++ app/images/alert.svg | 19 +++++++++++++++++++ app/images/caret-left.svg | 18 ++++++++++++++++++ app/scripts/controllers/network/enums.js | 3 ++- .../controllers/transactions/tx-state-manager.js | 22 ++++++++++------------ 6 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 app/images/alert-red.svg create mode 100644 app/images/alert.svg create mode 100644 app/images/caret-left.svg (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 621775592..936af914c 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -40,6 +40,9 @@ "message": "MetaMask", "description": "The name of the application" }, + "approve": { + "message": "Approve" + }, "approved": { "message": "Approved" }, @@ -89,6 +92,9 @@ "buyCoinbaseExplainer": { "message": "Coinbase is the world’s most popular way to buy and sell bitcoin, ethereum, and litecoin." }, + "bytes": { + "message": "Bytes" + }, "ok": { "message": "Ok" }, @@ -149,6 +155,9 @@ "copyContractAddress": { "message": "Copy Contract Address" }, + "copyAddress": { + "message": "Copy address to clipboard" + }, "copyToClipboard": { "message": "Copy to clipboard" }, @@ -318,6 +327,9 @@ "fromShapeShift": { "message": "From ShapeShift" }, + "functionType": { + "message": "Function Type" + }, "gas": { "message": "Gas", "description": "Short indication of gas cost" @@ -370,6 +382,9 @@ "hereList": { "message": "Here's a list!!!!" }, + "hexData": { + "message": "Hex Data" + }, "hide": { "message": "Hide" }, @@ -582,6 +597,9 @@ "message": "or", "description": "choice between creating or importing a new account" }, + "origin": { + "message": "Origin" + }, "password": { "message": "Password" }, @@ -911,6 +929,9 @@ "transactionNumber": { "message": "Transaction Number" }, + "transfer": { + "message": "Transfer" + }, "transfers": { "message": "Transfers" }, diff --git a/app/images/alert-red.svg b/app/images/alert-red.svg new file mode 100644 index 000000000..ac5b30e27 --- /dev/null +++ b/app/images/alert-red.svg @@ -0,0 +1,14 @@ + + + + Artboard Copy + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/app/images/alert.svg b/app/images/alert.svg new file mode 100644 index 000000000..534eda194 --- /dev/null +++ b/app/images/alert.svg @@ -0,0 +1,19 @@ + + + + 7414FFD8-B28A-4593-9D7E-19E73D687B50 + Created with sketchtool. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/images/caret-left.svg b/app/images/caret-left.svg new file mode 100644 index 000000000..0ea266161 --- /dev/null +++ b/app/images/caret-left.svg @@ -0,0 +1,18 @@ + + + + 8439120D-5704-4273-B416-FEE134322584 + Created with sketchtool. + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/scripts/controllers/network/enums.js b/app/scripts/controllers/network/enums.js index 9da7f309c..3190eb37c 100644 --- a/app/scripts/controllers/network/enums.js +++ b/app/scripts/controllers/network/enums.js @@ -4,6 +4,7 @@ const KOVAN = 'kovan' const MAINNET = 'mainnet' const LOCALHOST = 'localhost' +const MAINNET_CODE = 1 const ROPSTEN_CODE = 3 const RINKEYBY_CODE = 4 const KOVAN_CODE = 42 @@ -13,13 +14,13 @@ const RINKEBY_DISPLAY_NAME = 'Rinkeby' const KOVAN_DISPLAY_NAME = 'Kovan' const MAINNET_DISPLAY_NAME = 'Main Ethereum Network' - module.exports = { ROPSTEN, RINKEBY, KOVAN, MAINNET, LOCALHOST, + MAINNET_CODE, ROPSTEN_CODE, RINKEYBY_CODE, KOVAN_CODE, diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 0aae4774b..b4d751830 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -399,19 +399,17 @@ class TransactionStateManager extends EventEmitter { _setTxStatus (txId, status) { const txMeta = this.getTx(txId) txMeta.status = status - setTimeout(() => { - try { - this.updateTx(txMeta, `txStateManager: setting status to ${status}`) - this.emit(`${txMeta.id}:${status}`, txId) - this.emit(`tx:status-update`, txId, status) - if (['submitted', 'rejected', 'failed'].includes(status)) { - this.emit(`${txMeta.id}:finished`, txMeta) - } - this.emit('update:badge') - } catch (error) { - log.error(error) + try { + this.updateTx(txMeta, `txStateManager: setting status to ${status}`) + this.emit(`${txMeta.id}:${status}`, txId) + this.emit(`tx:status-update`, txId, status) + if (['submitted', 'rejected', 'failed'].includes(status)) { + this.emit(`${txMeta.id}:finished`, txMeta) } - }) + this.emit('update:badge') + } catch (error) { + log.error(error) + } } /** -- cgit v1.2.3 From 7732cd4100f3c40f35eeaec669094f63292cbe3d Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Mon, 25 Jun 2018 21:29:16 -0700 Subject: Display newest transaction --- app/scripts/controllers/transactions/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 8e2288aed..2e5546fee 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -157,7 +157,6 @@ class TransactionController extends EventEmitter { // construct txMeta let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) this.addTx(txMeta) - this.emit('newUnapprovedTx', txMeta) try { // check whether recipient account is blacklisted @@ -172,6 +171,7 @@ class TransactionController extends EventEmitter { txMeta.loadingDefaults = false // save txMeta this.txStateManager.updateTx(txMeta) + this.emit('newUnapprovedTx', txMeta) return txMeta } -- cgit v1.2.3 From 9cde5ab11b0670eed7baeb2f31486cb3e253bdcb Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Thu, 28 Jun 2018 11:23:31 -0700 Subject: Use eth-method-registry to get method data --- app/_locales/en/messages.json | 3 +++ app/scripts/controllers/transactions/index.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 936af914c..897f16f04 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -286,6 +286,9 @@ "enterPasswordContinue": { "message": "Enter password to continue" }, + "parameters": { + "message": "Parameters" + }, "passwordNotLongEnough": { "message": "Password not long enough" }, diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 2e5546fee..8e2288aed 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -157,6 +157,7 @@ class TransactionController extends EventEmitter { // construct txMeta let txMeta = this.txStateManager.generateTxMeta({ txParams: normalizedTxParams }) this.addTx(txMeta) + this.emit('newUnapprovedTx', txMeta) try { // check whether recipient account is blacklisted @@ -171,7 +172,6 @@ class TransactionController extends EventEmitter { txMeta.loadingDefaults = false // save txMeta this.txStateManager.updateTx(txMeta) - this.emit('newUnapprovedTx', txMeta) return txMeta } -- cgit v1.2.3 From a2d9c43fba49680d7553409a4f5013d3febd80e9 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Fri, 6 Jul 2018 11:58:41 -0700 Subject: Various fixes from PR comments --- .../controllers/transactions/tx-state-manager.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index b4d751830..0aae4774b 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -399,17 +399,19 @@ class TransactionStateManager extends EventEmitter { _setTxStatus (txId, status) { const txMeta = this.getTx(txId) txMeta.status = status - try { - this.updateTx(txMeta, `txStateManager: setting status to ${status}`) - this.emit(`${txMeta.id}:${status}`, txId) - this.emit(`tx:status-update`, txId, status) - if (['submitted', 'rejected', 'failed'].includes(status)) { - this.emit(`${txMeta.id}:finished`, txMeta) + setTimeout(() => { + try { + this.updateTx(txMeta, `txStateManager: setting status to ${status}`) + this.emit(`${txMeta.id}:${status}`, txId) + this.emit(`tx:status-update`, txId, status) + if (['submitted', 'rejected', 'failed'].includes(status)) { + this.emit(`${txMeta.id}:finished`, txMeta) + } + this.emit('update:badge') + } catch (error) { + log.error(error) } - this.emit('update:badge') - } catch (error) { - log.error(error) - } + }) } /** -- cgit v1.2.3 From dddbb4250b30b7263eb97ddc2e23791166bcc98e Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Fri, 6 Jul 2018 20:04:20 -0400 Subject: update connect harwdware screen --- app/_locales/en/messages.json | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index e1f321c68..658a77e77 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -140,6 +140,12 @@ "connectToTrezor": { "message": "Connect to Trezor" }, + "connectToTrezorHelp": { + "message": "Metamask is able to access your TREZOR ethereum accounts. First make sure your device is connected and unlocked." + }, + "connectToTrezorTrouble": { + "message": "If you are having trouble, make sure you are using the latest version of the TREZOR firmware." + }, "continue": { "message": "Continue" }, @@ -944,6 +950,9 @@ "transfers": { "message": "Transfers" }, + "trezorHardwareWallet": { + "message": "TREZOR Hardware Wallet" + }, "troubleTokenBalances": { "message": "We had trouble loading your token balances. You can view them ", "description": "Followed by a link (here) to view token balances" -- cgit v1.2.3 From 512760154528c47213cc8ff75475c21e3e674a23 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Fri, 6 Jul 2018 20:37:08 -0400 Subject: copy updated --- app/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 658a77e77..4598d14a5 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -144,7 +144,7 @@ "message": "Metamask is able to access your TREZOR ethereum accounts. First make sure your device is connected and unlocked." }, "connectToTrezorTrouble": { - "message": "If you are having trouble, make sure you are using the latest version of the TREZOR firmware." + "message": "If you are having trouble, please make sure you are using the latest version of the TREZOR firmware." }, "continue": { "message": "Continue" -- cgit v1.2.3 From 7cca7ace2ea4cd4b9d3a242067c9a7c344406aba Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 9 Jul 2018 17:24:52 -0400 Subject: fix all the account related bugs --- app/scripts/metamask-controller.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index d70bac1c3..8104374bc 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -529,17 +529,28 @@ module.exports = class MetamaskController extends EventEmitter { switch (deviceName) { case 'trezor': const keyringController = this.keyringController + const oldAccounts = await keyringController.getAccounts() let keyring = await keyringController.getKeyringsByType( 'Trezor Hardware' )[0] if (!keyring) { keyring = await this.keyringController.addNewKeyring('Trezor Hardware') } - if (page === 0) { - keyring.page = 0 + let accounts = [] + + switch (page) { + case -1: + accounts = await keyring.getPreviousPage() + break + case 1: + accounts = await keyring.getNextPage() + break + default: + accounts = await keyring.getFirstPage() } - const accounts = page === -1 ? await keyring.getPreviousPage() : await keyring.getNextPage() - this.accountTracker.syncWithAddresses(accounts.map(a => a.address)) + + // Merge with existing accounts + this.accountTracker.syncWithAddresses(oldAccounts.concat(accounts.map(a => a.address))) return accounts default: -- cgit v1.2.3 From 2de3039b6b21ca05ef185c078b67815448864c72 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 9 Jul 2018 17:55:37 -0400 Subject: fix account duplication --- app/scripts/metamask-controller.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 8104374bc..08b75e839 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -550,7 +550,9 @@ module.exports = class MetamaskController extends EventEmitter { } // Merge with existing accounts - this.accountTracker.syncWithAddresses(oldAccounts.concat(accounts.map(a => a.address))) + // and make sure addresses are not repeated + const accountsToTrack = [...new Set(oldAccounts.concat(accounts.map(a => a.address.toLowerCase())))] + this.accountTracker.syncWithAddresses(accountsToTrack) return accounts default: -- cgit v1.2.3 From d3f793a44a94274c73e0ce770f34bb2e22cdbd5b Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 9 Jul 2018 19:04:30 -0400 Subject: added label for trezor accounts --- app/scripts/metamask-controller.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 08b75e839..71a22f6ec 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -578,7 +578,8 @@ module.exports = class MetamaskController extends EventEmitter { const oldAccounts = await keyringController.getAccounts() const keyState = await keyringController.addNewAccount(keyring) const newAccounts = await keyringController.getAccounts() - + // Assuming the trezor account is the last one + const trezorAccount = newAccounts[newAccounts.length -1] this.preferencesController.setAddresses(newAccounts) newAccounts.forEach(address => { if (!oldAccounts.includes(address)) { @@ -586,6 +587,7 @@ module.exports = class MetamaskController extends EventEmitter { } }) + this.preferencesController.setAccountLabel(trezorAccount, `TREZOR #${index + 1}`) const { identities } = this.preferencesController.store.getState() return { ...keyState, identities } } -- cgit v1.2.3 From 85a4e39b052b8e0c9d277766c79d1a2b5459d934 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 9 Jul 2018 20:54:47 -0400 Subject: fix trezor label --- app/scripts/metamask-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 71a22f6ec..bec02c3ed 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -579,7 +579,7 @@ module.exports = class MetamaskController extends EventEmitter { const keyState = await keyringController.addNewAccount(keyring) const newAccounts = await keyringController.getAccounts() // Assuming the trezor account is the last one - const trezorAccount = newAccounts[newAccounts.length -1] + const trezorAccount = newAccounts[newAccounts.length - 1] this.preferencesController.setAddresses(newAccounts) newAccounts.forEach(address => { if (!oldAccounts.includes(address)) { -- cgit v1.2.3 From 9b81180ab10cf8ca59666104e862c0331e953591 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 10 Jul 2018 00:20:00 -0400 Subject: added ui to remove accounts --- app/_locales/en/messages.json | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 4598d14a5..303a612f1 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -393,6 +393,9 @@ "message": "must be greater than or equal to $1.", "description": "helper for inputting hex as decimal input" }, + "hardware": { + "message": "hardware" + }, "here": { "message": "here", "description": "as in -click here- for more information (goes with troubleTokenBalances)" -- cgit v1.2.3 From b9c2994d24e688305d63aaefd7fac88d88773ad9 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 10 Jul 2018 19:19:29 -0400 Subject: finish warning modal UI --- app/_locales/en/messages.json | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 303a612f1..c4f78d121 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -733,6 +733,15 @@ "revert": { "message": "Revert" }, + "remove": { + "message": "remove" + }, + "removeAccount": { + "message": "Remove account?" + }, + "removeAccountDescription": { + "message": "This account will be removed from your wallet. Please make sure you have the original seed phrase or private key for this imported account before continuing. You can import or create accounts again from the account drop-down. " + }, "rinkeby": { "message": "Rinkeby Test Network" }, -- cgit v1.2.3 From 523cf9ad33d88719520ae5e7293329d133b64d4d Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Wed, 11 Jul 2018 00:20:40 -0400 Subject: account removal is working --- app/scripts/controllers/preferences.js | 24 ++++++++++++++++++++++++ app/scripts/metamask-controller.js | 16 +++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index b314745f5..f6250dc16 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -85,6 +85,30 @@ class PreferencesController { this.store.updateState({ identities }) } + /** + * Removes an address from state + * + * @param {string} address A hex address + * @returns {string} the address that was removed + */ + removeAddress (address) { + const identities = this.store.getState().identities + if (!identities[address]) { + throw new Error(`${address} can't be deleted cause it was not found`) + } + delete identities[address] + this.store.updateState({ identities }) + + // If the selected account is no longer valid, + // select an arbitrary other account: + if (address === this.getSelectedAddress()) { + const selected = Object.keys(identities)[0] + this.setSelectedAddress(selected) + } + return address + } + + /** * Adds addresses to the identities object without removing identities * diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index bec02c3ed..e8f0eba90 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -354,6 +354,7 @@ module.exports = class MetamaskController extends EventEmitter { verifySeedPhrase: nodeify(this.verifySeedPhrase, this), clearSeedWordCache: this.clearSeedWordCache.bind(this), resetAccount: nodeify(this.resetAccount, this), + removeAccount: nodeify(this.removeAccount, this), importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this), // trezor @@ -587,7 +588,8 @@ module.exports = class MetamaskController extends EventEmitter { } }) - this.preferencesController.setAccountLabel(trezorAccount, `TREZOR #${index + 1}`) + this.preferencesController.setAccountLabel(trezorAccount, `TREZOR #${parseInt(index, 10) + 1}`) + this.preferencesController.setSelectedAddress(trezorAccount) const { identities } = this.preferencesController.store.getState() return { ...keyState, identities } } @@ -705,6 +707,18 @@ module.exports = class MetamaskController extends EventEmitter { return selectedAddress } + /** + * Removes a "Loose" account from state. + * + * @param {string[]} address A hex address + * + */ + async removeAccount (address) { + this.preferencesController.removeAddress(address) + return address + } + + /** * Imports an account with the specified import strategy. * These are defined in app/scripts/account-import-strategies -- cgit v1.2.3 From 910713c6b3c5b8f865fdcb989bfe3ee0b14eb364 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Wed, 11 Jul 2018 15:59:05 -0400 Subject: improve tests --- app/scripts/controllers/detect-tokens.js | 29 +++++++++++++---------------- app/scripts/metamask-controller.js | 2 +- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index 1ea855356..fd8412078 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -20,7 +20,6 @@ class DetectTokensController { this.preferences = preferences this.interval = interval this.network = network - this.contracts = contracts } /** @@ -30,18 +29,16 @@ class DetectTokensController { async exploreNewTokens () { if (!this.isActive) { return } if (this._network.getState().provider.type !== MAINNET) { return } - let detectedTokenAddress, token - for (const address in this.contracts) { - const contract = this.contracts[address] - if (contract.erc20 && !(address in this.tokens)) { - detectedTokenAddress = await this.fetchContractAccountBalance(address) - if (detectedTokenAddress) { - token = this.contracts[detectedTokenAddress] - this._preferences.addToken(detectedTokenAddress, token['symbol'], token['decimals']) + let detectedTokenBalance, token + for (const contractAddress in contracts) { + const contract = contracts[contractAddress] + if (contract.erc20 && !(contractAddress in this.tokens)) { + detectedTokenBalance = await this.detectTokenBalance(contractAddress) + if (detectedTokenBalance) { + token = contracts[contractAddress] + this._preferences.addToken(contractAddress, token['symbol'], token['decimals']) } } - // etherscan restriction, 5 request/second, lazy scan - setTimeout(() => {}, 200) } } @@ -49,17 +46,17 @@ class DetectTokensController { * Find if selectedAddress has tokens with contract in contractAddress. * * @param {string} contractAddress Hex address of the token contract to explore. - * @returns {string} Contract address to be added to tokens. + * @returns {boolean} If balance is detected in token contract for address. * */ - async fetchContractAccountBalance (contractAddress) { + async detectTokenBalance (contractAddress) { const address = this._preferences.store.getState().selectedAddress const response = await fetch(`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${contractAddress}&address=${address}&tag=latest&apikey=NCKS6GTY41KPHWRJB62ES1MDNRBIT174PV`) const parsedResponse = await response.json() if (parsedResponse.result !== '0') { - return contractAddress + return true } - return null + return false } /** @@ -81,7 +78,7 @@ class DetectTokensController { } - /** + /** * @type {Object} */ set network (network) { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9a93cf584..eed3fc8e7 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -113,7 +113,7 @@ module.exports = class MetamaskController extends EventEmitter { preferences: this.preferencesController.store, }) - // detect tokens controller + // detect tokens controller this.detectTokensController = new DetectTokensController({ preferences: this.preferencesController, network: this.networkController.store, -- cgit v1.2.3 From 89cc48789af2bb6f0925384abe4d4a53179a3956 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Wed, 11 Jul 2018 20:01:44 -0400 Subject: update to temp dependencies --- app/scripts/metamask-controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index e8f0eba90..cd6fdcc37 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -708,13 +708,18 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * Removes a "Loose" account from state. + * Removes an account from state / storage. * * @param {string[]} address A hex address * */ async removeAccount (address) { + // Remove account from the preferences controller this.preferencesController.removeAddress(address) + // Remove account from the account tracker controller + this.accountTracker.removeAccount(address) + // Remove account from the keyring + await this.keyringController.removeAccount(address) return address } -- cgit v1.2.3 From 80e875308b4447ed38d7e0f677570d73956dd9de Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Wed, 11 Jul 2018 21:21:36 -0400 Subject: forget device and autiload account features added --- app/_locales/en/messages.json | 3 +++ app/scripts/metamask-controller.js | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c4f78d121..8e119d3e4 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -339,6 +339,9 @@ "followTwitter": { "message": "Follow us on Twitter" }, + "forgetDevice": { + "message": "Forget this device" + }, "from": { "message": "From" }, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index cd6fdcc37..b8b7c38e4 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -357,8 +357,12 @@ module.exports = class MetamaskController extends EventEmitter { removeAccount: nodeify(this.removeAccount, this), importAccountWithStrategy: nodeify(this.importAccountWithStrategy, this), - // trezor + // hardware wallets connectHardware: nodeify(this.connectHardware, this), + forgetDevice: nodeify(this.forgetDevice, this), + checkHardwareStatus: nodeify(this.checkHardwareStatus, this), + + // TREZOR unlockTrezorAccount: nodeify(this.unlockTrezorAccount, this), // vault management @@ -561,6 +565,37 @@ module.exports = class MetamaskController extends EventEmitter { } } + async checkHardwareStatus (deviceName) { + + switch (deviceName) { + case 'trezor': + const keyringController = this.keyringController + const keyring = await keyringController.getKeyringsByType( + 'Trezor Hardware' + )[0] + if (!keyring) { + return false + } + return keyring.isUnlocked() + } + } + + async forgetDevice (deviceName) { + + switch (deviceName) { + case 'trezor': + const keyringController = this.keyringController + const keyring = await keyringController.getKeyringsByType( + 'Trezor Hardware' + )[0] + if (!keyring) { + return false + } + keyring.forgetDevice() + return true + } + } + /** * Imports an account from a trezor device. * -- cgit v1.2.3 From 2a0a7853249284cb27831890f3b62847ea27eb83 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Thu, 12 Jul 2018 00:23:08 -0400 Subject: added tooltip --- app/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 8e119d3e4..14d2f923a 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -740,7 +740,7 @@ "message": "remove" }, "removeAccount": { - "message": "Remove account?" + "message": "Remove account" }, "removeAccountDescription": { "message": "This account will be removed from your wallet. Please make sure you have the original seed phrase or private key for this imported account before continuing. You can import or create accounts again from the account drop-down. " -- cgit v1.2.3 From 7581a4906f25c22ee773bd8537f277b28600d6cf Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Thu, 12 Jul 2018 10:51:42 -0230 Subject: Add tooltip to menu icon in tx-view --- app/_locales/en/messages.json | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 897f16f04..4b03c6747 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -515,6 +515,9 @@ "mainnet": { "message": "Main Ethereum Network" }, + "menu": { + "message": "Menu" + }, "message": { "message": "Message" }, -- cgit v1.2.3 From d9f98a704e33e1c626196bb490be66deba0dda69 Mon Sep 17 00:00:00 2001 From: Whymarrh Whitby Date: Thu, 12 Jul 2018 10:58:47 -0230 Subject: Add tooltip to new tab icon in tx-view --- app/_locales/en/messages.json | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 4b03c6747..35e28c087 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -599,6 +599,9 @@ "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." }, + "openInTab": { + "message": "Open in tab" + }, "or": { "message": "or", "description": "choice between creating or importing a new account" -- cgit v1.2.3 From c4ae3995778436f44a5465586cd37aa1addc143a Mon Sep 17 00:00:00 2001 From: Dan Finlay <542863+danfinlay@users.noreply.github.com> Date: Thu, 12 Jul 2018 14:21:32 -0700 Subject: Add harbourair.com to injection block list This site was getting unexpected results when MetaMask was installed. https://consensys.zendesk.com/agent/tickets/2312 --- app/scripts/contentscript.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app') diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index b35a70dd2..04dd51b01 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -177,6 +177,7 @@ function blacklistedDomainCheck () { 'cdn.shopify.com/s/javascripts/tricorder/xtld-read-only-frame.html', 'adyen.com', 'gravityforms.com', + 'harbourair.com', ] var currentUrl = window.location.href var currentRegex -- cgit v1.2.3 From 2fffe098736e2461b9238c7dcd91f9ef3d61dcc1 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 12 Jul 2018 20:43:43 -0400 Subject: detect tokens through infura --- app/scripts/controllers/detect-tokens.js | 45 +++++++++++++++++--------------- app/scripts/metamask-controller.js | 2 +- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index fd8412078..e245a7f9b 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -1,10 +1,12 @@ +const Web3 = require('web3') const contracts = require('eth-contract-metadata') +const { warn } = require('loglevel') const { MAINNET, } = require('./network/enums') - // By default, poll every 3 minutes const DEFAULT_INTERVAL = 180 * 1000 +const ERC20_ABI = [{'constant': true, 'inputs': [{'name': '_owner', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': 'balance', 'type': 'uint256'}], 'payable': false, 'type': 'function'}] /** * A controller that polls for token exchange @@ -28,17 +30,12 @@ class DetectTokensController { */ async exploreNewTokens () { if (!this.isActive) { return } - if (this._network.getState().provider.type !== MAINNET) { return } - let detectedTokenBalance, token + if (this._network.store.getState().provider.type !== MAINNET) { return } + this.web3.setProvider(this._network._provider) for (const contractAddress in contracts) { - const contract = contracts[contractAddress] - if (contract.erc20 && !(contractAddress in this.tokens)) { - detectedTokenBalance = await this.detectTokenBalance(contractAddress) - if (detectedTokenBalance) { - token = contracts[contractAddress] - this._preferences.addToken(contractAddress, token['symbol'], token['decimals']) - } - } + if (contracts[contractAddress].erc20 && !(this.tokenAddresses.includes(contractAddress.toLowerCase()))) { + this.detectTokenBalance(contractAddress) + } } } @@ -46,17 +43,20 @@ class DetectTokensController { * Find if selectedAddress has tokens with contract in contractAddress. * * @param {string} contractAddress Hex address of the token contract to explore. - * @returns {boolean} If balance is detected in token contract for address. + * @returns {boolean} If balance is detected, token is added. * */ async detectTokenBalance (contractAddress) { - const address = this._preferences.store.getState().selectedAddress - const response = await fetch(`https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress=${contractAddress}&address=${address}&tag=latest&apikey=NCKS6GTY41KPHWRJB62ES1MDNRBIT174PV`) - const parsedResponse = await response.json() - if (parsedResponse.result !== '0') { - return true - } - return false + const ethContract = this.web3.eth.contract(ERC20_ABI).at(contractAddress) + ethContract.balanceOf(this.selectedAddress, (error, result) => { + if (!error) { + if (!result.isZero()) { + this._preferences.addToken(contractAddress, contracts[contractAddress].symbol, contracts[contractAddress].decimals) + } + } else { + warn(`MetaMask - DetectTokensController balance fetch failed for ${contractAddress}.`, error) + } + }) } /** @@ -74,8 +74,10 @@ class DetectTokensController { set preferences (preferences) { if (!preferences) { return } this._preferences = preferences - this.tokens = preferences.store.getState().tokens - + this.tokenAddresses = preferences.store.getState().tokens.map((obj) => { return obj.address }) + this.selectedAddress = preferences.store.getState().selectedAddress + preferences.store.subscribe(({ tokens = [] }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) }) + preferences.store.subscribe(({ selectedAddress = [] }) => { this.selectedAddress = selectedAddress }) } /** @@ -84,6 +86,7 @@ class DetectTokensController { set network (network) { if (!network) { return } this._network = network + this.web3 = new Web3(network._provider) } } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index eed3fc8e7..39527ae3b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -116,7 +116,7 @@ module.exports = class MetamaskController extends EventEmitter { // detect tokens controller this.detectTokensController = new DetectTokensController({ preferences: this.preferencesController, - network: this.networkController.store, + network: this.networkController, }) this.recentBlocksController = new RecentBlocksController({ -- cgit v1.2.3 From 53995463883c062157a3d725e7cb8fe54486badb Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Fri, 13 Jul 2018 13:49:20 -0400 Subject: added affiliate link to trezor --- app/_locales/en/messages.json | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index c40a8c3b6..b8b9d9c1c 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -407,6 +407,9 @@ "message": "Get Ether from a faucet for the $1", "description": "Displays network name for Ether faucet" }, + "getYourTrezor": { + "message": "Don't have a TREZOR hardware wallet? Order yours now!" + }, "greaterThanMin": { "message": "must be greater than or equal to $1.", "description": "helper for inputting hex as decimal input" -- cgit v1.2.3 From 3ae5b4e77261035b7cfcec25c9186f6743b66026 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Fri, 13 Jul 2018 19:47:45 -0400 Subject: update label --- app/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index b8b9d9c1c..2b212a522 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -135,7 +135,7 @@ "message": "Confirm Transaction" }, "connectHardware": { - "message": "Connect Hardware" + "message": "Connect Hardware Wallet" }, "connect": { "message": "Connect" -- cgit v1.2.3 From 684fc710ee6db33e3ca4e5c5777874e46ccef3b1 Mon Sep 17 00:00:00 2001 From: Sara Reynolds Date: Mon, 16 Jul 2018 13:02:12 -0700 Subject: Fix edge cases and add translation compatibility --- app/_locales/en/messages.json | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 35e28c087..03f62424c 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -584,6 +584,9 @@ "noDeposits": { "message": "No deposits received" }, + "noConversionRateAvailable":{ + "message": "No Conversion Rate Available" + }, "noTransactionHistory": { "message": "No transaction history." }, -- cgit v1.2.3 From 6be6b9ef29695b405f7d9ef5a2ec780689be4065 Mon Sep 17 00:00:00 2001 From: Dan Finlay <542863+danfinlay@users.noreply.github.com> Date: Mon, 16 Jul 2018 15:39:35 -0700 Subject: Do not inject on blueskybooking.com Per zendesk 2312. --- app/scripts/contentscript.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app') diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 04dd51b01..7c775fb04 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -178,6 +178,7 @@ function blacklistedDomainCheck () { 'adyen.com', 'gravityforms.com', 'harbourair.com', + 'blueskybooking.com', ] var currentUrl = window.location.href var currentRegex -- cgit v1.2.3 From e5512c306ded1d2a521a0ba0d2c3cdd5878e53bb Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Mon, 16 Jul 2018 19:36:08 -0400 Subject: added unit tests for metamaskcontroller --- app/scripts/metamask-controller.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b8b7c38e4..2f114e9f0 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -561,10 +561,15 @@ module.exports = class MetamaskController extends EventEmitter { return accounts default: - throw new Error('MetamaskController - Unknown device') + throw new Error('MetamaskController:connectHardware - Unknown device') } } + /** + * Check if the device is unlocked + * + * @returns {Promise} + */ async checkHardwareStatus (deviceName) { switch (deviceName) { @@ -574,12 +579,19 @@ module.exports = class MetamaskController extends EventEmitter { 'Trezor Hardware' )[0] if (!keyring) { - return false + throw new Error('MetamaskController:checkHardwareStatus - Trezor Hardware keyring not found') } return keyring.isUnlocked() + default: + throw new Error('MetamaskController:checkHardwareStatus - Unknown device') } } + /** + * Clear + * + * @returns {Promise} + */ async forgetDevice (deviceName) { switch (deviceName) { @@ -589,10 +601,12 @@ module.exports = class MetamaskController extends EventEmitter { 'Trezor Hardware' )[0] if (!keyring) { - return false + throw new Error('MetamaskController:forgetDevice - Trezor Hardware keyring not found') } keyring.forgetDevice() return true + default: + throw new Error('MetamaskController:forgetDevice - Unknown device') } } -- cgit v1.2.3 From de4265c629f8e68d882c2ded0e20417327cf4d2f Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 17 Jul 2018 01:17:18 -0400 Subject: added more unit tests --- app/scripts/metamask-controller.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 2f114e9f0..7d3f4c2a8 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -628,17 +628,16 @@ module.exports = class MetamaskController extends EventEmitter { const oldAccounts = await keyringController.getAccounts() const keyState = await keyringController.addNewAccount(keyring) const newAccounts = await keyringController.getAccounts() - // Assuming the trezor account is the last one - const trezorAccount = newAccounts[newAccounts.length - 1] this.preferencesController.setAddresses(newAccounts) + console.log('new vs old', newAccounts, oldAccounts) newAccounts.forEach(address => { if (!oldAccounts.includes(address)) { + console.log('new address found', address) + this.preferencesController.setAccountLabel(address, `TREZOR #${parseInt(index, 10) + 1}`) this.preferencesController.setSelectedAddress(address) } }) - this.preferencesController.setAccountLabel(trezorAccount, `TREZOR #${parseInt(index, 10) + 1}`) - this.preferencesController.setSelectedAddress(trezorAccount) const { identities } = this.preferencesController.store.getState() return { ...keyState, identities } } -- cgit v1.2.3 From e89350b19fdac56968303e5c48806a4605fb4b22 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 17 Jul 2018 01:44:28 -0400 Subject: added tests for removeAccount --- app/scripts/metamask-controller.js | 2 -- 1 file changed, 2 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 7d3f4c2a8..575c591fa 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -629,10 +629,8 @@ module.exports = class MetamaskController extends EventEmitter { const keyState = await keyringController.addNewAccount(keyring) const newAccounts = await keyringController.getAccounts() this.preferencesController.setAddresses(newAccounts) - console.log('new vs old', newAccounts, oldAccounts) newAccounts.forEach(address => { if (!oldAccounts.includes(address)) { - console.log('new address found', address) this.preferencesController.setAccountLabel(address, `TREZOR #${parseInt(index, 10) + 1}`) this.preferencesController.setSelectedAddress(address) } -- cgit v1.2.3 From d19c42fcaeea933793ed459ab5248b42811a0498 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Sat, 14 Jul 2018 13:47:07 -0700 Subject: Add fallback when no function found, fix network colors, add fiat values for tokens with contract exchange rates --- app/_locales/en/messages.json | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 03f62424c..a87753274 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -581,6 +581,9 @@ "noAddressForName": { "message": "No address has been set for this name." }, + "noConversionRate": { + "message": "No Conversion Rate" + }, "noDeposits": { "message": "No deposits received" }, @@ -593,6 +596,9 @@ "noTransactions": { "message": "No Transactions" }, + "notFound": { + "message": "Not Found" + }, "notStarted": { "message": "Not Started" }, @@ -972,6 +978,9 @@ "unknown": { "message": "Unknown" }, + "unknownFunction": { + "message": "Unknown Function" + }, "unknownNetwork": { "message": "Unknown Private Network" }, -- cgit v1.2.3 From 301ae305b88d0a7e3a3cbca9a500f4b39753dc52 Mon Sep 17 00:00:00 2001 From: Alexander Tseung Date: Tue, 17 Jul 2018 13:43:15 -0700 Subject: Use Number constructor for number conversion. Use existing noConversionRateAvailable message --- app/_locales/en/messages.json | 3 --- 1 file changed, 3 deletions(-) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index a87753274..6da058786 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -581,9 +581,6 @@ "noAddressForName": { "message": "No address has been set for this name." }, - "noConversionRate": { - "message": "No Conversion Rate" - }, "noDeposits": { "message": "No deposits received" }, -- cgit v1.2.3 From cb97517b26a7732cbb7c4a9f30f85b5fa596e608 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 17 Jul 2018 18:53:37 -0400 Subject: updated account list based on new designs --- app/_locales/en/messages.json | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 564c12f86..f0927af9c 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -877,6 +877,12 @@ "selectAnAddress": { "message": "Select an Address" }, + "selectAnAccount": { + "message": "Select an Account" + }, + "selectAnAccountHelp": { + "message": "These are the accounts available in your hardware wallet. Select the one you’d like to use in MetaMask." + }, "sendTokensAnywhere": { "message": "Send Tokens to anyone with an Ethereum account" }, -- cgit v1.2.3 From aa5a987765677b4945e9eefe03cae8dcc93318cd Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 17 Jul 2018 21:54:04 -0400 Subject: added some e2e tests --- app/scripts/metamask-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 575c591fa..dc5c24b1b 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -579,7 +579,7 @@ module.exports = class MetamaskController extends EventEmitter { 'Trezor Hardware' )[0] if (!keyring) { - throw new Error('MetamaskController:checkHardwareStatus - Trezor Hardware keyring not found') + return false } return keyring.isUnlocked() default: -- cgit v1.2.3 From cbb14f1d5e50c10865838a98452ecfb4b6cb8d6a Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Tue, 17 Jul 2018 21:57:19 -0400 Subject: fix browser not supported screen --- app/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 8619d6716..069db400d 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -84,7 +84,7 @@ "message": "Borrow With Dharma (Beta)" }, "browserNotSupported": { - "message": "Bummer! Your Browser is not supported..." + "message": "Your Browser is not supported..." }, "builtInCalifornia": { "message": "MetaMask is designed and built in California." -- cgit v1.2.3 From dd9e3587d47c9a233cea5f7b305d8faa08ee6a0e Mon Sep 17 00:00:00 2001 From: Dan Finlay <542863+danfinlay@users.noreply.github.com> Date: Wed, 18 Jul 2018 15:23:32 -0700 Subject: Do not inject on ani.gamer.com.tw Fixes #4799 --- app/scripts/contentscript.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app') diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js index 04dd51b01..f2a2469c9 100644 --- a/app/scripts/contentscript.js +++ b/app/scripts/contentscript.js @@ -178,6 +178,7 @@ function blacklistedDomainCheck () { 'adyen.com', 'gravityforms.com', 'harbourair.com', + 'ani.gamer.com.tw', ] var currentUrl = window.location.href var currentRegex -- cgit v1.2.3 From 49d1bdea8a47139cc814d3c49aa97bf2d542d3d5 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Wed, 18 Jul 2018 22:57:47 -0400 Subject: design done --- app/_locales/en/messages.json | 44 +++++++++++++++++-- app/images/hardware-wallet-step-1.svg | 44 +++++++++++++++++++ app/images/hardware-wallet-step-2.svg | 81 +++++++++++++++++++++++++++++++++++ app/images/hardware-wallet-step-3.svg | 42 ++++++++++++++++++ 4 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 app/images/hardware-wallet-step-1.svg create mode 100644 app/images/hardware-wallet-step-2.svg create mode 100644 app/images/hardware-wallet-step-3.svg (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 069db400d..f9f01f040 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -134,7 +134,7 @@ "confirmTransaction": { "message": "Confirm Transaction" }, - "connectHardware": { + "connectHardwareWallet": { "message": "Connect Hardware Wallet" }, "connect": { @@ -286,6 +286,9 @@ "downloadStateLogs": { "message": "Download State Logs" }, + "dontHaveATrezorWallet": { + "message": "Don't have a TREZOR hardware wallet?" + }, "dropped": { "message": "Dropped" }, @@ -407,8 +410,8 @@ "message": "Get Ether from a faucet for the $1", "description": "Displays network name for Ether faucet" }, - "getYourTrezor": { - "message": "Don't have a TREZOR hardware wallet? Order yours now!" + "getHelp": { + "message": "Get Help." }, "greaterThanMin": { "message": "must be greater than or equal to $1.", @@ -417,6 +420,15 @@ "hardware": { "message": "hardware" }, + "hardwareSupport": { + "message": "Hardware Support" + }, + "hardwareSupportMsg": { + "message": "You can now view your Hardware accounts in MetaMask! Scroll down and read how it works." + }, + "havingTroubleConnecting": { + "message": "Having trouble connecting?" + }, "here": { "message": "here", "description": "as in -click here- for more information (goes with troubleTokenBalances)" @@ -515,7 +527,7 @@ "message": "Max" }, "learnMore": { - "message": "Learn more." + "message": "Learn more" }, "lessThanMax": { "message": "must be less than or equal to $1.", @@ -778,6 +790,9 @@ "removeAccountDescription": { "message": "This account will be removed from your wallet. Please make sure you have the original seed phrase or private key for this imported account before continuing. You can import or create accounts again from the account drop-down. " }, + "readyToConnect": { + "message": "Ready to Connect?" + }, "rinkeby": { "message": "Rinkeby Test Network" }, @@ -874,6 +889,9 @@ "message": "Only send $1 to an Ethereum account address.", "description": "displays token symbol" }, + "orderOneHere": { + "message": "Order one here." + }, "searchTokens": { "message": "Search Tokens" }, @@ -892,6 +910,24 @@ "settings": { "message": "Settings" }, + "step1HardwareWallet": { + "message": "1. Connect Hardware Wallet" + }, + "step1HardwareWalletMsg": { + "message": "Connect your hardware wallet directly to your computer." + }, + "step2HardwareWallet": { + "message": "2. Select an Account" + }, + "step2HardwareWalletMsg": { + "message": "Select the account you want to view. You can only choose one at a time." + }, + "step3HardwareWallet": { + "message": "3. Start using dApps and more!" + }, + "step3HardwareWalletMsg": { + "message": "Use your hardware account like you would with any Ethereum account. Log in to dApps, send Eth, buy and store ERC20 tokens and Non-Fungible tokens like CryptoKitties." + }, "info": { "message": "Info" }, diff --git a/app/images/hardware-wallet-step-1.svg b/app/images/hardware-wallet-step-1.svg new file mode 100644 index 000000000..2b6596a43 --- /dev/null +++ b/app/images/hardware-wallet-step-1.svg @@ -0,0 +1,44 @@ + + + + 2981A924-C7CB-4957-87AD-8C680802DAD7 + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/images/hardware-wallet-step-2.svg b/app/images/hardware-wallet-step-2.svg new file mode 100644 index 000000000..9fff05b7e --- /dev/null +++ b/app/images/hardware-wallet-step-2.svg @@ -0,0 +1,81 @@ + + + + 27B850D0-B3BA-4F98-8BB4-B542D8BFDE3B + Created with sketchtool. + + + + + + + + + + + + + + + + + + 3 + + + OXz3…T3A4 + + + 0.020000 ETH + + + + + + + 1 + + + OXa4…s0a2 + + + 0.01500 ETH + + + + + + + 4 + + + OXd2…D0V4 + + + 0.030000 ETH + + + + + + + + + + + 2 + + + OXe7…B0a1 + + + 0.041000 ETH + + + + + + + + + \ No newline at end of file diff --git a/app/images/hardware-wallet-step-3.svg b/app/images/hardware-wallet-step-3.svg new file mode 100644 index 000000000..4a7655b3b --- /dev/null +++ b/app/images/hardware-wallet-step-3.svg @@ -0,0 +1,42 @@ + + + + CEB55C41-7BCE-405E-83CD-834B388B495F + Created with sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LOGIN WITH + METAMASK + + + + + + \ No newline at end of file -- cgit v1.2.3 From 1a9748667daca9db496fe0090f6086b095cb5def Mon Sep 17 00:00:00 2001 From: Jenny Pollack Date: Thu, 19 Jul 2018 02:22:56 -0400 Subject: sentry - add detection for Brave Browser --- app/scripts/background.js | 6 ++++-- app/scripts/lib/setupRaven.js | 9 +++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/scripts/background.js b/app/scripts/background.js index 1479d9f72..4f877af57 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -27,6 +27,7 @@ const EdgeEncryptor = require('./edge-encryptor') const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code') const getObjStructure = require('./lib/getObjStructure') const ipfsContent = require('./lib/ipfsContent.js') +const assert = require('assert') const { ENVIRONMENT_TYPE_POPUP, @@ -44,8 +45,9 @@ const notificationManager = new NotificationManager() global.METAMASK_NOTIFIER = notificationManager // setup sentry error reporting -const release = platform.getVersion() -const raven = setupRaven({ release }) +const releaseVersion = platform.getVersion() +const raven = setupRaven({ releaseVersion }) +assert.equal('test', 'notest') // browser check if it is Edge - https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser // Internet Explorer 6-11 diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js index 3f69fb3bb..683ab52df 100644 --- a/app/scripts/lib/setupRaven.js +++ b/app/scripts/lib/setupRaven.js @@ -8,8 +8,10 @@ module.exports = setupRaven // Setup raven / sentry remote error reporting function setupRaven (opts) { - const { release } = opts + const { releaseVersion } = opts let ravenTarget + // detect brave + const isBrave = Boolean(window.chrome.ipcRenderer) if (METAMASK_DEBUG) { console.log('Setting up Sentry Remote Error Reporting: DEV') @@ -20,10 +22,13 @@ function setupRaven (opts) { } const client = Raven.config(ravenTarget, { - release, + releaseVersion, transport: function (opts) { + opts.data.extra.isBrave = isBrave const report = opts.data + try { + console.log('testing opts in transport sentry') // handle error-like non-error exceptions rewriteErrorLikeExceptions(report) // simplify certain complex error messages (e.g. Ethjs) -- cgit v1.2.3 From df19163bf9611d75aaf8ea6da52651dbba9a5e00 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Thu, 19 Jul 2018 02:31:13 -0400 Subject: last css fixes --- app/_locales/en/messages.json | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index f9f01f040..08d1d33ae 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -420,6 +420,9 @@ "hardware": { "message": "hardware" }, + "hardwareWalletConnected": { + "message": "Hardware wallet connected" + }, "hardwareSupport": { "message": "Hardware Support" }, -- cgit v1.2.3 From 8df433ca8476e14b08fc95be973283d0200f8458 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Sun, 24 Jun 2018 23:50:55 -0400 Subject: Add support for .test Ropsten ENS domains --- app/manifest.json | 3 ++- app/scripts/background.js | 7 ++++++- app/scripts/lib/resolver.js | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/manifest.json b/app/manifest.json index a226adfb0..b67cef025 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -62,7 +62,8 @@ "https://*.infura.io/", "activeTab", "webRequest", - "*://*.eth/" + "*://*.eth/", + "*://*.test/" ], "web_accessible_resources": [ "inpage.js" diff --git a/app/scripts/background.js b/app/scripts/background.js index 1479d9f72..3d3afdd4e 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -53,6 +53,7 @@ const isIE = !!document.documentMode // Edge 20+ const isEdge = !isIE && !!window.StyleMedia +let ipfsHandle let popupIsOpen = false let notificationIsOpen = false const openMetamaskTabsIDs = {} @@ -158,7 +159,7 @@ async function initialize () { const initLangCode = await getFirstPreferredLangCode() await setupController(initState, initLangCode) log.debug('MetaMask initialization complete.') - ipfsContent(initState.NetworkController.provider) + ipfsHandle = ipfsContent(initState.NetworkController.provider) } // @@ -262,6 +263,10 @@ function setupController (initState, initLangCode) { }) global.metamaskController = controller + controller.networkController.on('networkDidChange', () => { + ipfsHandle && ipfsHandle.remove() + ipfsHandle = ipfsContent(controller.networkController.providerStore.getState()) + }) // report failed transactions to Sentry controller.txController.on(`tx:status-update`, (txId, status) => { diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js index 6786929d8..cbda0c028 100644 --- a/app/scripts/lib/resolver.js +++ b/app/scripts/lib/resolver.js @@ -61,7 +61,7 @@ function getRegistrar (type) { module.exports.resolve = function (name, provider) { const path = name.split('.') const tld = path[path.length - 1] - if (tld === 'eth') { + if (tld === 'eth' || tld === 'test') { return ens(name, provider) } else { return new Promise((resolve, reject) => { -- cgit v1.2.3 From 94f99b6ed0385e90fda07c6ef5b5ceaf654fb666 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Sun, 15 Jul 2018 14:34:48 -0400 Subject: Updates --- app/scripts/lib/ipfsContent.js | 70 ++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 33 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index a6b99b2f9..15d25df20 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -2,39 +2,43 @@ const extension = require('extensionizer') const resolver = require('./resolver.js') module.exports = function (provider) { - extension.webRequest.onBeforeRequest.addListener(details => { - const urlhttpreplace = details.url.replace(/\w+?:\/\//, '') - const url = urlhttpreplace.replace(/[\\/].*/g, '') // eslint-disable-line no-useless-escape - let domainhtml = urlhttpreplace.match(/[\\/].*/g) // eslint-disable-line no-useless-escape - let clearTime = null - const name = url.replace(/\/$/g, '') - if (domainhtml === null) domainhtml = [''] - extension.tabs.getSelected(null, tab => { - extension.tabs.update(tab.id, { url: 'loading.html' }) + function ipfsContent(details) { + const name = details.url.substring(7, details.url.length - 1) + let clearTime = null + extension.tabs.getSelected(null, tab => { + extension.tabs.update(tab.id, { url: 'loading.html' }) - clearTime = setTimeout(() => { - return extension.tabs.update(tab.id, { url: '404.html' }) - }, 60000) + clearTime = setTimeout(() => { + return extension.tabs.update(tab.id, { url: '404.html' }) + }, 60000) - resolver.resolve(name, provider).then(ipfsHash => { - clearTimeout(clearTime) - let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + domainhtml[0] - return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { - if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' }) - extension.tabs.update(tab.id, { url: url }) - }) - .catch(err => { - url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + domainhtml[0] - extension.tabs.update(tab.id, {url: url}) - return err - }) - }) - .catch(err => { - clearTimeout(clearTime) - const url = err === 'unsupport' ? 'unsupport' : 'error' - extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) - }) - }) - return { cancel: true } - }, {urls: ['*://*.eth/', '*://*.eth/*']}) + resolver.resolve(name, provider).then(ipfsHash => { + clearTimeout(clearTime) + let url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + return fetch(url, { method: 'HEAD' }).then(response => response.status).then(statusCode => { + if (statusCode !== 200) return extension.tabs.update(tab.id, { url: '404.html' }) + extension.tabs.update(tab.id, { url: url }) + }) + .catch(err => { + url = 'https://ipfs.infura.io/ipfs/' + ipfsHash + extension.tabs.update(tab.id, {url: url}) + return err + }) + }) + .catch(err => { + clearTimeout(clearTime) + const url = err === 'unsupport' ? 'unsupport' : 'error' + extension.tabs.update(tab.id, {url: `${url}.html?name=${name}`}) + }) + }) + return { cancel: true } + } + + extension.webRequest.onBeforeRequest.addListener(ipfsContent, {urls: ['*://*.eth/', '*://*.test/']}) + + return { + remove () { + extension.webRequest.onBeforeRequest.removeListener(ipfsContent) + }, + } } -- cgit v1.2.3 From d02c160db5525caaccf9c137076f9ef8948b7080 Mon Sep 17 00:00:00 2001 From: bitpshr Date: Sun, 15 Jul 2018 14:37:16 -0400 Subject: Updates for linting --- app/scripts/lib/ipfsContent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app') diff --git a/app/scripts/lib/ipfsContent.js b/app/scripts/lib/ipfsContent.js index 15d25df20..5222151ea 100644 --- a/app/scripts/lib/ipfsContent.js +++ b/app/scripts/lib/ipfsContent.js @@ -2,7 +2,7 @@ const extension = require('extensionizer') const resolver = require('./resolver.js') module.exports = function (provider) { - function ipfsContent(details) { + function ipfsContent (details) { const name = details.url.substring(7, details.url.length - 1) let clearTime = null extension.tabs.getSelected(null, tab => { -- cgit v1.2.3 From d24b5b76fdd02c47942cbc20b471718bd7bda91d Mon Sep 17 00:00:00 2001 From: bitpshr Date: Mon, 16 Jul 2018 09:05:08 -0400 Subject: Update variable name --- app/scripts/lib/resolver.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/lib/resolver.js b/app/scripts/lib/resolver.js index cbda0c028..ff0fed161 100644 --- a/app/scripts/lib/resolver.js +++ b/app/scripts/lib/resolver.js @@ -60,8 +60,8 @@ function getRegistrar (type) { module.exports.resolve = function (name, provider) { const path = name.split('.') - const tld = path[path.length - 1] - if (tld === 'eth' || tld === 'test') { + const topLevelDomain = path[path.length - 1] + if (topLevelDomain === 'eth' || topLevelDomain === 'test') { return ens(name, provider) } else { return new Promise((resolve, reject) => { -- cgit v1.2.3 From 16fc7efef17aa9579f6b7ac8fe33026511301f20 Mon Sep 17 00:00:00 2001 From: Jenny Pollack Date: Thu, 19 Jul 2018 09:59:58 -0400 Subject: remove local tests --- app/scripts/background.js | 2 -- app/scripts/lib/setupRaven.js | 1 - 2 files changed, 3 deletions(-) (limited to 'app') diff --git a/app/scripts/background.js b/app/scripts/background.js index 4f877af57..e07b6b38a 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -27,7 +27,6 @@ const EdgeEncryptor = require('./edge-encryptor') const getFirstPreferredLangCode = require('./lib/get-first-preferred-lang-code') const getObjStructure = require('./lib/getObjStructure') const ipfsContent = require('./lib/ipfsContent.js') -const assert = require('assert') const { ENVIRONMENT_TYPE_POPUP, @@ -47,7 +46,6 @@ global.METAMASK_NOTIFIER = notificationManager // setup sentry error reporting const releaseVersion = platform.getVersion() const raven = setupRaven({ releaseVersion }) -assert.equal('test', 'notest') // browser check if it is Edge - https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser // Internet Explorer 6-11 diff --git a/app/scripts/lib/setupRaven.js b/app/scripts/lib/setupRaven.js index 683ab52df..e657e278f 100644 --- a/app/scripts/lib/setupRaven.js +++ b/app/scripts/lib/setupRaven.js @@ -28,7 +28,6 @@ function setupRaven (opts) { const report = opts.data try { - console.log('testing opts in transport sentry') // handle error-like non-error exceptions rewriteErrorLikeExceptions(report) // simplify certain complex error messages (e.g. Ethjs) -- cgit v1.2.3 From 3b97d816ffcaebc7606d4564ea95918f647ba413 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 19 Jul 2018 15:56:38 -0400 Subject: detect tokens when submit password and new account selected --- app/scripts/controllers/detect-tokens.js | 52 ++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 13 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index e245a7f9b..db21f7489 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -1,9 +1,7 @@ const Web3 = require('web3') const contracts = require('eth-contract-metadata') const { warn } = require('loglevel') -const { - MAINNET, - } = require('./network/enums') +const { MAINNET } = require('./network/enums') // By default, poll every 3 minutes const DEFAULT_INTERVAL = 180 * 1000 const ERC20_ABI = [{'constant': true, 'inputs': [{'name': '_owner', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': 'balance', 'type': 'uint256'}], 'payable': false, 'type': 'function'}] @@ -22,14 +20,15 @@ class DetectTokensController { this.preferences = preferences this.interval = interval this.network = network + this._isActive = false } - /** + /** * For each token in eth-contract-metada, find check selectedAddress balance. * */ - async exploreNewTokens () { - if (!this.isActive) { return } + async detectNewTokens () { + if (!this._isActive) { return } if (this._network.store.getState().provider.type !== MAINNET) { return } this.web3.setProvider(this._network._provider) for (const contractAddress in contracts) { @@ -59,28 +58,44 @@ class DetectTokensController { }) } + /** + * Restart token detection polling period and call detectNewTokens + * in case of address change or user session initialization. + * + */ + restartTokenDetection () { + if (this._isActive && this.selectedAddress) { + this.detectNewTokens() + this.interval = DEFAULT_INTERVAL + } + } + /** * @type {Number} */ set interval (interval) { this._handle && clearInterval(this._handle) if (!interval) { return } - this._handle = setInterval(() => { this.exploreNewTokens() }, interval) + this._handle = setInterval(() => { this.detectNewTokens() }, interval) } - /** + /** + * In setter when selectedAddress is changed, detectNewTokens and restart polling * @type {Object} */ set preferences (preferences) { if (!preferences) { return } this._preferences = preferences - this.tokenAddresses = preferences.store.getState().tokens.map((obj) => { return obj.address }) - this.selectedAddress = preferences.store.getState().selectedAddress - preferences.store.subscribe(({ tokens = [] }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) }) - preferences.store.subscribe(({ selectedAddress = [] }) => { this.selectedAddress = selectedAddress }) + preferences.store.subscribe(({ tokens }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) }) + preferences.store.subscribe(({ selectedAddress }) => { + if (this.selectedAddress !== selectedAddress) { + this.selectedAddress = selectedAddress + this.restartTokenDetection() + } + }) } - /** + /** * @type {Object} */ set network (network) { @@ -88,6 +103,17 @@ class DetectTokensController { this._network = network this.web3 = new Web3(network._provider) } + + /** + * In setter, when _isActive is changed, detectNewTokens and restart polling + * @type {Object} + */ + set isActive (active) { + if (this._isActive !== active) { + this._isActive = active + this.restartTokenDetection() + } + } } module.exports = DetectTokensController -- cgit v1.2.3 From 6921f94bfe11e618dbe586e608a5b6aeaa8feadf Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Thu, 19 Jul 2018 22:34:06 +0200 Subject: initial test --- app/manifest.json | 3 ++- .../lib/transaction-notification-manager.js | 27 ++++++++++++++++++++++ .../controllers/transactions/tx-state-manager.js | 3 +++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 app/scripts/controllers/transactions/lib/transaction-notification-manager.js (limited to 'app') diff --git a/app/manifest.json b/app/manifest.json index b67cef025..52256c5b7 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -63,7 +63,8 @@ "activeTab", "webRequest", "*://*.eth/", - "*://*.test/" + "*://*.test/", + "notifications" ], "web_accessible_resources": [ "inpage.js" diff --git a/app/scripts/controllers/transactions/lib/transaction-notification-manager.js b/app/scripts/controllers/transactions/lib/transaction-notification-manager.js new file mode 100644 index 000000000..6bdf40b62 --- /dev/null +++ b/app/scripts/controllers/transactions/lib/transaction-notification-manager.js @@ -0,0 +1,27 @@ +const extension = require('extensionizer') + +// Confirmed tx +// Transaction ${tx.nonce} confirmed! View on Etherscan + +// Failed tx +// Transaction ${tx.nonce} failed. (Maybe append tx.error.message) + +// Dropped tx +// A Transaction ${tx.nonce} was dropped, because another transaction with that number was successfully processed. + +function showConfirmedNotification (txMeta) { + extension.notifications.create({ + "type": "basic", + "title": "Confirmed transaction", + "iconUrl": extension.extension.getURL('../../../../images/icon-64.png'), + "message": JSON.stringify(txMeta) + }); +} + + +/** +@module +*/ +module.exports = { + showConfirmedNotification +} \ No newline at end of file diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 28a18ca2e..72da45913 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -5,6 +5,7 @@ const ethUtil = require('ethereumjs-util') const log = require('loglevel') const txStateHistoryHelper = require('./lib/tx-state-history-helper') const createId = require('../../lib/random-id') +const transactionNotificationManager = require('./lib/transaction-notification-manager') const { getFinalStates } = require('./lib/util') /** TransactionStateManager is responsible for the state of a transaction and @@ -332,6 +333,8 @@ class TransactionStateManager extends EventEmitter { */ setTxStatusConfirmed (txId) { this._setTxStatus(txId, 'confirmed') + const txMeta = this.getTx(txId) + transactionNotificationManager.showConfirmedNotification(txMeta) } /** -- cgit v1.2.3 From 009b1cefbe3d19dcad01078927b1a55c3439b22f Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Thu, 19 Jul 2018 19:46:46 -0400 Subject: keyring unlocked detect and unit tests --- app/scripts/controllers/detect-tokens.js | 28 ++++++++++++++++------------ app/scripts/metamask-controller.js | 16 +++++++++------- 2 files changed, 25 insertions(+), 19 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index db21f7489..f1810cfa1 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -16,11 +16,11 @@ class DetectTokensController { * * @param {Object} [config] - Options to configure controller */ - constructor ({ interval = DEFAULT_INTERVAL, preferences, network } = {}) { + constructor ({ interval = DEFAULT_INTERVAL, preferences, network, keyringMemStore } = {}) { this.preferences = preferences this.interval = interval this.network = network - this._isActive = false + this.keyringMemStore = keyringMemStore } /** @@ -28,7 +28,7 @@ class DetectTokensController { * */ async detectNewTokens () { - if (!this._isActive) { return } + if (!this.isActive) { return } if (this._network.store.getState().provider.type !== MAINNET) { return } this.web3.setProvider(this._network._provider) for (const contractAddress in contracts) { @@ -64,7 +64,7 @@ class DetectTokensController { * */ restartTokenDetection () { - if (this._isActive && this.selectedAddress) { + if (this.isActive && this.selectedAddress) { this.detectNewTokens() this.interval = DEFAULT_INTERVAL } @@ -105,15 +105,19 @@ class DetectTokensController { } /** - * In setter, when _isActive is changed, detectNewTokens and restart polling - * @type {Object} - */ - set isActive (active) { - if (this._isActive !== active) { - this._isActive = active - this.restartTokenDetection() + * In setter when isUnlocked is updated to true, detectNewTokens and restart polling + * @type {Object} + */ + set keyringMemStore (keyringMemStore) { + if (!keyringMemStore) { return } + this._keyringMemStore = keyringMemStore + this._keyringMemStore.subscribe(({ isUnlocked }) => { + if (this.isUnlocked !== isUnlocked) { + if (isUnlocked) { this.restartTokenDetection() } + this.isUnlocked = isUnlocked } - } + }) + } } module.exports = DetectTokensController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 39527ae3b..4e97ce583 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -113,12 +113,6 @@ module.exports = class MetamaskController extends EventEmitter { preferences: this.preferencesController.store, }) - // detect tokens controller - this.detectTokensController = new DetectTokensController({ - preferences: this.preferencesController, - network: this.networkController, - }) - this.recentBlocksController = new RecentBlocksController({ blockTracker: this.blockTracker, provider: this.provider, @@ -151,6 +145,13 @@ module.exports = class MetamaskController extends EventEmitter { this.accountTracker.syncWithAddresses(addresses) }) + // detect tokens controller + this.detectTokensController = new DetectTokensController({ + preferences: this.preferencesController, + network: this.networkController, + keyringMemStore: this.keyringController.memStore, + }) + // address book controller this.addressBookController = new AddressBookController({ initState: initState.AddressBookController, @@ -1276,7 +1277,8 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * A method for activating the retrieval of price data, which should only be fetched when the UI is visible. + * A method for activating the retrieval of price data and auto detect tokens, + * which should only be fetched when the UI is visible. * @private * @param {boolean} active - True if price data should be getting fetched. */ -- cgit v1.2.3 From a3822b4680a295848f900616e3e154a1e1003a54 Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Fri, 20 Jul 2018 13:20:40 +0200 Subject: add notifications --- .../lib/transaction-notification-manager.js | 27 --------- .../controllers/transactions/tx-state-manager.js | 3 - app/scripts/metamask-controller.js | 7 +++ app/scripts/platforms/extension.js | 64 ++++++++++++++++++++++ 4 files changed, 71 insertions(+), 30 deletions(-) delete mode 100644 app/scripts/controllers/transactions/lib/transaction-notification-manager.js (limited to 'app') diff --git a/app/scripts/controllers/transactions/lib/transaction-notification-manager.js b/app/scripts/controllers/transactions/lib/transaction-notification-manager.js deleted file mode 100644 index 6bdf40b62..000000000 --- a/app/scripts/controllers/transactions/lib/transaction-notification-manager.js +++ /dev/null @@ -1,27 +0,0 @@ -const extension = require('extensionizer') - -// Confirmed tx -// Transaction ${tx.nonce} confirmed! View on Etherscan - -// Failed tx -// Transaction ${tx.nonce} failed. (Maybe append tx.error.message) - -// Dropped tx -// A Transaction ${tx.nonce} was dropped, because another transaction with that number was successfully processed. - -function showConfirmedNotification (txMeta) { - extension.notifications.create({ - "type": "basic", - "title": "Confirmed transaction", - "iconUrl": extension.extension.getURL('../../../../images/icon-64.png'), - "message": JSON.stringify(txMeta) - }); -} - - -/** -@module -*/ -module.exports = { - showConfirmedNotification -} \ No newline at end of file diff --git a/app/scripts/controllers/transactions/tx-state-manager.js b/app/scripts/controllers/transactions/tx-state-manager.js index 72da45913..28a18ca2e 100644 --- a/app/scripts/controllers/transactions/tx-state-manager.js +++ b/app/scripts/controllers/transactions/tx-state-manager.js @@ -5,7 +5,6 @@ const ethUtil = require('ethereumjs-util') const log = require('loglevel') const txStateHistoryHelper = require('./lib/tx-state-history-helper') const createId = require('../../lib/random-id') -const transactionNotificationManager = require('./lib/transaction-notification-manager') const { getFinalStates } = require('./lib/util') /** TransactionStateManager is responsible for the state of a transaction and @@ -333,8 +332,6 @@ class TransactionStateManager extends EventEmitter { */ setTxStatusConfirmed (txId) { this._setTxStatus(txId, 'confirmed') - const txMeta = this.getTx(txId) - transactionNotificationManager.showConfirmedNotification(txMeta) } /** diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 450113acf..31156b5b6 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -164,6 +164,13 @@ module.exports = class MetamaskController extends EventEmitter { }) this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts)) + this.txController.on(`tx:status-update`, (txId, status) => { + if (status === 'confirmed' || status === 'failed' || status === 'dropped') { + const txMeta = this.txController.txStateManager.getTx(txId) + this.platform.showTransactionNotification(txMeta) + } + }) + // computed balances (accounting for pending transactions) this.balancesController = new BalancesController({ accountTracker: this.accountTracker, diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index f5cc255d1..afcc9bbca 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -1,4 +1,5 @@ const extension = require('extensionizer') +const explorerLink = require('etherscan-link').createExplorerLink class ExtensionPlatform { @@ -31,6 +32,69 @@ class ExtensionPlatform { cb(e) } } + + showTransactionNotification (txMeta) { + + const status = txMeta.status + if (status === 'confirmed') { + this._showConfirmedTransaction(txMeta) + } else if (status === 'failed') { + this._showFailedTransaction(txMeta) + } else if (status === 'dropped') { + this._showDroppedTransaction(txMeta) + } + } + + _showConfirmedTransaction (txMeta) { + + this._subscribeToNotificationClicked() + + const url = explorerLink(txMeta.hash, parseInt(txMeta.metamaskNetworkId)) + const nonce = parseInt(txMeta.txParams.nonce, 16) + + const title = 'Confirmed transaction' + const message = `Transaction ${nonce} confirmed! View on EtherScan` + this._showNotification(title, message, url) + } + + _showFailedTransaction (txMeta) { + + const nonce = parseInt(txMeta.txParams.nonce, 16) + const title = 'Failed transaction' + const message = `Transaction ${nonce} failed! ${txMeta.err.message}` + this._showNotification(title, message) + } + + _showDroppedTransaction (txMeta) { + + const nonce = parseInt(txMeta.txParams.nonce, 16) + const title = 'Dropped transaction' + const message = `Transaction ${nonce} was dropped, because another transaction with that number was successfully processed.` + this._showNotification(title, message) + } + + _showNotification (title, message, url) { + extension.notifications.create( + url, + { + 'type': 'basic', + 'title': title, + 'iconUrl': extension.extension.getURL('../../images/icon-64.png'), + 'message': message, + }) + } + + _subscribeToNotificationClicked () { + if (!extension.notifications.onClicked.hasListener(this._viewOnEtherScan)) { + extension.notifications.onClicked.addListener(this._viewOnEtherScan) + } + } + + _viewOnEtherScan (txId) { + if (txId.startsWith('http://')) { + global.metamaskController.platform.openWindow({ url: txId }) + } + } } module.exports = ExtensionPlatform -- cgit v1.2.3 From cb045fd8feec88bd631329ab9b3285aeed0f2e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Mi=C3=B1o?= Date: Fri, 20 Jul 2018 12:36:24 -0400 Subject: Auto-detect tokens #3034 (#4683) * detect tokens polling * network store to detect token * tests for spec * passtest-lint * fix lint * improve tests * detect tokens through infura * detect tokens when submit password and new account selected * keyring unlocked detect and unit tests * add changelog --- app/scripts/controllers/detect-tokens.js | 123 +++++++++++++++++++++++++++++++ app/scripts/metamask-controller.js | 12 ++- 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 app/scripts/controllers/detect-tokens.js (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js new file mode 100644 index 000000000..f1810cfa1 --- /dev/null +++ b/app/scripts/controllers/detect-tokens.js @@ -0,0 +1,123 @@ +const Web3 = require('web3') +const contracts = require('eth-contract-metadata') +const { warn } = require('loglevel') +const { MAINNET } = require('./network/enums') +// By default, poll every 3 minutes +const DEFAULT_INTERVAL = 180 * 1000 +const ERC20_ABI = [{'constant': true, 'inputs': [{'name': '_owner', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': 'balance', 'type': 'uint256'}], 'payable': false, 'type': 'function'}] + +/** + * A controller that polls for token exchange + * rates based on a user's current token list + */ +class DetectTokensController { + /** + * Creates a DetectTokensController + * + * @param {Object} [config] - Options to configure controller + */ + constructor ({ interval = DEFAULT_INTERVAL, preferences, network, keyringMemStore } = {}) { + this.preferences = preferences + this.interval = interval + this.network = network + this.keyringMemStore = keyringMemStore + } + + /** + * For each token in eth-contract-metada, find check selectedAddress balance. + * + */ + async detectNewTokens () { + if (!this.isActive) { return } + if (this._network.store.getState().provider.type !== MAINNET) { return } + this.web3.setProvider(this._network._provider) + for (const contractAddress in contracts) { + if (contracts[contractAddress].erc20 && !(this.tokenAddresses.includes(contractAddress.toLowerCase()))) { + this.detectTokenBalance(contractAddress) + } + } + } + + /** + * Find if selectedAddress has tokens with contract in contractAddress. + * + * @param {string} contractAddress Hex address of the token contract to explore. + * @returns {boolean} If balance is detected, token is added. + * + */ + async detectTokenBalance (contractAddress) { + const ethContract = this.web3.eth.contract(ERC20_ABI).at(contractAddress) + ethContract.balanceOf(this.selectedAddress, (error, result) => { + if (!error) { + if (!result.isZero()) { + this._preferences.addToken(contractAddress, contracts[contractAddress].symbol, contracts[contractAddress].decimals) + } + } else { + warn(`MetaMask - DetectTokensController balance fetch failed for ${contractAddress}.`, error) + } + }) + } + + /** + * Restart token detection polling period and call detectNewTokens + * in case of address change or user session initialization. + * + */ + restartTokenDetection () { + if (this.isActive && this.selectedAddress) { + this.detectNewTokens() + this.interval = DEFAULT_INTERVAL + } + } + + /** + * @type {Number} + */ + set interval (interval) { + this._handle && clearInterval(this._handle) + if (!interval) { return } + this._handle = setInterval(() => { this.detectNewTokens() }, interval) + } + + /** + * In setter when selectedAddress is changed, detectNewTokens and restart polling + * @type {Object} + */ + set preferences (preferences) { + if (!preferences) { return } + this._preferences = preferences + preferences.store.subscribe(({ tokens }) => { this.tokenAddresses = tokens.map((obj) => { return obj.address }) }) + preferences.store.subscribe(({ selectedAddress }) => { + if (this.selectedAddress !== selectedAddress) { + this.selectedAddress = selectedAddress + this.restartTokenDetection() + } + }) + } + + /** + * @type {Object} + */ + set network (network) { + if (!network) { return } + this._network = network + this.web3 = new Web3(network._provider) + } + + /** + * In setter when isUnlocked is updated to true, detectNewTokens and restart polling + * @type {Object} + */ + set keyringMemStore (keyringMemStore) { + if (!keyringMemStore) { return } + this._keyringMemStore = keyringMemStore + this._keyringMemStore.subscribe(({ isUnlocked }) => { + if (this.isUnlocked !== isUnlocked) { + if (isUnlocked) { this.restartTokenDetection() } + this.isUnlocked = isUnlocked + } + }) + } +} + +module.exports = DetectTokensController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index dc5c24b1b..6f5908414 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -35,6 +35,7 @@ const TypedMessageManager = require('./lib/typed-message-manager') const TransactionController = require('./controllers/transactions') const BalancesController = require('./controllers/computed-balances') const TokenRatesController = require('./controllers/token-rates') +const DetectTokensController = require('./controllers/detect-tokens') const ConfigManager = require('./lib/config-manager') const nodeify = require('./lib/nodeify') const accountImporter = require('./account-import-strategies') @@ -147,6 +148,13 @@ module.exports = class MetamaskController extends EventEmitter { this.accountTracker.syncWithAddresses(addresses) }) + // detect tokens controller + this.detectTokensController = new DetectTokensController({ + preferences: this.preferencesController, + network: this.networkController, + keyringMemStore: this.keyringController.memStore, + }) + // address book controller this.addressBookController = new AddressBookController({ initState: initState.AddressBookController, @@ -1420,11 +1428,13 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * A method for activating the retrieval of price data, which should only be fetched when the UI is visible. + * A method for activating the retrieval of price data and auto detect tokens, + * which should only be fetched when the UI is visible. * @private * @param {boolean} active - True if price data should be getting fetched. */ set isClientOpenAndUnlocked (active) { this.tokenRatesController.isActive = active + this.detectTokensController.isActive = active } } -- cgit v1.2.3 From 72591d4f41aafa7556cbc37d613fcbe016444156 Mon Sep 17 00:00:00 2001 From: Csaba Solya Date: Fri, 20 Jul 2018 19:58:26 +0200 Subject: remove dropped handler --- app/scripts/metamask-controller.js | 2 +- app/scripts/platforms/extension.js | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 31156b5b6..939a30d74 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -165,7 +165,7 @@ module.exports = class MetamaskController extends EventEmitter { this.txController.on('newUnapprovedTx', opts.showUnapprovedTx.bind(opts)) this.txController.on(`tx:status-update`, (txId, status) => { - if (status === 'confirmed' || status === 'failed' || status === 'dropped') { + if (status === 'confirmed' || status === 'failed') { const txMeta = this.txController.txStateManager.getTx(txId) this.platform.showTransactionNotification(txMeta) } diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index afcc9bbca..60ecfeff4 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -40,8 +40,6 @@ class ExtensionPlatform { this._showConfirmedTransaction(txMeta) } else if (status === 'failed') { this._showFailedTransaction(txMeta) - } else if (status === 'dropped') { - this._showDroppedTransaction(txMeta) } } @@ -65,14 +63,6 @@ class ExtensionPlatform { this._showNotification(title, message) } - _showDroppedTransaction (txMeta) { - - const nonce = parseInt(txMeta.txParams.nonce, 16) - const title = 'Dropped transaction' - const message = `Transaction ${nonce} was dropped, because another transaction with that number was successfully processed.` - this._showNotification(title, message) - } - _showNotification (title, message, url) { extension.notifications.create( url, -- cgit v1.2.3 From 9c955549338f49d8b5eb6ca003c2c65c725aa328 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Fri, 20 Jul 2018 19:58:03 -0400 Subject: fix detection on submit password --- app/scripts/controllers/detect-tokens.js | 13 ++++++++----- app/scripts/metamask-controller.js | 5 ++--- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index f1810cfa1..4fe4b4c61 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -64,10 +64,9 @@ class DetectTokensController { * */ restartTokenDetection () { - if (this.isActive && this.selectedAddress) { - this.detectNewTokens() - this.interval = DEFAULT_INTERVAL - } + if (!(this.isActive && this.selectedAddress)) { return } + this.detectNewTokens() + this.interval = DEFAULT_INTERVAL } /** @@ -113,11 +112,15 @@ class DetectTokensController { this._keyringMemStore = keyringMemStore this._keyringMemStore.subscribe(({ isUnlocked }) => { if (this.isUnlocked !== isUnlocked) { - if (isUnlocked) { this.restartTokenDetection() } this.isUnlocked = isUnlocked + if (isUnlocked) { this.restartTokenDetection() } } }) } + + get isActive () { + return this.isOpen && this.isUnlocked + } } module.exports = DetectTokensController diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 4e97ce583..d3650815e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1274,16 +1274,15 @@ module.exports = class MetamaskController extends EventEmitter { set isClientOpen (open) { this._isClientOpen = open this.isClientOpenAndUnlocked = this.getState().isUnlocked && open + this.detectTokensController.isOpen = open } /** - * A method for activating the retrieval of price data and auto detect tokens, - * which should only be fetched when the UI is visible. + * A method for activating the retrieval of price data, which should only be fetched when the UI is visible. * @private * @param {boolean} active - True if price data should be getting fetched. */ set isClientOpenAndUnlocked (active) { this.tokenRatesController.isActive = active - this.detectTokensController.isActive = active } } -- cgit v1.2.3 From 9be22775c31699b49873a43820dc315067a567a9 Mon Sep 17 00:00:00 2001 From: Esteban MIno Date: Fri, 20 Jul 2018 20:15:33 -0400 Subject: fix merge --- app/scripts/metamask-controller.js | 1 - 1 file changed, 1 deletion(-) (limited to 'app') diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 18448961d..3b303a95c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1443,6 +1443,5 @@ module.exports = class MetamaskController extends EventEmitter { */ set isClientOpenAndUnlocked (active) { this.tokenRatesController.isActive = active - this.detectTokensController.isActive = active } } -- cgit v1.2.3 From 5ebefc0e502696098599a4a17f185a88aaeed628 Mon Sep 17 00:00:00 2001 From: brunobar79 Date: Sat, 21 Jul 2018 16:03:31 -0400 Subject: run linter --- app/scripts/controllers/detect-tokens.js | 2 +- app/scripts/metamask-controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/controllers/detect-tokens.js b/app/scripts/controllers/detect-tokens.js index b30dc00f1..195ec918a 100644 --- a/app/scripts/controllers/detect-tokens.js +++ b/app/scripts/controllers/detect-tokens.js @@ -117,7 +117,7 @@ class DetectTokensController { } }) } - + /** * Internal isActive state * @type {Object} diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3b303a95c..bcc7075c2 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1436,7 +1436,7 @@ module.exports = class MetamaskController extends EventEmitter { } /** - * A method for activating the retrieval of price data and auto detect tokens, + * A method for activating the retrieval of price data and auto detect tokens, * which should only be fetched when the UI is visible. * @private * @param {boolean} active - True if price data should be getting fetched. -- cgit v1.2.3 From 2359062b62cf65f38b36ccb6bb33fa7d15ada1ae Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 23 Jul 2018 22:20:06 -0230 Subject: UI confirm screen closes confirmation window on submit or cancel of a tx --- app/scripts/platforms/extension.js | 6 ++++++ app/scripts/ui.js | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'app') diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index 901c26cab..bd4f7bd9f 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -14,6 +14,12 @@ class ExtensionPlatform { extension.tabs.create({ url }) } + closeCurrentWindow (cb) { + return extension.windows.getCurrent((windowDetails) => { + return extension.windows.remove(windowDetails.id) + }) + } + getVersion () { return extension.runtime.getManifest().version } diff --git a/app/scripts/ui.js b/app/scripts/ui.js index 9bf97be87..42703f029 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -64,16 +64,16 @@ async function start () { css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss() deleteInjectedCss = injectCss(css) } - if (state.appState.shouldClose) notificationManager.closePopup() + // if (state.appState.shouldClose) notificationManager.closePopup() }) }) function closePopupIfOpen (windowType) { - if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) { + // if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) { // should close only chrome popup - notificationManager.closePopup() - } + // notificationManager.closePopup() + // } } function displayCriticalError (err) { -- cgit v1.2.3 From 64a82fd3dae66f33b0934d58e43e85df27fbfe1d Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Tue, 24 Jul 2018 11:45:36 -0230 Subject: Uncomment closePopupIfOpen code. --- app/scripts/ui.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'app') diff --git a/app/scripts/ui.js b/app/scripts/ui.js index 42703f029..cbe15c92d 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -70,10 +70,10 @@ async function start () { function closePopupIfOpen (windowType) { - // if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) { + if (windowType !== ENVIRONMENT_TYPE_NOTIFICATION) { // should close only chrome popup - // notificationManager.closePopup() - // } + notificationManager.closePopup() + } } function displayCriticalError (err) { -- cgit v1.2.3 From a61227f224e37559c8d6e2c59441b0032633feaf Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Tue, 24 Jul 2018 15:01:03 -0230 Subject: Remove unneeded code from i4829-close-notifications-from-ui branch. --- app/scripts/platforms/extension.js | 2 +- app/scripts/ui.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'app') diff --git a/app/scripts/platforms/extension.js b/app/scripts/platforms/extension.js index bd4f7bd9f..0803164e8 100644 --- a/app/scripts/platforms/extension.js +++ b/app/scripts/platforms/extension.js @@ -14,7 +14,7 @@ class ExtensionPlatform { extension.tabs.create({ url }) } - closeCurrentWindow (cb) { + closeCurrentWindow () { return extension.windows.getCurrent((windowDetails) => { return extension.windows.remove(windowDetails.id) }) diff --git a/app/scripts/ui.js b/app/scripts/ui.js index cbe15c92d..da100f928 100644 --- a/app/scripts/ui.js +++ b/app/scripts/ui.js @@ -64,7 +64,6 @@ async function start () { css = betaUIState ? NewMetaMaskUiCss() : OldMetaMaskUiCss() deleteInjectedCss = injectCss(css) } - // if (state.appState.shouldClose) notificationManager.closePopup() }) }) -- cgit v1.2.3 From 3667f3cb8556d55ad893ec8c1a0f84447273906f Mon Sep 17 00:00:00 2001 From: pinkiebell <40266861+pinkiebell@users.noreply.github.com> Date: Wed, 25 Jul 2018 00:03:36 +0200 Subject: network.js: convert rpc protocol to lower case (#4855) Fixes #4253 --- app/scripts/controllers/network/network.js | 3 +++ 1 file changed, 3 insertions(+) (limited to 'app') diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js index a50f6dc45..b6f7705b5 100644 --- a/app/scripts/controllers/network/network.js +++ b/app/scripts/controllers/network/network.js @@ -9,6 +9,7 @@ const extend = require('xtend') const EthQuery = require('eth-query') const createEventEmitterProxy = require('../../lib/events-proxy.js') const log = require('loglevel') +const urlUtil = require('url') const { ROPSTEN, RINKEBY, @@ -155,6 +156,8 @@ module.exports = class NetworkController extends EventEmitter { } _configureStandardProvider ({ rpcUrl }) { + // urlUtil handles malformed urls + rpcUrl = urlUtil.parse(rpcUrl).format() const providerParams = extend(this._baseProviderParams, { rpcUrl, engineParams: { -- cgit v1.2.3 From 527b62ee8e57335360402c2e121bf34d3e621a8c Mon Sep 17 00:00:00 2001 From: frankiebee Date: Wed, 25 Jul 2018 14:11:07 -0700 Subject: migrations - fix to include 27 --- app/scripts/migrations/index.js | 1 + 1 file changed, 1 insertion(+) (limited to 'app') diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index 04d90bfff..bd0005221 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -37,4 +37,5 @@ module.exports = [ require('./024'), require('./025'), require('./026'), + require('./027'), ] -- cgit v1.2.3