on d760cbf help sign in
bridge/src/views/Login/Ticket.js
185 lines 5.0 kB history raw
1import { Just, Nothing } from 'folktale/maybe';
2import React, { useCallback, useState, useEffect } from 'react';
3import * as azimuth from 'azimuth-js';
4import * as ob from 'urbit-ob';
5import * as kg from 'urbit-key-generation/dist/index';
6import { Input, Grid, CheckboxInput } from 'indigo-react';
8import { useNetwork } from 'store/network';
9import { useWallet } from 'store/wallet';
10import { usePointCursor } from 'store/pointCursor';
12import {
13 usePointInput,
14 useTicketInput,
15 usePassphraseInput,
16 useCheckboxInput,
17} from 'lib/useInputs';
18import * as need from 'lib/need';
19import { WALLET_TYPES, urbitWalletFromTicket } from 'lib/wallet';
20import useImpliedPoint from 'lib/useImpliedPoint';
21import useLoginView from 'lib/useLoginView';
23export default function Ticket({ className }) {
24 useLoginView(WALLET_TYPES.TICKET);
26 const { contracts } = useNetwork();
27 const { setUrbitWallet } = useWallet();
28 const { setPointCursor } = usePointCursor();
29 const impliedPoint = useImpliedPoint();
31 // point
32 const [pointInput, { data: pointName }] = usePointInput({
33 name: 'point',
34 initialValue: impliedPoint || '',
35 autoFocus: true,
36 });
38 // passphrase
39 const [passphraseInput, { data: passphrase }] = usePassphraseInput({
40 name: 'passphrase',
41 label: 'Wallet Passphrase',
42 });
44 const [hasPassphraseInput] = useCheckboxInput({
45 name: 'has-passphrase',
46 label: 'Passphrase',
47 initialValue: false,
48 });
50 // ticket
51 const [error, setError] = useState();
52 const [deriving, setDeriving] = useState(false);
53 const [ticketInput, { data: ticket, pass: validTicket }] = useTicketInput({
54 name: 'ticket',
55 label: 'Master Ticket',
56 error,
57 deriving,
58 });
60 // shards
61 const [shardsInput, { data: isUsingShards }] = useCheckboxInput({
62 name: 'shards',
63 label: 'Shards',
64 initialValue: false,
65 });
67 const [shard1Input, { data: shard1, pass: shard1Pass }] = useTicketInput({
68 name: 'shard1',
69 label: 'Shard 1',
70 });
72 const [shard2Input, { data: shard2, pass: shard2Pass }] = useTicketInput({
73 name: 'shard2',
74 label: 'Shard 2',
75 });
77 const [shard3Input, { data: shard3, pass: shard3Pass }] = useTicketInput({
78 name: 'shard3',
79 label: 'Shard 3',
80 });
82 const shardsReady = shard1Pass && shard2Pass && shard3Pass;
84 // TODO: maybe want to do this only on-go, because wallet derivation is slow...
85 const deriveWalletFromTicket = useCallback(async () => {
86 // clear states
87 setError();
88 setDeriving(true);
89 setUrbitWallet(Nothing());
91 if (!ob.isValidPatq(ticket) || !ob.isValidPatp(pointName)) {
92 setDeriving(false);
93 return;
94 }
96 const _contracts = need.contracts(contracts);
97 const pointNumber = ob.patp2dec(pointName);
98 const urbitWallet = await urbitWalletFromTicket(
99 ticket,
100 pointName,
101 passphrase
102 );
103 const [isOwner, isTransferProxy] = await Promise.all([
104 azimuth.azimuth.isOwner(
105 _contracts,
106 pointNumber,
107 urbitWallet.ownership.keys.address
108 ),
109 azimuth.azimuth.isTransferProxy(
110 _contracts,
111 pointNumber,
112 urbitWallet.ownership.keys.address
113 ),
114 ]);
116 if (!isOwner && !isTransferProxy) {
117 // notify the user, but allow login regardless
118 setError(
119 'This ticket is not the owner of or transfer proxy for this point.'
120 );
121 }
123 setUrbitWallet(Just(urbitWallet));
124 setPointCursor(Just(ob.patp2dec(pointName)));
125 setDeriving(false);
126 }, [
127 pointName,
128 ticket,
129 passphrase,
130 contracts,
131 setUrbitWallet,
132 setPointCursor,
133 setDeriving,
134 ]);
136 const deriveWalletFromShards = useCallback(async () => {
137 const s1 = shard1 || undefined;
138 const s2 = shard2 || undefined;
139 const s3 = shard3 || undefined;
141 try {
142 const ticket = kg.combine([s1, s2, s3]);
143 const uhdw = await urbitWalletFromTicket(ticket, pointName, passphrase);
144 setUrbitWallet(Just(uhdw));
145 } catch (_) {
146 // do nothing
147 }
148 }, [passphrase, pointName, setUrbitWallet, shard1, shard2, shard3]);
150 // derive wallet on change
151 useEffect(() => {
152 if (isUsingShards && shardsReady) {
153 deriveWalletFromShards();
154 } else if (validTicket) {
155 deriveWalletFromTicket();
156 }
157 }, [
158 isUsingShards,
159 validTicket,
160 shardsReady,
161 deriveWalletFromShards,
162 deriveWalletFromTicket,
163 ]);
165 return (
166 <Grid className={className}>
167 <Grid.Item full as={Input} {...pointInput} />
169 {!isUsingShards && <Grid.Item full as={Input} {...ticketInput} />}
170 {isUsingShards && (
171 <>
172 <Grid.Item full as={Input} {...shard1Input} />
173 <Grid.Item full as={Input} {...shard2Input} />
174 <Grid.Item full as={Input} {...shard3Input} />
175 </>
176 )}
177 {hasPassphraseInput.data && (
178 <Grid.Item full as={Input} {...passphraseInput} />
179 )}
181 <Grid.Item full as={CheckboxInput} {...hasPassphraseInput} />
182 <Grid.Item full as={CheckboxInput} {...shardsInput} />
183 </Grid>
184 );