on 4c8c607 help sign in
bridge/src/views/Login/Trezor.js
154 lines 4.5 kB history raw
1import React, { useCallback, useMemo } from 'react';
2import { Just } from 'folktale/maybe';
3import * as bip32 from 'bip32';
4import { times } from 'lodash';
5import TrezorConnect from 'trezor-connect';
6import * as secp256k1 from 'secp256k1';
7import { Text, Grid, H5, CheckboxInput, SelectInput } from 'indigo-react';
9import { useWallet } from 'store/wallet';
11import { TREZOR_PATH } from 'lib/trezor';
12import { WALLET_TYPES } from 'lib/wallet';
13import useLoginView from 'lib/useLoginView';
14import { getAuthToken } from 'lib/authToken';
16import {
17 composeValidator,
18 buildCheckboxValidator,
19 buildSelectValidator,
20 buildHdPathValidator,
21} from 'form/validators';
22import BridgeForm from 'form/BridgeForm';
23import Condition from 'form/Condition';
24import FormError from 'form/FormError';
26import SubmitButton from 'form/SubmitButton';
27import { FORM_ERROR } from 'final-form';
28import { HdPathInput } from 'form/Inputs';
30const ACCOUNT_OPTIONS = times(20, i => ({
31 text: `Account #${i + 1}`,
32 value: i,
33}));
35// see Ledger.js for context — Trezor is basicaly Ledger with less complexity
36export default function Trezor({ className, goHome }) {
37 useLoginView(WALLET_TYPES.TREZOR);
39 const { setWallet, setWalletHdPath, setAuthToken } = useWallet();
41 const validate = useMemo(
42 () =>
43 composeValidator({
44 useCustomPath: buildCheckboxValidator(),
45 account: buildSelectValidator(ACCOUNT_OPTIONS),
46 hdPath: buildHdPathValidator(),
47 }),
48 []
49 );
51 const onSubmit = useCallback(
52 async values => {
53 TrezorConnect.manifest({
54 email: 'bridge-trezor@urbit.org',
55 appUrl: 'https://github.com/urbit/bridge',
56 });
58 const { success, payload } = await TrezorConnect.getPublicKey({
59 path: values.hdPath,
60 });
62 if (!success) {
63 return { [FORM_ERROR]: 'Failed to authenticate with your Trezor.' };
64 }
66 const publicKey = Buffer.from(payload.publicKey, 'hex');
67 const chainCode = Buffer.from(payload.chainCode, 'hex');
68 const pub = secp256k1.publicKeyConvert(publicKey, true);
69 const hd = bip32.fromPublicKey(pub, chainCode);
71 const authToken = await getAuthToken({
72 wallet: hd,
73 walletType: WALLET_TYPES.TREZOR,
74 walletHdPath: values.hdPath,
75 });
77 setAuthToken(Just(authToken));
78 setWallet(Just(hd));
79 setWalletHdPath(values.hdPath);
80 },
81 [setAuthToken, setWallet, setWalletHdPath]
82 );
84 const onValues = useCallback(({ valid, values, form }) => {
85 if (!valid) {
86 return;
87 }
89 if (!values.useCustomPath) {
90 form.change('hdPath', TREZOR_PATH.replace(/x/g, values.account));
91 }
92 }, []);
94 const initialValues = useMemo(
95 () => ({
96 useCustomPath: false,
97 hdPath: TREZOR_PATH.replace(/x/g, ACCOUNT_OPTIONS[0].value),
98 account: ACCOUNT_OPTIONS[0].value,
99 }),
100 []
101 );
103 return (
104 <Grid className={className}>
105 <Grid.Item full as={Text} className="f6 gray4 mb3">
106 Connect and authenticate to your Trezor. If you'd like to use a custom
107 derivation path, you may enter it below. Upon login, you will prompted
108 to sign the message "Bridge Authentication Token". This allows Bridge to
109 operate correctly. Never sign this message outside of Bridge.
110 </Grid.Item>
112 <BridgeForm
113 validate={validate}
114 onValues={onValues}
115 onSubmit={onSubmit}
116 afterSubmit={goHome}
117 initialValues={initialValues}>
118 {({ handleSubmit, submitting }) => (
119 <>
120 <Condition when="useCustomPath" is={true}>
121 <Grid.Item full as={HdPathInput} name="hdPath" label="HD Path" />
122 </Condition>
124 <Condition when="useCustomPath" is={false}>
125 <Grid.Item
126 full
127 as={SelectInput}
128 name="account"
129 label="Account"
130 placeholder="Choose account..."
131 options={ACCOUNT_OPTIONS}
132 />
133 </Condition>
135 <Grid.Item
136 full
137 as={CheckboxInput}
138 className="mv3"
139 name="useCustomPath"
140 label="Custom HD Path"
141 />
143 <Grid.Item full as={FormError} />
145 <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
146 {!submitting && 'Authenticate'}
147 {submitting && 'Please check your device'}
148 </Grid.Item>
149 </>
150 )}
151 </BridgeForm>
152 </Grid>
153 );