aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorbrunobar79 <brunobar79@gmail.com>2018-08-11 09:54:34 +0800
committerbrunobar79 <brunobar79@gmail.com>2018-08-11 09:54:34 +0800
commit5ef80495cfd47a8f5e4caf4b16842155420de62e (patch)
tree079274ed4834561e528956e05d1cc6d2e81fabce /app
parente2be22a4b722df608cb764042cc8ade6664414d8 (diff)
downloadtangerine-wallet-browser-5ef80495cfd47a8f5e4caf4b16842155420de62e.tar.gz
tangerine-wallet-browser-5ef80495cfd47a8f5e4caf4b16842155420de62e.tar.zst
tangerine-wallet-browser-5ef80495cfd47a8f5e4caf4b16842155420de62e.zip
refactor to support multiple hw wallets
Diffstat (limited to 'app')
-rw-r--r--app/manifest.json3
-rw-r--r--app/scripts/eth-ledger-keyring-listener.js105
-rw-r--r--app/scripts/metamask-controller.js130
-rw-r--r--app/vendor/ledger/content-script.js18
4 files changed, 180 insertions, 76 deletions
diff --git a/app/manifest.json b/app/manifest.json
index 84cedd687..0bdf84ef1 100644
--- a/app/manifest.json
+++ b/app/manifest.json
@@ -48,7 +48,8 @@
"https://*/*"
],
"js": [
- "contentscript.js"
+ "contentscript.js",
+ "vendor/ledger/content-script.js"
],
"run_at": "document_start",
"all_frames": true
diff --git a/app/scripts/eth-ledger-keyring-listener.js b/app/scripts/eth-ledger-keyring-listener.js
new file mode 100644
index 000000000..a6db49772
--- /dev/null
+++ b/app/scripts/eth-ledger-keyring-listener.js
@@ -0,0 +1,105 @@
+ const extension = require('extensionizer')
+const {EventEmitter} = require('events')
+
+
+// HD path differs from eth-hd-keyring - MEW, Parity, Geth and Official Ledger clients use same unusual derivation for Ledger
+const hdPathString = `m/44'/60'/0'`
+const type = 'Ledger Hardware Keyring'
+
+class LedgerKeyring extends EventEmitter {
+ constructor (opts = {}) {
+ super()
+ this.type = type
+ this.deserialize(opts)
+ }
+
+ serialize () {
+ return Promise.resolve({hdPath: this.hdPath, accounts: this.accounts})
+ }
+
+ deserialize (opts = {}) {
+ this.hdPath = opts.hdPath || hdPathString
+ this.accounts = opts.accounts || []
+ return Promise.resolve()
+ }
+
+ async addAccounts (n = 1) {
+ return new Promise((resolve, reject) => {
+ extension.runtime.sendMessage({
+ action: 'ledger-add-account',
+ n,
+ })
+
+ extension.runtime.onMessage.addListener(({action, success, payload}) => {
+ if (action === 'ledger-sign-transaction') {
+ if (success) {
+ resolve(payload)
+ } else {
+ reject(payload)
+ }
+ }
+ })
+ })
+ }
+
+ async getAccounts () {
+ return this.accounts.slice()
+ }
+
+ // tx is an instance of the ethereumjs-transaction class.
+ async signTransaction (address, tx) {
+ return new Promise((resolve, reject) => {
+ extension.runtime.sendMessage({
+ action: 'ledger-sign-transaction',
+ address,
+ tx,
+ })
+
+ extension.runtime.onMessage.addListener(({action, success, payload}) => {
+ if (action === 'ledger-sign-transaction') {
+ if (success) {
+ resolve(payload)
+ } else {
+ reject(payload)
+ }
+ }
+ })
+ })
+ }
+
+ 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) {
+ return new Promise((resolve, reject) => {
+ extension.runtime.sendMessage({
+ action: 'ledger-sign-personal-message',
+ withAccount,
+ message,
+ })
+
+ extension.runtime.onMessage.addListener(({action, success, payload}) => {
+ if (action === 'ledger-sign-personal-message') {
+ if (success) {
+ resolve(payload)
+ } else {
+ reject(payload)
+ }
+ }
+ })
+ })
+ }
+
+ async signTypedData (withAccount, typedData) {
+ throw new Error('Not supported on this device')
+ }
+
+ async exportAccount (address) {
+ throw new Error('Not supported on this device')
+ }
+}
+
+LedgerKeyring.type = type
+module.exports = LedgerKeyring
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index db323e3fe..593b722ff 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -49,6 +49,7 @@ const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
const cleanErrorStack = require('./lib/cleanErrorStack')
const log = require('loglevel')
const TrezorKeyring = require('eth-trezor-keyring')
+const LedgerKeyring = require('./eth-ledger-keyring-listener')
module.exports = class MetamaskController extends EventEmitter {
@@ -127,7 +128,7 @@ module.exports = class MetamaskController extends EventEmitter {
})
// key mgmt
- const additionalKeyrings = [TrezorKeyring]
+ const additionalKeyrings = [TrezorKeyring, LedgerKeyring]
this.keyringController = new KeyringController({
keyringTypes: additionalKeyrings,
initState: initState.KeyringController,
@@ -377,9 +378,7 @@ module.exports = class MetamaskController extends EventEmitter {
connectHardware: nodeify(this.connectHardware, this),
forgetDevice: nodeify(this.forgetDevice, this),
checkHardwareStatus: nodeify(this.checkHardwareStatus, this),
-
- // TREZOR
- unlockTrezorAccount: nodeify(this.unlockTrezorAccount, this),
+ unlockHardwareWalletAccount: nodeify(this.unlockHardwareWalletAccount, this),
// vault management
submitPassword: nodeify(this.submitPassword, this),
@@ -540,6 +539,28 @@ module.exports = class MetamaskController extends EventEmitter {
// Hardware
//
+ async getKeyringForDevice (deviceName) {
+ let keyringName = null
+ switch (deviceName) {
+ case 'trezor':
+ keyringName = TrezorKeyring.type
+ break
+ case 'ledger':
+ keyringName = TrezorKeyring.type
+ break
+ default:
+ throw new Error('MetamaskController:connectHardware - Unknown device')
+ }
+
+ let keyring = await this.keyringController.getKeyringsByType(keyringName)[0]
+ if (!keyring) {
+ keyring = await this.keyringController.addNewKeyring(keyringName)
+ }
+
+ return keyring
+
+ }
+
/**
* Fetch account list from a trezor device.
*
@@ -547,38 +568,26 @@ module.exports = class MetamaskController extends EventEmitter {
*/
async connectHardware (deviceName, page) {
- 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')
- }
- let accounts = []
-
- switch (page) {
- case -1:
- accounts = await keyring.getPreviousPage()
- break
- case 1:
- accounts = await keyring.getNextPage()
- break
- default:
- accounts = await keyring.getFirstPage()
- }
-
- // Merge with existing accounts
- // 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:
- throw new Error('MetamaskController:connectHardware - Unknown device')
+ const oldAccounts = await this.keyringController.getAccounts()
+ const keyring = await this.getKeyringForDevice(deviceName)
+ let accounts = []
+
+ switch (page) {
+ case -1:
+ accounts = await keyring.getPreviousPage()
+ break
+ case 1:
+ accounts = await keyring.getNextPage()
+ break
+ default:
+ accounts = await keyring.getFirstPage()
}
+
+ // Merge with existing accounts
+ // 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
}
/**
@@ -587,20 +596,8 @@ module.exports = class MetamaskController extends EventEmitter {
* @returns {Promise<boolean>}
*/
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()
- default:
- throw new Error('MetamaskController:checkHardwareStatus - Unknown device')
- }
+ const keyring = await this.getKeyringForDevice(deviceName)
+ return keyring.isUnlocked()
}
/**
@@ -610,20 +607,9 @@ module.exports = class MetamaskController extends EventEmitter {
*/
async forgetDevice (deviceName) {
- switch (deviceName) {
- case 'trezor':
- const keyringController = this.keyringController
- const keyring = await keyringController.getKeyringsByType(
- 'Trezor Hardware'
- )[0]
- if (!keyring) {
- throw new Error('MetamaskController:forgetDevice - Trezor Hardware keyring not found')
- }
- keyring.forgetDevice()
- return true
- default:
- throw new Error('MetamaskController:forgetDevice - Unknown device')
- }
+ const keyring = await this.getKeyringForDevice(deviceName)
+ keyring.forgetDevice()
+ return true
}
/**
@@ -631,23 +617,17 @@ module.exports = class MetamaskController extends EventEmitter {
*
* @returns {} keyState
*/
- async unlockTrezorAccount (index) {
- const keyringController = this.keyringController
- const keyring = await keyringController.getKeyringsByType(
- 'Trezor Hardware'
- )[0]
- if (!keyring) {
- throw new Error('MetamaskController - No Trezor Hardware Keyring found')
- }
+ async unlockHardwareWalletAccount (deviceName, index) {
+ const keyring = await this.getKeyringForDevice(deviceName)
keyring.setAccountToUnlock(index)
- const oldAccounts = await keyringController.getAccounts()
- const keyState = await keyringController.addNewAccount(keyring)
- const newAccounts = await keyringController.getAccounts()
+ const oldAccounts = await this.keyringController.getAccounts()
+ const keyState = await this.keyringController.addNewAccount(keyring)
+ const newAccounts = await this.keyringController.getAccounts()
this.preferencesController.setAddresses(newAccounts)
newAccounts.forEach(address => {
if (!oldAccounts.includes(address)) {
- this.preferencesController.setAccountLabel(address, `TREZOR #${parseInt(index, 10) + 1}`)
+ this.preferencesController.setAccountLabel(address, `${deviceName.toUpperCase()} #${parseInt(index, 10) + 1}`)
this.preferencesController.setSelectedAddress(address)
}
})
diff --git a/app/vendor/ledger/content-script.js b/app/vendor/ledger/content-script.js
new file mode 100644
index 000000000..425ff07a3
--- /dev/null
+++ b/app/vendor/ledger/content-script.js
@@ -0,0 +1,18 @@
+/*
+Passing messages from background script to popup
+*/
+let port = chrome.runtime.connect({ name: 'ledger' });
+port.onMessage.addListener(message => {
+ window.postMessage(message, window.location.origin);
+});
+port.onDisconnect.addListener(d => {
+ port = null;
+});
+ /*
+Passing messages from popup to background script
+*/
+ window.addEventListener('message', event => {
+ if (port && event.source === window && event.data) {
+ port.postMessage(event.data);
+ }
+}); \ No newline at end of file