on faa39fb help sign in
bridge/src/views/Trezor.js
146 lines 4.0 kB history raw
1import * as bip32 from 'bip32';
2import React from 'react';
3import Maybe from 'folktale/maybe';
4import { Button } from '../components/old/Base';
5import { H1, P } from '../components/old/Base';
6import { InnerLabel, Input, InnerLabelDropdown } from '../components/old/Base';
7import TrezorConnect from 'trezor-connect';
8import * as secp256k1 from 'secp256k1';
10import { TREZOR_PATH } from '../lib/trezor';
11import { ROUTE_NAMES } from '../lib/routeNames';
12import { withHistory } from '../store/history';
13import { compose } from '../lib/lib';
14import { withWallet } from '../store/wallet';
15import View from 'components/View';
17class Trezor extends React.Component {
18 constructor(props) {
19 super(props);
21 this.state = {
22 hdpath: TREZOR_PATH.replace(/x/g, 0),
23 account: 0,
24 };
26 this.handleAccountSelection = this.handleAccountSelection.bind(this);
27 this.handleHdPathInput = this.handleHdPathInput.bind(this);
28 this.pollDevice = this.pollDevice.bind(this);
29 }
31 handleAccountSelection(account) {
32 let hdpath = this.state.hdpath;
33 if (account !== 'custom') {
34 hdpath = TREZOR_PATH.replace(/x/g, account);
35 }
36 this.setState({ account, hdpath });
37 }
39 handleHdPathInput(hdpath) {
40 this.setState({ hdpath });
41 }
43 async pollDevice() {
44 const { setWallet, setWalletHdPath } = this.props;
45 const { hdpath } = this.state;
47 TrezorConnect.manifest({
48 email: 'bridge-trezor@urbit.org',
49 appUrl: 'https://github.com/urbit/bridge',
50 });
51 TrezorConnect.getPublicKey({ path: hdpath }).then(info => {
52 if (info.success === true) {
53 const payload = info.payload;
54 const publicKey = Buffer.from(payload.publicKey, 'hex');
55 const chainCode = Buffer.from(payload.chainCode, 'hex');
56 const pub = secp256k1.publicKeyConvert(publicKey, true);
57 const hd = bip32.fromPublicKey(pub, chainCode);
58 setWallet(Maybe.Just(hd));
59 setWalletHdPath(hdpath);
60 } else {
61 setWallet(Maybe.Nothing());
62 }
63 });
64 }
66 render() {
67 const { history, wallet } = this.props;
68 const { hdpath, account } = this.state;
69 const { handleAccountSelection } = this;
71 let accountOptions = [
72 {
73 title: 'Custom path',
74 value: 'custom',
75 },
76 ];
77 for (let i = 0; i < 20; i++) {
78 accountOptions.push({
79 title: 'Account #' + (i + 1),
80 value: i,
81 });
82 }
83 let accountTitle = accountOptions.find(o => o.value === account).title;
85 const accountSelection = (
86 <InnerLabelDropdown
87 className="mt-8"
88 prop-size="md"
89 prop-format="innerLabel"
90 options={accountOptions}
91 handleUpdate={handleAccountSelection}
92 title={'Account'}
93 currentSelectionTitle={accountTitle}
94 fullWidth={true}
95 />
96 );
98 const pathSelection =
99 account !== 'custom' ? null : (
100 <Input
101 className="mt-8 pt-8 text-mono"
102 prop-size="md"
103 prop-format="innerLabel"
104 name="hdpath"
105 value={hdpath}
106 autocomplete="off"
107 onChange={this.handleHdPathInput}>
108 <InnerLabel>{'HD Path'}</InnerLabel>
109 </Input>
110 );
112 return (
113 <View>
114 <H1>{'Authenticate With Your Trezor'}</H1>
116 <P>
117 {`Connect and authenticate to your Trezor. If you'd like
118 to use a custom derivation path, you may enter it below.`}
119 </P>
121 {accountSelection}
122 {pathSelection}
124 <Button
125 className={'mt-8'}
126 prop-size={'wide lg'}
127 onClick={this.pollDevice}>
128 {'Authenticate →'}
129 </Button>
131 <Button
132 className={'mt-8'}
133 prop-size={'wide lg'}
134 disabled={Maybe.Nothing.hasInstance(wallet)}
135 onClick={() => history.popAndPush(ROUTE_NAMES.POINTS)}>
136 {'Continue →'}
137 </Button>
138 </View>
139 );
140 }
143export default compose(
144 withHistory,
145 withWallet
146)(Trezor);