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';
25 buildCheckboxValidator,
28} from 'form/validators';
29import BridgeForm from 'form/BridgeForm';
30import FormError from 'form/FormError';
31import SubmitButton from 'form/SubmitButton';
32import Condition from 'form/Condition';
33import { FORM_ERROR } from 'final-form';
34import { HdPathInput } from 'form/Inputs';
35import { getAuthToken } from 'lib/authToken';
38 { text: 'Ledger Live', value: LEDGER_LIVE_PATH },
39 { text: 'Ledger Legacy', value: LEDGER_LEGACY_PATH },
42const ACCOUNT_OPTIONS = times(20, i => ({
43 text: `Account #${i + 1}`,
47export default function Ledger({ className, goHome }) {
48 useLoginView(WALLET_TYPES.LEDGER);
50 const { setWallet, setWalletHdPath, setAuthToken } = useWallet();
52 const validate = useMemo(
55 useCustomPath: buildCheckboxValidator(),
56 derivationpath: buildSelectValidator(PATH_OPTIONS),
57 account: buildSelectValidator(ACCOUNT_OPTIONS),
58 hdpath: buildHdPathValidator(),
63 const onSubmit = useCallback(
65 const transport = await Transport.create();
66 const eth = new Eth(transport);
67 const path = chopHdPrefix(values.hdpath);
70 const info = await eth.getAddress(path, false, true);
71 const publicKey = Buffer.from(info.publicKey, 'hex');
72 const chainCode = Buffer.from(info.chainCode, 'hex');
73 const pub = secp256k1.publicKeyConvert(publicKey, true);
74 const hd = bip32.fromPublicKey(pub, chainCode);
75 const authToken = await getAuthToken({
77 walletType: WALLET_TYPES.LEDGER,
78 walletHdPath: addHdPrefix(values.hdpath),
81 setAuthToken(Just(authToken));
83 setWalletHdPath(addHdPrefix(values.hdpath));
86 return { [FORM_ERROR]: error.message };
89 [setAuthToken, setWallet, setWalletHdPath]
92 const onValues = useCallback(({ valid, values, form }) => {
97 // when the base path pattern or the account index changes
98 // update the hd path in our input
99 if (!values.useCustomPath) {
102 values.derivationpath.replace(/x/g, values.account)
107 const initialValues = useMemo(
109 useCustomPath: false,
110 hdpath: PATH_OPTIONS[0].value.replace(/x/g, ACCOUNT_OPTIONS[0].value),
111 derivationpath: PATH_OPTIONS[0].value,
112 account: ACCOUNT_OPTIONS[0].value,
117 const full = useBreakpoints([true, true, false]);
118 const half = useBreakpoints([false, false, true]);
119 const isHTTPS = document.location.protocol === 'https:';
121 // when not on https, tell user how to get there
122 const renderHTTP = () => (
124 <Grid.Item full as={H5}>
128 <Grid.Item full as={P}>
129 To authenticate and sign transactions with a Ledger, Bridge must be
130 serving over HTTPS on localhost. You can do this via the following:
133 <Grid.Item full as="ol">
138 href="https://github.com/FiloSottile/mkcert"
139 rel="noopener noreferrer">
144 Install a local certificate authority via <code>mkcert -install</code>
147 In your <code>bridge</code> directory, generate a certificate valid
148 for localhost via <code>mkcert localhost</code>. This will produce two
149 files: <code>localhost.pem</code>, the local certificate, and
150 <code>localhost-key.pem</code> , its corresponding private key.
153 Run <code>python bridge-https.py</code>
155 <li className="mt3">Return to this page.</li>
160 const renderHTTPS = () => (
162 <Grid.Item full as={Text} className="f6 gray4 mb3">
163 Connect and authenticate to your Ledger, and then open the "Ethereum"
164 application. If you're running on older firmware, make sure the "browser
165 support" option is turned on. To sign transactions, you'll also need to
166 enable the "contract data" option. You will be prompted to sign a
167 message. This allows Bridge to operate correctly. Never sign this
168 message outside of Bridge.
176 initialValues={initialValues}>
177 {({ handleSubmit, submitting }) => (
179 <Condition when="useCustomPath" is={true}>
180 <Grid.Item full as={HdPathInput} name="hdpath" label="HD Path" />
183 <Condition when="useCustomPath" is={false}>
188 className={cn({ pr1: half })}
189 name="derivationpath"
190 label="Derivation Path"
191 placeholder="Choose path pattern..."
192 options={PATH_OPTIONS}
199 className={cn({ pl1: half })}
202 placeholder="Choose account..."
203 options={ACCOUNT_OPTIONS}
212 label="Custom HD Path"
215 <Grid.Item full as={FormError} />
217 <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
218 {!submitting && 'Authenticate'}
219 {submitting && 'Please check your device'}
228 <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