on 616e621 help sign in
bridge/src/views/Ledger.js
141 lines 4.5 kB history raw
1import * as bip32 from 'bip32'
2import React from 'react'
3import Maybe from 'folktale/maybe'
4import { Button } from '../components/Base'
5import { Row, Col, H1, P, H2 } from '../components/Base'
6import { Form, Input, InnerLabel } from '../components/Base'
7import Transport from '@ledgerhq/hw-transport-u2f'
8import Eth from '@ledgerhq/hw-app-eth'
9import * as secp256k1 from 'secp256k1'
11import { LEDGER_BASE_PATH } from '../lib/ledger'
12import { ROUTE_NAMES } from '../lib/router'
14const chopHdPrefix = str =>
15 str.slice(0, 2) === "m/"
16 ? str.slice(2)
17 : str
19const addHdPrefix = str =>
20 str.slice(0, 2) === "m/"
21 ? str
22 : "m/" + str
24class Ledger extends React.Component {
25 constructor(props) {
26 super(props)
28 this.state = {
29 hdpath: LEDGER_BASE_PATH
30 }
32 this.pollDevice = this.pollDevice.bind(this)
33 this.handleHdPathInput = this.handleHdPathInput.bind(this)
34 }
36 handleHdPathInput(hdpath) {
37 this.setState({ hdpath })
38 }
40 async pollDevice() {
41 const { setWallet, setWalletHdPath } = this.props
42 const { hdpath } = this.state
44 const transport = await Transport.create()
45 const eth = new Eth(transport)
46 const path = chopHdPrefix(hdpath)
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 })
59 }
61 render() {
62 const { pushRoute, popRoute, wallet } = this.props
63 const { hdpath } = this.state
65 return (
67 <Row>
68 <Col className={'measure-md'}>
69 <H1>{ 'Authenticate With Your Ledger' }</H1>
71 <H2>{'Running on HTTPS?'}</H2>
73 <P>
74 { `Connect and authenticate to your Ledger, and then open the
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.` }
79 </P>
81 <P>
82 { `If you'd like to use a custom derivation path, you may enter
83 it below.`
84 }
85 </P>
87 <H2>{'Running on HTTP?'}</H2>
89 <P>{`To authenticate and sign transactions with a Ledger, Bridge
90 must be serving over HTTPS on localhost. You can do this via the
91 following:`}</P>
93 <ol className={'measure-md'}>
94 <li className={'mt-4'}>{'Install'} <a target={'_blank'} href={'https://github.com/FiloSottile/mkcert'}>{'mkcert'}</a></li>
95 <li className={'mt-4'}>{'Install a local certificate authority via '}<code>{'mkcert -install'}</code></li>
96 <li className={'mt-4'}>{'In your '}<code>{'bridge'}</code>{' directory, generate a certificate valid for localhost via '} <code>{'mkcert localhost'}</code>{'.'}
97 {'This will produce two files: '}<code>{'localhost.pem'}</code> {', the local certificate, and '}<code>{'localhost-key.pem'}</code>{', its corresponding private key.'}
98 </li>
99 <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
100 </ol>
102 <Form>
103 <Input
104 className='pt-8 mt-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>
114 <Button
115 prop-size={'wide lg'}
116 className={'mt-8'}
117 onClick={ this.pollDevice }>
118 { 'Authenticate →' }
119 </Button>
121 <Button
122 className={'mt-8'}
123 prop-size={'wide lg'}
124 disabled={ Maybe.Nothing.hasInstance(wallet) }
125 onClick={
126 () => {
127 popRoute()
128 pushRoute(ROUTE_NAMES.SHIPS)
129 }
130 }>
131 { 'Continue →' }
132 </Button>
133 </Form>
135 </Col>
136 </Row>
137 )
138 }
141export default Ledger