@@ -3,6 +3,7 @@ import React from 'react'
33import Maybe from 'folktale/maybe'
44import { Button } from '../components/Base'
55import { Row, Col, H1, P, H2 } from '../components/Base'
6+import { Form, Input, InnerLabel } from '../components/Base'
67import Transport from '@ledgerhq/hw-transport-u2f'
78import Eth from '@ledgerhq/hw-app-eth'
89import * as secp256k1 from 'secp256k1'
@@ -10,43 +11,56 @@ import * as secp256k1 from 'secp256k1'
1011import { LEDGER_BASE_PATH } from '../lib/ledger'
1112import { ROUTE_NAMES } from '../lib/router'
1213
14+const chopHdPrefix = str =>
15+ str.slice(0, 2) === "m/"
16+ ? str.slice(2)
17+ : str
18+
19+const addHdPrefix = str =>
20+ str.slice(0, 2) === "m/"
21+ ? str
22+ : "m/" + str
23+
1324class Ledger extends React.Component {
25+ constructor(props) {
26+ super(props)
1427
15− componentDidMount() {
16− this.pollDevice()
28+ this.state = {
29+ hdpath: LEDGER_BASE_PATH
30+ }
1731
18− this.pollerId = setInterval(
19− () => this.pollDevice(),
20− 500
21− )
32+ this.pollDevice = this.pollDevice.bind(this)
33+ this.handleHdPathInput = this.handleHdPathInput.bind(this)
2234 }
2335
24− componentWillUnmount() {
25− clearInterval(this.pollerId)
36+ handleHdPathInput(hdpath) {
37+ this.setState({ hdpath })
2638 }
2739
2840 async pollDevice() {
29− const { wallet, setWallet } = this.props
30−
31− if (Maybe.Nothing.hasInstance(wallet)) {
32− const transport = await Transport.create()
33− const eth = new Eth(transport)
34−
35− eth.getAddress(LEDGER_BASE_PATH, false, true)
36− .then(info => {
37− const publicKey = Buffer.from(info.publicKey, 'hex')
38− const chainCode = Buffer.from(info.chainCode, 'hex')
39− const pub = secp256k1.publicKeyConvert(publicKey, true)
40− const hd = bip32.fromPublicKey(pub, chainCode)
41− setWallet(Maybe.Just(hd))
42− }, _ => {
43− setWallet(Maybe.Nothing())
44− })
45− }
41+ const { setWallet, setWalletHdPath } = this.props
42+ const { hdpath } = this.state
43+
44+ const transport = await Transport.create()
45+ const eth = new Eth(transport)
46+ const path = chopHdPrefix(hdpath)
47+
48+ eth.getAddress(path, false, true)
49+ .then(info => {
50+ const publicKey = Buffer.from(info.publicKey, 'hex')
51+ const chainCode = Buffer.from(info.chainCode, 'hex')
52+ const pub = secp256k1.publicKeyConvert(publicKey, true)
53+ const hd = bip32.fromPublicKey(pub, chainCode)
54+ setWallet(Maybe.Just(hd))
55+ setWalletHdPath(addHdPrefix(hdpath))
56+ }, _ => {
57+ setWallet(Maybe.Nothing())
58+ })
4659 }
4760
4861 render() {
4962 const { pushRoute, popRoute, wallet } = this.props
63+ const { hdpath } = this.state
5064
5165 return (
5266
@@ -58,10 +72,17 @@ class Ledger extends React.Component {
5872
5973 <P>
6074 { `Connect and authenticate to your Ledger, and then open the
61− "Ethereum" application. If you're running on older firmware, make sure the browser
62− support" option is turned on.` }
75+ "Ethereum" application. If you're running on older firmware,
76+ make sure the "browser support" option is turned on. To sign
77+ transactions, you'll also need to enable the "contract data"
78+ option.` }
6379 </P>
6480
81+ <P>
82+ { `If you'd like to use a custom derivation path, you may enter
83+ it below.`
84+ }
85+ </P>
6586
6687 <H2>{'Running on HTTP?'}</H2>
6788
@@ -78,19 +99,36 @@ class Ledger extends React.Component {
7899 <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
79100 </ol>
80101
81−
82− <Button
83− className={'mt-8'}
84− disabled={ Maybe.Nothing.hasInstance(wallet) }
85− onClick={
86− () => {
87− popRoute()
88− pushRoute(ROUTE_NAMES.SHIPS)
89− }
90− }
91− >
92− { 'Continue →' }
93− </Button>
102+ <Form>
103+ <Input
104+ className='pt-8'
105+ prop-size='md'
106+ prop-format='innerLabel'
107+ name='hdpath'
108+ value={ addHdPrefix(hdpath) }
109+ autocomplete='off'
110+ onChange={ this.handleHdPathInput }>
111+ <InnerLabel>{'HD Path'}</InnerLabel>
112+ </Input>
113+
114+ <Button
115+ className={'mt-8'}
116+ onClick={ this.pollDevice }>
117+ { 'Authenticate →' }
118+ </Button>
119+
120+ <Button
121+ className={'mt-8'}
122+ disabled={ Maybe.Nothing.hasInstance(wallet) }
123+ onClick={
124+ () => {
125+ popRoute()
126+ pushRoute(ROUTE_NAMES.SHIPS)
127+ }
128+ }>
129+ { 'Continue →' }
130+ </Button>
131+ </Form>
94132
95133 </Col>
96134 </Row>