on 58fbcd2 help sign in
bridge/src/views/Login/Ticket.js
208 lines 5.8 kB history raw
1import React, { useCallback, useMemo, useRef } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
3import cn from 'classnames';
4import * as azimuth from 'azimuth-js';
5import * as kg from 'urbit-key-generation/dist/index';
6import { Grid, CheckboxInput } from 'indigo-react';
7import { FORM_ERROR } from 'final-form';
9import { useNetwork } from 'store/network';
10import { useWallet } from 'store/wallet';
11import { usePointCursor } from 'store/pointCursor';
13import * as need from 'lib/need';
14import { WALLET_TYPES, urbitWalletFromTicket } from 'lib/wallet';
15import useImpliedPoint from 'lib/useImpliedPoint';
16import useLoginView from 'lib/useLoginView';
17import patp2dec from 'lib/patp2dec';
18import timeout from 'lib/timeout';
20import BridgeForm from 'form/BridgeForm';
21import Condition from 'form/Condition';
22import { TicketInput, PassphraseInput, PointInput } from 'form/Inputs';
23import {
24 composeValidator,
25 buildCheckboxValidator,
26 buildPatqValidator,
27 buildPassphraseValidator,
28 buildPointValidator,
29} from 'form/validators';
30import FormError from 'form/FormError';
32import ContinueButton from './ContinueButton';
33import useSetState from 'lib/useSetState';
35export default function Ticket({ className, goHome }) {
36 useLoginView(WALLET_TYPES.TICKET);
38 const { contracts } = useNetwork();
39 const { setUrbitWallet } = useWallet();
40 const { setPointCursor } = usePointCursor();
41 const impliedPoint = useImpliedPoint();
42 const [warnings, addWarning] = useSetState();
44 const cachedUrbitWallet = useRef(Nothing());
46 const validateForm = useCallback(
47 async (values, errors) => {
48 if (errors.point) {
49 return errors;
50 }
52 await timeout(100); // allow ui events to flush
54 let ticket;
55 if (values.useShards) {
56 if (errors.shard1 || errors.shard2 || errors.shard3) {
57 return errors;
58 }
60 ticket = kg.combine([values.shard1, values.shard2, values.shard3]);
61 } else {
62 if (errors.ticket) {
63 return errors;
64 }
66 ticket = values.ticket;
67 }
69 try {
70 // ticket
71 const _contracts = need.contracts(contracts);
72 const point = patp2dec(values.point);
74 cachedUrbitWallet.current = await urbitWalletFromTicket(
75 ticket,
76 point,
77 values.passphrase
78 );
80 const [isOwner, isTransferProxy] = await Promise.all([
81 azimuth.azimuth.isOwner(
82 _contracts,
83 point,
84 cachedUrbitWallet.current.ownership.keys.address
85 ),
86 azimuth.azimuth.isTransferProxy(
87 _contracts,
88 point,
89 cachedUrbitWallet.current.ownership.keys.address
90 ),
91 ]);
93 const noPermissions = !isOwner && !isTransferProxy;
94 // notify the user, but allow login regardless
95 addWarning({
96 point: noPermissions
97 ? 'This wallet is not the owner or transfer proxy for this point.'
98 : null,
99 });
100 } catch (error) {
101 console.error(error);
102 return {
103 [FORM_ERROR]: `Unable to derive wallet from ${
104 values.useShards ? 'shards' : 'ticket'
105 }.`,
106 };
107 }
108 },
109 [addWarning, contracts]
110 );
112 const validate = useMemo(
113 () =>
114 composeValidator(
115 {
116 usePassphrase: buildCheckboxValidator(),
117 useShards: buildCheckboxValidator(),
118 point: buildPointValidator(4),
119 ticket: buildPatqValidator(),
120 shard1: buildPatqValidator(),
121 shard2: buildPatqValidator(),
122 shard3: buildPatqValidator(),
123 passphrase: buildPassphraseValidator(),
124 },
125 validateForm
126 ),
127 [validateForm]
128 );
130 const onValues = useCallback(
131 ({ valid, values }) => {
132 if (valid) {
133 setUrbitWallet(Just(cachedUrbitWallet.current));
134 setPointCursor(Just(patp2dec(values.point)));
135 } else {
136 setUrbitWallet(Nothing());
137 }
138 },
139 [setPointCursor, setUrbitWallet]
140 );
142 const initialValues = useMemo(
143 () => ({
144 point: impliedPoint || '',
145 usePasshrase: false,
146 useShards: false,
147 }),
148 [impliedPoint]
149 );
151 return (
152 <Grid className={cn('mt4', className)}>
153 <BridgeForm
154 warnings={warnings}
155 validate={validate}
156 onValues={onValues}
157 afterSubmit={goHome}
158 initialValues={initialValues}>
159 {({ handleSubmit }) => (
160 <>
161 <Grid.Item full as={PointInput} name="point" />
163 <Condition when="useShards" is={false}>
164 <Grid.Item
165 full
166 as={TicketInput}
167 name="ticket"
168 label="Master Ticket"
169 />
170 </Condition>
172 <Condition when="useShards" is={true}>
173 <Grid.Item full as={TicketInput} name="shard1" label="Shard 1" />
174 <Grid.Item full as={TicketInput} name="shard2" label="Shard 2" />
175 <Grid.Item full as={TicketInput} name="shard3" label="Shard 3" />
176 </Condition>
178 <Condition when="usePassphrase" is={true}>
179 <Grid.Item
180 full
181 as={PassphraseInput}
182 name="passphrase"
183 label="Wallet Passphrase"
184 />
185 </Condition>
187 <Grid.Item
188 full
189 as={CheckboxInput}
190 name="usePassphrase"
191 label="Passphrase"
192 />
193 <Grid.Item
194 full
195 as={CheckboxInput}
196 name="useShards"
197 label="Shards"
198 />
200 <Grid.Item full as={FormError} />
202 <Grid.Item full as={ContinueButton} handleSubmit={handleSubmit} />
203 </>
204 )}
205 </BridgeForm>
206 </Grid>
207 );