Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ledger hardware wallet support #5050

Merged
merged 32 commits into from
Aug 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
5ef8049
refactor to support multiple hw wallets
brunobar79 Aug 11, 2018
78a1cd3
iframe communication working
brunobar79 Aug 11, 2018
8e842a8
able to add accounts
brunobar79 Aug 11, 2018
aa6a42e
rename keyring
brunobar79 Aug 11, 2018
011cc14
tx signing should work
brunobar79 Aug 11, 2018
12b41b8
tx signature is valid
brunobar79 Aug 11, 2018
068bf43
working
brunobar79 Aug 11, 2018
e6d64ce
message signing works
brunobar79 Aug 12, 2018
2355573
clean up
brunobar79 Aug 12, 2018
0b9b892
this should be ready to go
brunobar79 Aug 12, 2018
8f9a0a5
clean up
brunobar79 Aug 12, 2018
77ad856
remove ledger lib
brunobar79 Aug 12, 2018
4e1d8ba
good progress adding paths
brunobar79 Aug 13, 2018
61a2792
legacy and new hd path working
brunobar79 Aug 14, 2018
b77cc3d
fix tx tests
brunobar79 Aug 14, 2018
c72ced7
ui fixes
brunobar79 Aug 14, 2018
19d1988
fix
brunobar79 Aug 14, 2018
53dcad5
more ui
brunobar79 Aug 14, 2018
fdf202e
fixed unit tests
brunobar79 Aug 15, 2018
82a5ed1
remove console logs
brunobar79 Aug 15, 2018
80fe3ce
Merge branch 'develop' into ledger-support
Aug 15, 2018
e54b850
use eth-ledger-bridge-keyring from npm
brunobar79 Aug 15, 2018
8fb7099
Merge branch 'ledger-support' of github.com:MetaMask/metamask-extensi…
brunobar79 Aug 15, 2018
d2d8d38
update package-lock.json
brunobar79 Aug 15, 2018
837be70
change Metamask for MetaMask
brunobar79 Aug 15, 2018
2ea05e3
connect screen ready
brunobar79 Aug 16, 2018
2858146
ui ready
brunobar79 Aug 17, 2018
b369560
fix e2e tests
brunobar79 Aug 17, 2018
51e4a6d
fix ledger affiliate link
brunobar79 Aug 17, 2018
992e7f1
fix merge conflicts
brunobar79 Aug 17, 2018
0391ec3
update package-lock.json
brunobar79 Aug 17, 2018
bd904c8
Merge branch 'develop' into ledger-support
Aug 17, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 22 additions & 16 deletions app/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@
"close": {
"message": "Close"
},
"chromeRequiredForTrezor":{
"message": "You need to use MetaMask on Google Chrome in order to connect to your TREZOR device."
"chromeRequiredForHardwareWallets":{
"message": "You need to use MetaMask on Google Chrome in order to connect to your Hardware Wallet."
},
"confirm": {
"message": "Confirm"
Expand All @@ -146,15 +146,12 @@
"connecting": {
"message": "Connecting..."
},
"connectToLedger": {
"message": "Connect to Ledger"
},
"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, please make sure you are using the latest version of the TREZOR firmware."
},
"continue": {
"message": "Continue"
},
Expand Down Expand Up @@ -289,8 +286,8 @@
"downloadStateLogs": {
"message": "Download State Logs"
},
"dontHaveATrezorWallet": {
"message": "Don't have a TREZOR hardware wallet?"
"dontHaveAHardwareWallet": {
"message": "Dont have a hardware wallet?"
},
"dropped": {
"message": "Dropped"
Expand Down Expand Up @@ -426,11 +423,11 @@
"hardwareWalletConnected": {
"message": "Hardware wallet connected"
},
"hardwareSupport": {
"message": "Hardware Support"
"hardwareWallets": {
"message": "Connect a hardware wallet"
},
"hardwareSupportMsg": {
"message": "You can now view your Hardware accounts in MetaMask! Scroll down and read how it works."
"hardwareWalletsMsg": {
"message": "Select a hardware wallet you'd like to use with MetaMask"
},
"havingTroubleConnecting": {
"message": "Having trouble connecting?"
Expand Down Expand Up @@ -538,6 +535,9 @@
"learnMore": {
"message": "Learn more"
},
"ledgerAccountRestriction": {
"message": "You need to make use your last account before you can add a new one."
},
"lessThanMax": {
"message": "must be less than or equal to $1.",
"description": "helper for inputting hex as decimal input"
Expand Down Expand Up @@ -908,7 +908,7 @@
"description": "displays token symbol"
},
"orderOneHere": {
"message": "Order one here."
"message": "Order a Trezor or Ledger and keep your funds in cold storage"
},
"searchTokens": {
"message": "Search Tokens"
Expand All @@ -920,7 +920,13 @@
"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."
"message": "Select the account to view in MetaMask"
},
"selectHdPath": {
"message": "Select HD Path"
},
"selectPathHelp": {
"message": "If you don't see your existing Ledger accounts below, try switching paths to \"Legacy (MEW / MyCrypto)\""
},
"sendTokensAnywhere": {
"message": "Send Tokens to anyone with an Ethereum account"
Expand Down
1 change: 1 addition & 0 deletions app/images/ledger-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/images/trezor-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
138 changes: 60 additions & 78 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const percentile = require('percentile')
const seedPhraseVerifier = require('./lib/seed-phrase-verifier')
const log = require('loglevel')
const TrezorKeyring = require('eth-trezor-keyring')
const LedgerBridgeKeyring = require('eth-ledger-bridge-keyring')
const EthQuery = require('eth-query')

module.exports = class MetamaskController extends EventEmitter {
Expand Down Expand Up @@ -128,7 +129,7 @@ module.exports = class MetamaskController extends EventEmitter {
})

// key mgmt
const additionalKeyrings = [TrezorKeyring]
const additionalKeyrings = [TrezorKeyring, LedgerBridgeKeyring]
this.keyringController = new KeyringController({
keyringTypes: additionalKeyrings,
initState: initState.KeyringController,
Expand Down Expand Up @@ -372,9 +373,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),
Expand Down Expand Up @@ -579,67 +578,67 @@ module.exports = class MetamaskController extends EventEmitter {
// Hardware
//

async getKeyringForDevice (deviceName, hdPath = null) {
let keyringName = null
switch (deviceName) {
case 'trezor':
keyringName = TrezorKeyring.type
break
case 'ledger':
keyringName = LedgerBridgeKeyring.type
break
default:
throw new Error('MetamaskController:getKeyringForDevice - Unknown device')
}
let keyring = await this.keyringController.getKeyringsByType(keyringName)[0]
if (!keyring) {
keyring = await this.keyringController.addNewKeyring(keyringName)
}
if (hdPath && keyring.setHdPath) {
keyring.setHdPath(hdPath)
}

keyring.network = this.networkController.getProviderConfig().type

return keyring

}

/**
* Fetch account list from a trezor device.
*
* @returns [] accounts
*/
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')
async connectHardware (deviceName, page, hdPath) {
const keyring = await this.getKeyringForDevice(deviceName, hdPath)
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 oldAccounts = await this.keyringController.getAccounts()
const accountsToTrack = [...new Set(oldAccounts.concat(accounts.map(a => a.address.toLowerCase())))]
this.accountTracker.syncWithAddresses(accountsToTrack)
return accounts
}

/**
* Check if the device is unlocked
*
* @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')
}
async checkHardwareStatus (deviceName, hdPath) {
const keyring = await this.getKeyringForDevice(deviceName, hdPath)
return keyring.isUnlocked()
}

/**
Expand All @@ -649,44 +648,27 @@ 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
}

/**
* Imports an account from a trezor device.
*
* @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 (index, deviceName, hdPath) {
const keyring = await this.getKeyringForDevice(deviceName, hdPath)

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)
}
})
Expand Down
Loading