import React, { useCallback, useEffect } from 'react';
import cn from 'classnames';
import { Just, Nothing } from 'folktale/maybe';
import {
P,
Text,
Input,
Grid,
H5,
CheckboxInput,
SelectInput,
} from 'indigo-react';
import { times } from 'lodash';
import * as bip32 from 'bip32';
import Transport from '@ledgerhq/hw-transport-u2f';
import Eth from '@ledgerhq/hw-app-eth';
import * as secp256k1 from 'secp256k1';
import { useWallet } from 'store/wallet';
import { ForwardButton } from 'components/Buttons';
import {
useCheckboxInput,
useHdPathInput,
useSelectInput,
} from 'lib/useInputs';
import {
LEDGER_LIVE_PATH,
LEDGER_LEGACY_PATH,
chopHdPrefix,
addHdPrefix,
} from 'lib/ledger';
import { WALLET_TYPES } from 'lib/wallet';
import useLoginView from 'lib/useLoginView';
import useBreakpoints from 'lib/useBreakpoints';
const PATH_OPTIONS = [
{ text: 'Ledger Live', value: LEDGER_LIVE_PATH },
{ text: 'Ledger Legacy', value: LEDGER_LEGACY_PATH },
];
const ACCOUNT_OPTIONS = times(20, i => ({
text: `Account #${i + 1}`,
value: i,
}));
export default function Ledger({ className }) {
useLoginView(WALLET_TYPES.LEDGER);
const { setWallet, setWalletHdPath } = useWallet();
// derivation path input
const [derivationPathInput, { data: basePathPattern }] = useSelectInput({
name: 'derivationpath',
label: 'Derivation Path',
placeholder: 'Choose path pattern...',
options: PATH_OPTIONS,
});
// account input
const [accountInput, { data: accountIndex }] = useSelectInput({
name: 'account',
label: 'Account',
placeholder: 'Choose account...',
options: ACCOUNT_OPTIONS,
});
// custom toggle
const [customPathInput, { data: useCustomPath }] = useCheckboxInput({
name: 'customPath',
label: 'Custom HD Path',
autoComplete: 'off',
initialValue: false,
});
// hd path input
const [
hdPathInput,
{ data: hdPath },
{ setValue: setHdPath },
] = useHdPathInput({
name: 'hdpath',
label: 'HD Path',
initialValue: basePathPattern.replace(/x/g, 0),
});
const pollDevice = useCallback(async () => {
const transport = await Transport.create();
const eth = new Eth(transport);
const path = chopHdPrefix(hdPath);
try {
const info = await eth.getAddress(path, false, true);
const publicKey = Buffer.from(info.publicKey, 'hex');
const chainCode = Buffer.from(info.chainCode, 'hex');
const pub = secp256k1.publicKeyConvert(publicKey, true);
const hd = bip32.fromPublicKey(pub, chainCode);
setWallet(Just(hd));
setWalletHdPath(addHdPrefix(hdPath));
} catch (error) {
console.error(error);
setWallet(Nothing());
}
}, [hdPath, setWallet, setWalletHdPath]);
// when the base path pattern or the account index changes
// update the hd path in our input
useEffect(() => {
if (useCustomPath) {
// updated by useForm
} else {
setHdPath(basePathPattern.replace(/x/g, accountIndex));
}
}, [useCustomPath, setHdPath, basePathPattern, accountIndex]);
const full = useBreakpoints([true, true, false]);
const half = useBreakpoints([false, false, true]);
const isHTTPS = document.location.protocol === 'https:';
// when not on https, tell user how to get there
const renderHTTP = () => (
<>
Running on HTTP?
To authenticate and sign transactions with a Ledger, Bridge must be
serving over HTTPS on localhost. You can do this via the following:
Install{' '}
mkcert
Install a local certificate authority via mkcert -install
In your bridge directory, generate a certificate valid
for localhost via mkcert localhost. This will produce two
files: localhost.pem, the local certificate, and
localhost-key.pem , its corresponding private key.
Run python bridge-https.py
Return to this page.
>
);
const renderHTTPS = () => (
<>
Authenticate With Your Ledger
Connect and authenticate to your Ledger, and then open the "Ethereum"
application. If you're running on older firmware, make sure the "browser
support" option is turned on. To sign transactions, you'll also need to
enable the "contract data" option.
{useCustomPath && }
{!useCustomPath && (
<>
>
)}
Authenticate
>
);
return (
{isHTTPS ? renderHTTPS() : renderHTTP()}
);
}