@@ -3,6 +3,7 @@ import React from 'react'
3import Maybe from 'folktale/maybe'
3import Maybe from 'folktale/maybe'
4import { Button } from '../components/Base'
4import { Button } from '../components/Base'
5import { Row, Col, H1, P, H2 } from '../components/Base'
5import { Row, Col, H1, P, H2 } from '../components/Base'
6+import { Form, Input, InnerLabel } from '../components/Base'
6import Transport from '@ledgerhq/hw-transport-u2f'
7import Transport from '@ledgerhq/hw-transport-u2f'
7import Eth from '@ledgerhq/hw-app-eth'
8import Eth from '@ledgerhq/hw-app-eth'
8import * as secp256k1 from 'secp256k1'
9import * as secp256k1 from 'secp256k1'
@@ -10,43 +11,56 @@ import * as secp256k1 from 'secp256k1'
10import { LEDGER_BASE_PATH } from '../lib/ledger'
11import { LEDGER_BASE_PATH } from '../lib/ledger'
11import { ROUTE_NAMES } from '../lib/router'
12import { ROUTE_NAMES } from '../lib/router'
14+const chopHdPrefix = str =>
15+ str.slice(0, 2) === "m/"
19+const addHdPrefix = str =>
20+ str.slice(0, 2) === "m/"
13class Ledger extends React.Component {
24class Ledger extends React.Component {
15− componentDidMount() {
28+ this.state = {
16− this.pollDevice()
29+ hdpath: LEDGER_BASE_PATH
18− this.pollerId = setInterval(
19− () => this.pollDevice(),
32+ this.pollDevice = this.pollDevice.bind(this)
20− 500
33+ this.handleHdPathInput = this.handleHdPathInput.bind(this)
24− componentWillUnmount() {
36+ handleHdPathInput(hdpath) {
25− clearInterval(this.pollerId)
37+ this.setState({ hdpath })
28 async pollDevice() {
40 async pollDevice() {
29− const { wallet, setWallet } = this.props
41+ const { setWallet, setWalletHdPath } = this.props
30−
42+ const { hdpath } = this.state
31− if (Maybe.Nothing.hasInstance(wallet)) {
43+
32− const transport = await Transport.create()
44+ const transport = await Transport.create()
33− const eth = new Eth(transport)
45+ const eth = new Eth(transport)
34−
46+ const path = chopHdPrefix(hdpath)
35− eth.getAddress(LEDGER_BASE_PATH, false, true)
48+ eth.getAddress(path, false, true)
36− .then(info => {
49+ .then(info => {
37− const publicKey = Buffer.from(info.publicKey, 'hex')
50+ const publicKey = Buffer.from(info.publicKey, 'hex')
38− const chainCode = Buffer.from(info.chainCode, 'hex')
51+ const chainCode = Buffer.from(info.chainCode, 'hex')
39− const pub = secp256k1.publicKeyConvert(publicKey, true)
52+ const pub = secp256k1.publicKeyConvert(publicKey, true)
40− const hd = bip32.fromPublicKey(pub, chainCode)
53+ const hd = bip32.fromPublicKey(pub, chainCode)
41− setWallet(Maybe.Just(hd))
54+ setWallet(Maybe.Just(hd))
55+ setWalletHdPath(addHdPrefix(hdpath))
42− }, _ => {
56+ }, _ => {
43− setWallet(Maybe.Nothing())
57+ setWallet(Maybe.Nothing())
48 render() {
61 render() {
49 const { pushRoute, popRoute, wallet } = this.props
62 const { pushRoute, popRoute, wallet } = this.props
63+ const { hdpath } = this.state
@@ -58,10 +72,17 @@ class Ledger extends React.Component {
60 { `Connect and authenticate to your Ledger, and then open the
74 { `Connect and authenticate to your Ledger, and then open the
61− "Ethereum" application. If you're running on older firmware, make sure the browser
75+ "Ethereum" application. If you're running on older firmware,
62− support" option is turned on.` }
76+ make sure the "browser support" option is turned on. To sign
77+ transactions, you'll also need to enable the "contract data"
82+ { `If you'd like to use a custom derivation path, you may enter
66 <H2>{'Running on HTTP?'}</H2>
87 <H2>{'Running on HTTP?'}</H2>
@@ -78,19 +99,36 @@ class Ledger extends React.Component {
78 <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
99 <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
106+ prop-format='innerLabel'
108+ value={ addHdPrefix(hdpath) }
110+ onChange={ this.handleHdPathInput }>
111+ <InnerLabel>{'HD Path'}</InnerLabel>
116+ onClick={ this.pollDevice }>
117+ { 'Authenticate →' }
83− className={'mt-8'}
121+ className={'mt-8'}
84− disabled={ Maybe.Nothing.hasInstance(wallet) }
122+ disabled={ Maybe.Nothing.hasInstance(wallet) }
85− onClick={
123+ onClick={
87− popRoute()
125+ popRoute()
88− pushRoute(ROUTE_NAMES.SHIPS)
126+ pushRoute(ROUTE_NAMES.SHIPS)
92− { 'Continue →' }
129+ { 'Continue →' }
93− </Button>
130+ </Button>