@@ -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,58 +11,78 @@ 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
54− <Col>
68+ <Col className={'measure-md'}>
55 <H1>{ 'Authenticate With Your Ledger' }</H1>
69 <H1>{ 'Authenticate With Your Ledger' }</H1>
57 <H2>{'Running on HTTPS?'}</H2>
71 <H2>{'Running on HTTPS?'}</H2>
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,38 @@ 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>
104+ className='pt-8 mt-8'
106+ prop-format='innerLabel'
108+ value={ addHdPrefix(hdpath) }
110+ onChange={ this.handleHdPathInput }>
111+ <InnerLabel>{'HD Path'}</InnerLabel>
115+ prop-size={'wide lg'}
83− className={'mt-8'}
116+ className={'mt-8'}
117+ onClick={ this.pollDevice }>
118+ { 'Authenticate →' }
123+ prop-size={'wide lg'}
84− disabled={ Maybe.Nothing.hasInstance(wallet) }
124+ disabled={ Maybe.Nothing.hasInstance(wallet) }
85− onClick={
125+ onClick={
87− popRoute()
127+ popRoute()
88− pushRoute(ROUTE_NAMES.SHIPS)
128+ pushRoute(ROUTE_NAMES.SHIPS)
92− { 'Continue →' }
131+ { 'Continue →' }
93− </Button>
132+ </Button>