@@ -0,0 +1,294 @@
1+import React, { useCallback, useMemo, useRef, useState } from 'react';
2+import { Just } from 'folktale/maybe';
3+import { Grid, ToggleInput, CheckboxInput } from 'indigo-react';
4+import * as azimuth from 'azimuth-js';
5+import { randomHex } from 'web3-utils';
7+import { usePointCursor } from 'store/pointCursor';
8+import { usePointCache } from 'store/pointCache';
9+import { useNetwork } from 'store/network';
10+import { useWallet } from 'store/wallet';
12+import { useLocalRouter } from 'lib/LocalRouter';
13+import * as need from 'lib/need';
15+ attemptNetworkSeedDerivation,
17+ CRYPTO_SUITE_VERSION,
19+import useEthereumTransaction from 'lib/useEthereumTransaction';
20+import { GAS_LIMITS } from 'lib/constants';
21+import { addHexPrefix } from 'lib/wallet';
22+import useKeyfileGenerator from 'lib/useKeyfileGenerator';
24+import ViewHeader from 'components/ViewHeader';
25+import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
26+import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
27+import NoticeBox from 'components/NoticeBox';
29+import { HexInput } from 'form/Inputs';
32+ buildCheckboxValidator,
34+} from 'form/validators';
35+import BridgeForm from 'form/BridgeForm';
36+import Condition from 'form/Condition';
37+import FormError from 'form/FormError';
38+import convertToInt from 'lib/convertToInt';
40+function useSetKeys() {
41+ const { urbitWallet, wallet, authMnemonic } = useWallet();
42+ const { pointCursor } = usePointCursor();
43+ const { syncDetails, syncRekeyDate, getDetails } = usePointCache();
44+ const { contracts } = useNetwork();
46+ const _point = need.point(pointCursor);
47+ const _contracts = need.contracts(contracts);
48+ const _details = need.details(getDetails(_point));
50+ const networkRevision = convertToInt(_details.keyRevisionNumber, 10);
51+ const randomSeed = useRef();
53+ // NOTE: nd = 'nondeterministic' (can also be a 'manual' seed)
54+ const [ndNetworkSeed, setNdNetworkSeed] = useState();
57+ available: keyfileAvailable,
60+ } = useKeyfileGenerator(ndNetworkSeed);
62+ const buildNetworkSeed = useCallback(
63+ async manualSeed => {
64+ if (manualSeed !== undefined) {
65+ setNdNetworkSeed(manualSeed);
68+ const newNetworkRevision = networkRevision + 1;
69+ console.log(`deriving seed with revision ${newNetworkRevision}`);
71+ const networkSeed = await attemptNetworkSeedDerivation({
76+ revision: newNetworkRevision,
79+ if (Just.hasInstance(networkSeed)) {
80+ return networkSeed.value;
83+ randomSeed.current = randomSeed.current || randomHex(32); // 32 bytes
84+ setNdNetworkSeed(randomSeed.current);
86+ return randomSeed.current;
89+ [_details, authMnemonic, networkRevision, urbitWallet, wallet]
92+ const { completed: _completed, ...rest } = useEthereumTransaction(
94+ async (manualSeed, isDiscontinuity) => {
95+ const seed = await buildNetworkSeed(manualSeed);
96+ const pair = deriveNetworkKeys(seed);
98+ return azimuth.ecliptic.configureKeys(
101+ addHexPrefix(pair.crypt.public),
102+ addHexPrefix(pair.auth.public),
103+ CRYPTO_SUITE_VERSION,
107+ [_contracts, _point, buildNetworkSeed]
110+ () => Promise.all([syncDetails(_point), syncRekeyDate(_point)]),
111+ [_point, syncDetails, syncRekeyDate]
113+ GAS_LIMITS.CONFIGURE_KEYS
116+ // only treat the transaction as completed once we also have keys to download
117+ const completed = _completed && keyfileAvailable;
128+export default function UrbitOSNetworkingKeys() {
129+ const { push, names, pop } = useLocalRouter();
130+ const { pointCursor } = usePointCursor();
131+ const { getDetails } = usePointCache();
133+ const point = need.point(pointCursor);
134+ const details = getDetails(point);
136+ const hasKeys = details.matchWith({
137+ Nothing: () => false,
138+ Just: ({ value: details }) => parseInt(details.keyRevisionNumber, 10) > 0,
139+ // we actually don't mind the default NaN behavior of parseInt here,
140+ // since NaN > 0 === false and that's a reasonable result
154+ const validateForm = useCallback((values, errors) => {
155+ if (values.useNetworkSeed && errors.networkSeed) {
160+ const validate = useMemo(
164+ useNetworkSeed: buildCheckboxValidator(),
165+ networkSeed: buildHexValidator(64), // 64 chars
166+ useDiscontinuity: buildCheckboxValidator(),
173+ const onValues = useCallback(
174+ ({ valid, values, form }) => {
177+ values.useNetworkSeed ? values.networkSeed : undefined,
178+ values.useDiscontinuity
184+ if (!values.useNetworkSeed && values.networkSeed) {
185+ form.change('networkSeed', '');
188+ [construct, unconstruct]
191+ const initialValues = useMemo(
193+ useNetworkSeed: false,
194+ useDiscontinuity: false,
199+ const goRelocate = useCallback(() => push(names.RELOCATE), [push, names]);
201+ const usageMessage = 'You need this to authenticate with arvo';
203+ const renderTitle = () => {
205+ return 'Networking keys are now set.';
209+ return 'Setting Network Keys...';
213+ return 'Networking keys have not yet been set.';
216+ return 'Networking';
222+ <Grid.Item full as={ViewHeader}>
230+ as={DownloadKeyfileButton}
234+ <Grid.Item full className="mb3 bg-red3 white t-center p3">
242+ initialValues={initialValues}>
250+ name="useNetworkSeed"
251+ label="Use Custom Network Seed"
252+ inverseLabel="Back to Derived Network Seed"
253+ disabled={inputsLocked}
255+ <Condition when="useNetworkSeed" is={true}>
256+ <Grid.Item full as={NoticeBox} className="mb2">
257+ When using a custom network seed, you'll need to download
258+ your Arvo keyfile immediately after this transaction is
259+ Bridge does not store your seed.
265+ label="Network Seed (32 bytes)"
266+ disabled={inputsLocked}
272+ name="useDiscontinuity"
273+ label="Breach Continuity"
274+ disabled={inputsLocked}
277+ <Grid.Item full as={FormError} />
283+ as={InlineEthereumTransaction}
285+ label={`${hasKeys ? 'Reset' : 'Set'} Networking Keys`}
286+ onReturn={() => pop()}