1import React, { useCallback, useMemo } from 'react';
2import cn from 'classnames';
3import { Just } from 'folktale/maybe';
4import { P, Text, Grid, H5, CheckboxInput, SelectInput } from 'indigo-react';
5import { times } from 'lodash';
6import * as bip32 from 'bip32';
7import Transport from '@ledgerhq/hw-transport-u2f';
8import Eth from '@ledgerhq/hw-app-eth';
9import * as secp256k1 from 'secp256k1';
11import { useWallet } from 'store/wallet';
19import { WALLET_TYPES } from 'lib/wallet';
20import useLoginView from 'lib/useLoginView';
21import useBreakpoints from 'lib/useBreakpoints';
24 buildCheckboxValidator,
27} from 'form/validators';
28import BridgeForm from 'form/BridgeForm';
29import FormError from 'form/FormError';
30import SubmitButton from 'form/SubmitButton';
31import Condition from 'form/Condition';
32import { FORM_ERROR } from 'final-form';
33import { HdPathInput } from 'form/Inputs';
36 { text: 'Ledger Live', value: LEDGER_LIVE_PATH },
37 { text: 'Ledger Legacy', value: LEDGER_LEGACY_PATH },
40const ACCOUNT_OPTIONS = times(20, i => ({
41 text: `Account #${i + 1}`,
45export default function Ledger({ className, goHome }) {
46 useLoginView(WALLET_TYPES.LEDGER);
48 const { setWallet, setWalletHdPath } = useWallet();
50 const validate = useMemo(
53 useCustomPath: buildCheckboxValidator(),
54 derivationpath: buildSelectValidator(PATH_OPTIONS),
55 account: buildSelectValidator(ACCOUNT_OPTIONS),
56 hdpath: buildHdPathValidator(),
61 const onSubmit = useCallback(
63 const transport = await Transport.create();
64 const eth = new Eth(transport);
65 const path = chopHdPrefix(values.hdpath);
68 const info = await eth.getAddress(path, false, true);
69 const publicKey = Buffer.from(info.publicKey, 'hex');
70 const chainCode = Buffer.from(info.chainCode, 'hex');
71 const pub = secp256k1.publicKeyConvert(publicKey, true);
72 const hd = bip32.fromPublicKey(pub, chainCode);
75 setWalletHdPath(addHdPrefix(values.hdpath));
77 return { [FORM_ERROR]: error.message };
80 [setWallet, setWalletHdPath]
83 const onValues = useCallback(({ valid, values, form }) => {
88 // when the base path pattern or the account index changes
89 // update the hd path in our input
90 if (!values.useCustomPath) {
93 values.derivationpath.replace(/x/g, values.account)
98 const initialValues = useMemo(
100 useCustomPath: false,
101 hdpath: PATH_OPTIONS[0].value.replace(/x/g, ACCOUNT_OPTIONS[0].value),
102 derivationpath: PATH_OPTIONS[0].value,
103 account: ACCOUNT_OPTIONS[0].value,
108 const full = useBreakpoints([true, true, false]);
109 const half = useBreakpoints([false, false, true]);
110 const isHTTPS = document.location.protocol === 'https:';
112 // when not on https, tell user how to get there
113 const renderHTTP = () => (
115 <Grid.Item full as={H5}>
119 <Grid.Item full as={P}>
120 To authenticate and sign transactions with a Ledger, Bridge must be
121 serving over HTTPS on localhost. You can do this via the following:
124 <Grid.Item full as="ol">
129 href="https://github.com/FiloSottile/mkcert"
130 rel="noopener noreferrer">
135 Install a local certificate authority via <code>mkcert -install</code>
138 In your <code>bridge</code> directory, generate a certificate valid
139 for localhost via <code>mkcert localhost</code>. This will produce two
140 files: <code>localhost.pem</code>, the local certificate, and
141 <code>localhost-key.pem</code> , its corresponding private key.
144 Run <code>python bridge-https.py</code>
146 <li className="mt3">Return to this page.</li>
151 const renderHTTPS = () => (
153 <Grid.Item full as={H5}>
154 Authenticate With Your Ledger
156 <Grid.Item full as={Text} className="f6 mb3">
157 Connect and authenticate to your Ledger, and then open the "Ethereum"
158 application. If you're running on older firmware, make sure the "browser
159 support" option is turned on. To sign transactions, you'll also need to
160 enable the "contract data" option.
168 initialValues={initialValues}>
169 {({ handleSubmit }) => (
171 <Condition when="useCustomPath" is={true}>
172 <Grid.Item full as={HdPathInput} name="hdpath" label="HD Path" />
175 <Condition when="useCustomPath" is={false}>
180 className={cn({ pr1: half })}
181 name="derivationpath"
182 label="Derivation Path"
183 placeholder="Choose path pattern..."
184 options={PATH_OPTIONS}
191 className={cn({ pl1: half })}
194 placeholder="Choose account..."
195 options={ACCOUNT_OPTIONS}
204 label="Custom HD Path"
207 <Grid.Item full as={FormError} />
209 <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
219 <Grid className={className}>{isHTTPS ? renderHTTPS() : renderHTTP()}</Grid>
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list