on e366ef3 help sign in
bridge/src/views/Trezor.js
147 lines 4.1 kB history raw
1import * as bip32 from 'bip32';
2import React from 'react';
3import Maybe from 'folktale/maybe';
4import { Button } from '../components/old/Base';
5import { Row, Col, 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';
16class Trezor extends React.Component {
17 constructor(props) {
18 super(props);
20 this.state = {
21 hdpath: TREZOR_PATH.replace(/x/g, 0),
22 account: 0,
23 };
25 this.handleAccountSelection = this.handleAccountSelection.bind(this);
26 this.handleHdPathInput = this.handleHdPathInput.bind(this);
27 this.pollDevice = this.pollDevice.bind(this);
28 }
30 handleAccountSelection(account) {
31 let hdpath = this.state.hdpath;
32 if (account !== 'custom') {
33 hdpath = TREZOR_PATH.replace(/x/g, account);
34 }
35 this.setState({ account, hdpath });
36 }
38 handleHdPathInput(hdpath) {
39 this.setState({ hdpath });
40 }
42 async pollDevice() {
43 const { setWallet, setWalletHdPath } = this.props;
44 const { hdpath } = this.state;
46 TrezorConnect.manifest({
47 email: 'bridge-trezor@urbit.org',
48 appUrl: 'https://github.com/urbit/bridge',
49 });
50 TrezorConnect.getPublicKey({ path: hdpath }).then(info => {
51 if (info.success === true) {
52 const payload = info.payload;
53 const publicKey = Buffer.from(payload.publicKey, 'hex');
54 const chainCode = Buffer.from(payload.chainCode, 'hex');
55 const pub = secp256k1.publicKeyConvert(publicKey, true);
56 const hd = bip32.fromPublicKey(pub, chainCode);
57 setWallet(Maybe.Just(hd));
58 setWalletHdPath(hdpath);
59 } else {
60 setWallet(Maybe.Nothing());
61 }
62 });
63 }
65 render() {
66 const { history, wallet } = this.props;
67 const { hdpath, account } = this.state;
68 const { handleAccountSelection } = this;
70 let accountOptions = [
71 {
72 title: 'Custom path',
73 value: 'custom',
74 },
75 ];
76 for (let i = 0; i < 20; i++) {
77 accountOptions.push({
78 title: 'Account #' + (i + 1),
79 value: i,
80 });
81 }
82 let accountTitle = accountOptions.find(o => o.value === account).title;
84 const accountSelection = (
85 <InnerLabelDropdown
86 className="mt-8"
87 prop-size="md"
88 prop-format="innerLabel"
89 options={accountOptions}
90 handleUpdate={handleAccountSelection}
91 title={'Account'}
92 currentSelectionTitle={accountTitle}
93 fullWidth={true}
94 />
95 );
97 const pathSelection =
98 account !== 'custom' ? null : (
99 <Input
100 className="mt-8 pt-8 text-mono"
101 prop-size="md"
102 prop-format="innerLabel"
103 name="hdpath"
104 value={hdpath}
105 autocomplete="off"
106 onChange={this.handleHdPathInput}>
107 <InnerLabel>{'HD Path'}</InnerLabel>
108 </Input>
109 );
111 return (
112 <Row>
113 <Col className={'measure-md'}>
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.SHIPS)}>
136 {'Continue →'}
137 </Button>
138 </Col>
139 </Row>
140 );
141 }
144export default compose(
145 withHistory,
146 withWallet
147)(Trezor);