on master help sign in
Network keys success message
aa8e0b2Will Galebach
src/components/DownloadKeyfileButton.js+1−1
@@ -13,7 +13,7 @@ export default function DownloadKeyfileButton({
13
13
14 // from caller
14 // from caller
15 className,
15 className,
16− children = 'Download Arvo Keyfile',
16+ children = 'Download Keyfile',
17 ...rest
17 ...rest
18}) {
18}) {
19 return (
19 return (
src/components/L2/PendingTransaction.tsx−20
@@ -1,29 +1,9 @@
1import { Box, Row, Icon } from '@tlon/indigo-react';
1import { Box, Row, Icon } from '@tlon/indigo-react';
2−import { L1TxnType } from 'lib/types/PendingL1Transaction';
3import { useExploreTxUrl } from 'lib/explorer';
2import { useExploreTxUrl } from 'lib/explorer';
4import LayerIndicator from './LayerIndicator';
3import LayerIndicator from './LayerIndicator';
5
4
6import './PendingTransaction.scss';
5import './PendingTransaction.scss';
7
6
8−export const getPendingL1Title = (type?: L1TxnType) =>
9− type === L1TxnType.setNetworkKeys
10− ? 'Network Keys Configured'
11− : type === L1TxnType.managementProxy
12− ? 'Management Address Changed'
13− : type === L1TxnType.transferProxy
14− ? 'Transfer Proxy Changed'
15− : type === L1TxnType.spawnProxy
16− ? 'Spawn Proxy Changed'
17− : type === L1TxnType.spawnPoint
18− ? 'Spawn Point'
19− : type === L1TxnType.acceptTransfer
20− ? 'Accept Transfer'
21− : type === L1TxnType.migrate
22− ? 'Migrate to Layer 2'
23− : type === L1TxnType.migrateSpawn
24− ? 'Set Spawn Proxy to Layer 2'
25− : '';
26−
27interface Props {
7interface Props {
28 title: string;
8 title: string;
29 nextRoll?: string;
9 nextRoll?: string;
src/components/NetworkKeys.js+40−65
@@ -1,14 +1,11 @@
1import React from 'react';
1import React from 'react';
2import { Grid, Flex, H5 } from 'indigo-react';
2import { Grid, Flex, H5 } from 'indigo-react';
3−import { Just, Nothing } from 'folktale/maybe';
4
3
5import { formatDotsWithTime } from 'lib/dateFormat';
4import { formatDotsWithTime } from 'lib/dateFormat';
6−import { segmentNetworkKey, CURVE_ZERO_ADDR } from 'lib/keys';
5+import { segmentNetworkKey } from 'lib/keys';
7
6
8import { usePointCache } from 'store/pointCache';
7import { usePointCache } from 'store/pointCache';
9−
8+import { useRollerStore } from 'store/rollerStore';
10−const chainKeyProp = name => d =>
11− d[name] === CURVE_ZERO_ADDR ? Nothing() : Just(d[name]);
12
9
13const renderNetworkKey = key => {
10const renderNetworkKey = key => {
14 const segments = segmentNetworkKey(key);
11 const segments = segmentNetworkKey(key);
@@ -20,40 +17,38 @@ const renderNetworkKey = key => {
20 return segments.join('\n');
17 return segments.join('\n');
21};
18};
22
19
23−export default function NetworkKeys({ point }) {
20+export default function NetworkKeys() {
24− const { getDetails, getRekeyDate } = usePointCache();
21+ const { getRekeyDate } = usePointCache();
25−
22+ const { point } = useRollerStore();
26− const details = getDetails(point);
27
23
28− const hasKeys = details.matchWith({
24+ const {
29− Nothing: () => false,
25+ networkKeysSet,
30− Just: ({ value: details }) => parseInt(details.keyRevisionNumber, 10) > 0,
26+ keyRevisionNumber,
31− // we actually don't mind the default NaN behavior of parseInt here,
27+ continuityNumber,
32− // since NaN > 0 === false and that's a reasonable result
28+ cryptoSuiteVersion,
33− });
29+ authenticationKey,
30+ encryptionKey,
31+ } = point;
34
32
35 const renderNetworkKeySection = (title, key) => (
33 const renderNetworkKeySection = (title, key) => (
36 <>
34 <>
37 <Grid.Item full as={H5} className="mt3 gray4">
35 <Grid.Item full as={H5} className="mt3 gray4">
38 {title}
36 {title}
39 </Grid.Item>
37 </Grid.Item>
40− {key.matchWith({
38+ {key ? (
41− Nothing: () => (
39+ <>
42− <Grid.Item full as="code" className="f5 mono gray4">
40+ <Grid.Item full as="div" className="f5 mono">
43− Not set
41+ 0x
44 </Grid.Item>
42 </Grid.Item>
45− ),
46− Just: ({ value: key }) => (
47− <>
48− <Grid.Item full as="div" className="f5 mono">
49− 0x
50− </Grid.Item>
51− <Grid.Item full as="code" className="f5 mono wrap">
43+ <Grid.Item full as="code" className="f5 mono wrap">
52− {renderNetworkKey(key)}
44+ {renderNetworkKey(key)}
53− </Grid.Item>
45+ </Grid.Item>
54− </>
46+ </>
55− ),
47+ ) : (
48+ <Grid.Item full as="code" className="f5 mono gray4">
49+ Not set
50+ </Grid.Item>
56− })}
51+ )}
57 </>
52 </>
58 );
53 );
59
54
@@ -62,26 +57,18 @@ export default function NetworkKeys({ point }) {
62 <Flex.Item style={{ minWidth: 192, marginBottom: 8 }} as={H5}>
57 <Flex.Item style={{ minWidth: 192, marginBottom: 8 }} as={H5}>
63 {title}
58 {title}
64 </Flex.Item>
59 </Flex.Item>
65− {value.matchWith({
60+ {value === 'Not set' ? (
66− Nothing: () => (
67− <Flex.Item as={H5} className="f5 gray4">
61+ <Flex.Item as={H5} className="f5 gray4">
68− Not set
62+ Not set
69− </Flex.Item>
63+ </Flex.Item>
70− ),
64+ ) : (
71− Just: ({ value }) => <Flex.Item as={H5}>{value}</Flex.Item>,
65+ <Flex.Item as={H5}>{value}</Flex.Item>
72− })}
66+ )}
73 </Flex.Item>
67 </Flex.Item>
74 );
68 );
75
69
76− const revisionNumber = details
77− .map(d => d.keyRevisionNumber)
78− .matchWith({
79− Nothing: () => 0,
80− Just: ({ value }) => value,
81− });
82−
83− const revisionTime = hasKeys
70+ const revisionTime = networkKeysSet
84− ? ` at ${getRekeyDate(point)
71+ ? ` at ${getRekeyDate(point.value)
85 .map(date =>
72 .map(date =>
86 date.matchWith({
73 date.matchWith({
87 Ok: r => formatDotsWithTime(r.value),
74 Ok: r => formatDotsWithTime(r.value),
@@ -98,25 +85,13 @@ export default function NetworkKeys({ point }) {
98 <>
85 <>
99 <h5 className="fw-bold">Network Keys</h5>
86 <h5 className="fw-bold">Network Keys</h5>
100 <h5 className="gray4">
87 <h5 className="gray4">
101− Revision {revisionNumber} {revisionTime}
88+ Revision {keyRevisionNumber} {revisionTime}
102 </h5>
89 </h5>
103− {renderNetworkKeySection(
90+ {renderNetworkKeySection('Authentication Key', authenticationKey)}
104− 'Authentication Key',
105− details.chain(chainKeyProp('authenticationKey'))
106− )}
107− {renderNetworkKeySection(
91+ {renderNetworkKeySection('Encryption Key', encryptionKey)}
108− 'Encryption Key',
109− details.chain(chainKeyProp('encryptionKey'))
110− )}
111 <Grid.Item full as={Flex} col justify="between" className="mt3">
92 <Grid.Item full as={Flex} col justify="between" className="mt3">
112− {renderDetail(
113− 'Continuity Era',
93+ {renderDetail('Continuity Era', continuityNumber)}
114− details.map(d => d.continuityNumber)
115− )}
116− {renderDetail(
117− 'Crypto Suite Version',
94+ {renderDetail('Crypto Suite Version', cryptoSuiteVersion)}
118− details.map(d => d.cryptoSuiteVersion)
119− )}
120 </Grid.Item>
95 </Grid.Item>
121 </>
96 </>
122 );
97 );
src/lib/types/Point.ts+2−2
@@ -85,7 +85,7 @@ export default class Point {
85 escapeRequested: boolean;
85 escapeRequested: boolean;
86 escapeRequestedTo: string;
86 escapeRequestedTo: string;
87 hasSponsor: boolean;
87 hasSponsor: boolean;
88− keyRevisionNumber: string;
88+ keyRevisionNumber: number;
89 managementProxy: string;
89 managementProxy: string;
90 owner: string;
90 owner: string;
91 spawnProxy: string;
91 spawnProxy: string;
@@ -148,7 +148,7 @@ export default class Point {
148 this.escapeRequested = details.escapeRequested;
148 this.escapeRequested = details.escapeRequested;
149 this.escapeRequestedTo = details.escapeRequestedTo;
149 this.escapeRequestedTo = details.escapeRequestedTo;
150 this.hasSponsor = details.hasSponsor;
150 this.hasSponsor = details.hasSponsor;
151− this.keyRevisionNumber = details.keyRevisionNumber;
151+ this.keyRevisionNumber = Number(details.keyRevisionNumber);
152 this.managementProxy = details.managementProxy;
152 this.managementProxy = details.managementProxy;
153 this.owner = details.owner;
153 this.owner = details.owner;
154 this.spawnProxy = details.spawnProxy;
154 this.spawnProxy = details.spawnProxy;
src/lib/useKeyfileGenerator.ts+13−36
@@ -1,13 +1,10 @@
1import { useCallback, useMemo, useEffect, useState } from 'react';
1import { useCallback, useMemo, useEffect, useState } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
2import { Just, Nothing } from 'folktale/maybe';
3import saveAs from 'file-saver';
3import saveAs from 'file-saver';
4−import ob from 'urbit-ob';
5
4
6−import { usePointCache } from 'store/pointCache';
7import { useWallet } from 'store/wallet';
5import { useWallet } from 'store/wallet';
8−import { usePointCursor } from 'store/pointCursor';
6+import { useRollerStore } from 'store/rollerStore';
9
7
10−import * as need from 'lib/need';
11import { generateCode } from 'lib/networkCode';
8import { generateCode } from 'lib/networkCode';
12
9
13import {
10import {
@@ -16,8 +13,6 @@ import {
16 deriveNetworkKeys,
13 deriveNetworkKeys,
17 compileNetworkKey,
14 compileNetworkKey,
18} from './keys';
15} from './keys';
19−import useCurrentPermissions from './useCurrentPermissions';
20−import { convertToInt } from './convertToInt';
21import { stripSigPrefix } from 'form/formatters';
16import { stripSigPrefix } from 'form/formatters';
22
17
23// overrideable defaults:
18// overrideable defaults:
@@ -32,13 +27,9 @@ interface useKeyfileGeneratorArgs {
32 * @deprecated
27 * @deprecated
33 * Use `useMultikeyFileGenerator` instead
28 * Use `useMultikeyFileGenerator` instead
34 */
29 */
35−export default function useKeyfileGenerator({
30+export default function useKeyfileGenerator({ seed }: useKeyfileGeneratorArgs) {
36− seed,
37− point,
38−}: useKeyfileGeneratorArgs) {
39 const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
31 const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
40− const { pointCursor }: any = usePointCursor();
32+ const { point } = useRollerStore();
41− const { syncDetails, getDetails }: any = usePointCache();
42
33
43 const [notice, setNotice] = useState('Deriving networking keys...');
34 const [notice, setNotice] = useState('Deriving networking keys...');
44 const [downloaded, setDownloaded] = useState(false);
35 const [downloaded, setDownloaded] = useState(false);
@@ -46,22 +37,11 @@ export default function useKeyfileGenerator({
46 const [keyfile, setKeyfile] = useState<boolean | string>(false);
37 const [keyfile, setKeyfile] = useState<boolean | string>(false);
47 const [code, setCode] = useState(false);
38 const [code, setCode] = useState(false);
48
39
49− const _point = point || need.point(pointCursor);
40+ const networkRevision = Number(point.keyRevisionNumber);
50− useEffect(() => {
51− syncDetails(_point);
52− }, [_point, syncDetails]);
53−
54− const details = getDetails(_point);
55−
56− const networkRevision = details.matchWith({
57− Just: ({ value }) => convertToInt(value.keyRevisionNumber, 10),
58− Nothing: () => 0,
59− });
60− const { isOwner, isManagementProxy } = useCurrentPermissions();
41+ const canManage = point.isOwner || point.isManagementProxy;
61
42
62 const hasNetworkKeys = networkRevision > 0;
43 const hasNetworkKeys = networkRevision > 0;
63− const available =
44+ const available = canManage && hasNetworkKeys && !!keyfile;
64− (isOwner || isManagementProxy) && hasNetworkKeys && !!keyfile;
65
45
66 const generate = useCallback(async () => {
46 const generate = useCallback(async () => {
67 if (!hasNetworkKeys) {
47 if (!hasNetworkKeys) {
@@ -73,17 +53,15 @@ export default function useKeyfileGenerator({
73 return;
53 return;
74 }
54 }
75
55
76− const _details = need.details(details);
77−
78 const networkSeed = seed
56 const networkSeed = seed
79 ? Just(seed)
57 ? Just(seed)
80 : await attemptNetworkSeedDerivation({
58 : await attemptNetworkSeedDerivation({
81 urbitWallet,
59 urbitWallet,
82 wallet,
60 wallet,
83 authMnemonic,
61 authMnemonic,
84− details: _details,
62+ details: point,
85 authToken,
63 authToken,
86− point: _point,
64+ point: point.value,
87 revision: networkRevision,
65 revision: networkRevision,
88 });
66 });
89
67
@@ -100,7 +78,7 @@ export default function useKeyfileGenerator({
100
78
101 const pair = deriveNetworkKeys(_networkSeed);
79 const pair = deriveNetworkKeys(_networkSeed);
102
80
103− if (!keysMatchChain(pair, _details)) {
81+ if (!keysMatchChain(pair, point)) {
104 setGenerating(false);
82 setGenerating(false);
105 setNotice('Derived networking keys do not match on-chain details.');
83 setNotice('Derived networking keys do not match on-chain details.');
106 console.log(`keys do not match details for revision ${networkRevision}`);
84 console.log(`keys do not match details for revision ${networkRevision}`);
@@ -109,7 +87,7 @@ export default function useKeyfileGenerator({
109
87
110 setNotice('');
88 setNotice('');
111 setCode(generateCode(pair));
89 setCode(generateCode(pair));
112− setKeyfile(compileNetworkKey(pair, _point, networkRevision));
90+ setKeyfile(compileNetworkKey(pair, point.value, networkRevision));
113 setGenerating(false);
91 setGenerating(false);
114 }, [
92 }, [
115 networkRevision,
93 networkRevision,
@@ -119,14 +97,13 @@ export default function useKeyfileGenerator({
119 wallet,
97 wallet,
120 authMnemonic,
98 authMnemonic,
121 setCode,
99 setCode,
122− details,
100+ point,
123− _point,
124 authToken,
101 authToken,
125 ]);
102 ]);
126
103
127 const filename = useMemo(() => {
104 const filename = useMemo(() => {
128− return `${stripSigPrefix(ob.patp(_point))}-${networkRevision}.key`;
105+ return `${stripSigPrefix(point.patp)}-${networkRevision}.key`;
129− }, [_point, networkRevision]);
106+ }, [point, networkRevision]);
130
107
131 const download = useCallback(() => {
108 const download = useCallback(() => {
132 if (typeof keyfile !== 'string') {
109 if (typeof keyfile !== 'string') {
src/lib/useMultikeyFileGenerator.ts+11−11
@@ -18,6 +18,7 @@ import { generateCode } from './networkCode';
18import { L1Point } from './types/L1Point';
18import { L1Point } from './types/L1Point';
19import useCurrentPermissions from './useCurrentPermissions';
19import useCurrentPermissions from './useCurrentPermissions';
20import { stripSigPrefix } from 'form/formatters';
20import { stripSigPrefix } from 'form/formatters';
21+import { useRollerStore } from 'store/rollerStore';
21
22
22interface useMultikeyFileGeneratorArgs {
23interface useMultikeyFileGeneratorArgs {
23 seed?: string;
24 seed?: string;
@@ -39,6 +40,7 @@ export default function useMultikeyFileGenerator({
39 const { pointCursor }: any = usePointCursor();
40 const { pointCursor }: any = usePointCursor();
40 const { syncDetails, getDetails }: any = usePointCache();
41 const { syncDetails, getDetails }: any = usePointCache();
41 const { isOwner, isManagementProxy } = useCurrentPermissions();
42 const { isOwner, isManagementProxy } = useCurrentPermissions();
43+ const rollerStore = useRollerStore();
42
44
43 const [notice, setNotice] = useState('Deriving networking keys...');
45 const [notice, setNotice] = useState('Deriving networking keys...');
44 const [downloaded, setDownloaded] = useState(false);
46 const [downloaded, setDownloaded] = useState(false);
@@ -53,20 +55,18 @@ export default function useMultikeyFileGenerator({
53 const details = getDetails(_point);
55 const details = getDetails(_point);
54 const _details: L1Point | null = useMemo(
56 const _details: L1Point | null = useMemo(
55 () =>
57 () =>
58+ rollerStore?.point?.isL2
59+ ? rollerStore.point
56− details.matchWith({
60+ : details.matchWith({
57− Just: d => need.details(d),
61+ Just: (d: any) => need.details(d),
58− Nothing: () => null,
62+ Nothing: () => null,
59− }),
63+ }),
60− [details]
64+ [details, rollerStore.point]
61 );
65 );
62
66
63 const currentRevision: number = useMemo(
67 const currentRevision: number = useMemo(
64− () =>
65− details.matchWith({
66− Just: ({ value }) => convertToInt(value.keyRevisionNumber, 10),
68+ () => convertToInt(_details?.keyRevisionNumber || 0, 10),
67− Nothing: () => 0,
69+ [_details]
68− }),
69− [details]
70 );
70 );
71 const nextRevision = useMemo(() => currentRevision + 1, [currentRevision]);
71 const nextRevision = useMemo(() => currentRevision + 1, [currentRevision]);
72
72
src/lib/useRoller.ts+20−8
@@ -30,6 +30,7 @@ import {
30 getTimeToNextBatch,
30 getTimeToNextBatch,
31 registerProxyAddress,
31 registerProxyAddress,
32 isL2Spawn,
32 isL2Spawn,
33+ isL2,
33} from './utils/roller';
34} from './utils/roller';
34import { ETH_ZERO_ADDR, ROLLER_HOSTS, TEN_SECONDS } from './constants';
35import { ETH_ZERO_ADDR, ROLLER_HOSTS, TEN_SECONDS } from './constants';
35
36
@@ -60,6 +61,7 @@ interface UpdateParams {
60 notify?: boolean;
61 notify?: boolean;
61 field?: PointField;
62 field?: PointField;
62 l1Txn?: PendingL1Txn;
63 l1Txn?: PendingL1Txn;
64+ intervalTime?: number;
63}
65}
64
66
65const inviteTemplate = (
67const inviteTemplate = (
@@ -191,8 +193,9 @@ export default function useRoller() {
191 const initPoint = useCallback(
193 const initPoint = useCallback(
192 async (point: string | number) => {
194 async (point: string | number) => {
193 const _wallet = wallet.getOrElse(null);
195 const _wallet = wallet.getOrElse(null);
196+ const _contracts = contracts.getOrElse(null);
194
197
195− if (!_wallet) {
198+ if (!_wallet || !_contracts) {
196 return EMPTY_POINT;
199 return EMPTY_POINT;
197 }
200 }
198
201
@@ -202,7 +205,10 @@ export default function useRoller() {
202 const l2Quota = isL2Spawn(rawDetails?.dominion)
205 const l2Quota = isL2Spawn(rawDetails?.dominion)
203 ? await api.getRemainingQuota(pointNum)
206 ? await api.getRemainingQuota(pointNum)
204 : 0;
207 : 0;
208+
209+ const details = isL2(rawDetails?.dominion)
205− const details = toL1Details(rawDetails);
210+ ? toL1Details(rawDetails)
211+ : await azimuth.azimuth.getPoint(_contracts, point);
206
212
207 return new Point({
213 return new Point({
208 value: pointNum,
214 value: pointNum,
@@ -239,7 +245,14 @@ export default function useRoller() {
239 );
245 );
240
246
241 const checkForUpdates = useCallback(
247 const checkForUpdates = useCallback(
242− async ({ point, message, notify = true, field, l1Txn }: UpdateParams) => {
248+ async ({
249+ point,
250+ message,
251+ notify = true,
252+ field,
253+ l1Txn,
254+ intervalTime = TEN_SECONDS,
255+ }: UpdateParams) => {
243 if (l1Txn) storePendingL1Txn(l1Txn);
256 if (l1Txn) storePendingL1Txn(l1Txn);
244
257
245 const interval = setInterval(async () => {
258 const interval = setInterval(async () => {
@@ -262,7 +275,7 @@ export default function useRoller() {
262 );
275 );
263 }
276 }
264 }
277 }
265− }, TEN_SECONDS);
278+ }, intervalTime);
266 },
279 },
267 [points, initPoint, updatePoint, storePendingL1Txn, deletePendingL1Txn]
280 [points, initPoint, updatePoint, storePendingL1Txn, deletePendingL1Txn]
268 );
281 );
@@ -604,8 +617,7 @@ export default function useRoller() {
604 }
617 }
605 const _wallet = wallet.getOrElse(null);
618 const _wallet = wallet.getOrElse(null);
606 const _web3 = web3.getOrElse(null);
619 const _web3 = web3.getOrElse(null);
607− const _details = getDetails(point.value);
608− if (!_wallet || !_web3 || !_details || point.isDefault) {
620+ if (!_wallet || !_web3 || point.isDefault) {
609 // not using need because we want a custom error
621 // not using need because we want a custom error
610 throw new Error('Internal Error: Missing Wallet/Details');
622 throw new Error('Internal Error: Missing Wallet/Details');
611 }
623 }
@@ -627,9 +639,9 @@ export default function useRoller() {
627 urbitWallet,
639 urbitWallet,
628 wallet,
640 wallet,
629 authMnemonic,
641 authMnemonic,
630− details: _details,
642+ details: point,
631− point: point.value,
632 authToken,
643 authToken,
644+ point: point.value,
633 revision: nextRevision,
645 revision: nextRevision,
634 });
646 });
635 const txHash = await submitL2Transaction({
647 const txHash = await submitL2Transaction({
src/views/Point/Point.tsx+1−3
@@ -21,9 +21,7 @@ import useSeenMissingKeys from 'lib/useSeenMissingKeys';
21import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
21import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
22
22
23import LoadingOverlay from 'components/L2/LoadingOverlay';
23import LoadingOverlay from 'components/L2/LoadingOverlay';
24−import PendingTransaction, {
25− getPendingL1Title,
26−} from 'components/L2/PendingTransaction';
24+import PendingTransaction from 'components/L2/PendingTransaction';
27import Modal from 'components/L2/Modal';
25import Modal from 'components/L2/Modal';
28import './Point.scss';
26import './Point.scss';
29import { TransactionRow } from 'views/Transactions/TransactionRow';
27import { TransactionRow } from 'views/Transactions/TransactionRow';
src/views/UrbitOS/Home.tsx+2−2
@@ -30,7 +30,7 @@ export default function UrbitOSHome() {
30 const toggleShowKeys = useCallback(() => setShowKeys(s => !s), [setShowKeys]);
30 const toggleShowKeys = useCallback(() => setShowKeys(s => !s), [setShowKeys]);
31
31
32 // The initial key revision number is 0, and increments when set
32 // The initial key revision number is 0, and increments when set
33− const hasSetNetworkKeys = point.keyRevisionNumber !== '0';
33+ const hasSetNetworkKeys = Number(point.keyRevisionNumber) > 0;
34
34
35 const goNetworkKeys = useCallback(() => push(names.NETWORKING_KEYS), [
35 const goNetworkKeys = useCallback(() => push(names.NETWORKING_KEYS), [
36 names,
36 names,
@@ -118,7 +118,7 @@ export default function UrbitOSHome() {
118
118
119 <Modal show={showKeys} hide={() => setShowKeys(false)}>
119 <Modal show={showKeys} hide={() => setShowKeys(false)}>
120 <Box className="show-keys-modal-content">
120 <Box className="show-keys-modal-content">
121− <Grid.Item full as={NetworkKeys} point={point.value} />
121+ <Grid.Item full as={NetworkKeys} />
122 </Box>
122 </Box>
123 </Modal>
123 </Modal>
124 </Window>
124 </Window>
src/views/UrbitOS/NetworkKeys.scss+19
@@ -4,6 +4,25 @@
4 font-size: 14px;
4 font-size: 14px;
5
5
6 .body-pane {
6 .body-pane {
7+ height: 360px;
8+
9+ .download-message {
10+ align-items: center;
11+ width: 350px;
12+ //styleName: Sans/UI + Control;
13+ font-family: Inter;
14+ line-height: 16px;
15+ letter-spacing: 0em;
16+ text-align: center;
17+ margin: 90px auto 40px;
18+
19+ .download {
20+ height: 24px;
21+ width: 24px;
22+ margin-bottom: 22px;
23+ }
24+ }
25+
7 .contents {
26 .contents {
8 height: 448px;
27 height: 448px;
9 width: 100%;
28 width: 100%;
src/views/UrbitOS/NetworkKeys.tsx+132−113
@@ -9,9 +9,8 @@ import { Just } from 'folktale/maybe';
9import { Grid, Button } from 'indigo-react';
9import { Grid, Button } from 'indigo-react';
10import * as azimuth from 'azimuth-js';
10import * as azimuth from 'azimuth-js';
11import { randomHex } from 'web3-utils';
11import { randomHex } from 'web3-utils';
12−import { Box, Checkbox, Row } from '@tlon/indigo-react';
12+import { Box, Checkbox, Icon, Row } from '@tlon/indigo-react';
13
13
14−import { usePointCursor } from 'store/pointCursor';
15import { usePointCache } from 'store/pointCache';
14import { usePointCache } from 'store/pointCache';
16import { useNetwork } from 'store/network';
15import { useNetwork } from 'store/network';
17import { useWallet } from 'store/wallet';
16import { useWallet } from 'store/wallet';
@@ -26,14 +25,13 @@ import {
26 CRYPTO_SUITE_VERSION,
25 CRYPTO_SUITE_VERSION,
27} from 'lib/keys';
26} from 'lib/keys';
28import useEthereumTransaction from 'lib/useEthereumTransaction';
27import useEthereumTransaction from 'lib/useEthereumTransaction';
29−import { GAS_LIMITS } from 'lib/constants';
28+import { GAS_LIMITS, ONE_SECOND } from 'lib/constants';
30import { addHexPrefix } from 'lib/utils/address';
29import { addHexPrefix } from 'lib/utils/address';
31import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
30import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
32
31
33import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
32import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
34import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
33import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
35import NoticeBox from 'components/NoticeBox';
34import NoticeBox from 'components/NoticeBox';
36−import AlertBox from 'components/AlertBox';
37
35
38import { HexInput } from 'form/Inputs';
36import { HexInput } from 'form/Inputs';
39import {
37import {
@@ -43,7 +41,6 @@ import {
43} from 'form/validators';
41} from 'form/validators';
44import BridgeForm from 'form/BridgeForm';
42import BridgeForm from 'form/BridgeForm';
45import FormError from 'form/FormError';
43import FormError from 'form/FormError';
46−import { convertToInt } from 'lib/convertToInt';
47import Window from 'components/L2/Window/Window';
44import Window from 'components/L2/Window/Window';
48import HeaderPane from 'components/L2/Window/HeaderPane';
45import HeaderPane from 'components/L2/Window/HeaderPane';
49import BodyPane from 'components/L2/Window/BodyPane';
46import BodyPane from 'components/L2/Window/BodyPane';
@@ -56,19 +53,16 @@ function useSetKeys(
56 setManualNetworkSeed: (seed: string) => void
53 setManualNetworkSeed: (seed: string) => void
57) {
54) {
58 const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
55 const { urbitWallet, wallet, authMnemonic, authToken }: any = useWallet();
59− const { pointCursor }: any = usePointCursor();
56+ const { point } = useRollerStore();
60− const { syncDetails, syncRekeyDate, getDetails }: any = usePointCache();
57+ const { syncDetails, syncRekeyDate }: any = usePointCache();
61 const { contracts }: any = useNetwork();
58 const { contracts }: any = useNetwork();
62
59
63− const _point = need.point(pointCursor);
64 const _contracts = need.contracts(contracts);
60 const _contracts = need.contracts(contracts);
65− const _details = need.details(getDetails(_point));
66
61
67− const networkRevision = convertToInt(_details.keyRevisionNumber, 10);
62+ const networkRevision = Number(point.keyRevisionNumber);
68 const randomSeed = useRef<string | null>();
63 const randomSeed = useRef<string | null>();
69
64
70 const {
65 const {
71− available: keyfileAvailable,
72 generating: keyfileGenerating,
66 generating: keyfileGenerating,
73 filename,
67 filename,
74 bind: keyfileBind,
68 bind: keyfileBind,
@@ -87,8 +81,8 @@ function useSetKeys(
87 urbitWallet,
81 urbitWallet,
88 wallet,
82 wallet,
89 authMnemonic,
83 authMnemonic,
90− details: _details,
84+ details: point,
91− point: _point,
85+ point: point.value,
92 authToken,
86 authToken,
93 revision: newNetworkRevision,
87 revision: newNetworkRevision,
94 });
88 });
@@ -104,13 +98,12 @@ function useSetKeys(
104 }
98 }
105 },
99 },
106 [
100 [
107− _details,
108 authMnemonic,
101 authMnemonic,
109 setManualNetworkSeed,
102 setManualNetworkSeed,
110 networkRevision,
103 networkRevision,
111 urbitWallet,
104 urbitWallet,
112 wallet,
105 wallet,
113− _point,
106+ point,
114 authToken,
107 authToken,
115 ]
108 ]
116 );
109 );
@@ -123,24 +116,24 @@ function useSetKeys(
123
116
124 return azimuth.ecliptic.configureKeys(
117 return azimuth.ecliptic.configureKeys(
125 _contracts,
118 _contracts,
126− _point,
119+ point.value,
127 addHexPrefix(pair.crypt.public),
120 addHexPrefix(pair.crypt.public),
128 addHexPrefix(pair.auth.public),
121 addHexPrefix(pair.auth.public),
129 CRYPTO_SUITE_VERSION,
122 CRYPTO_SUITE_VERSION,
130 isDiscontinuity
123 isDiscontinuity
131 );
124 );
132 },
125 },
133− [_contracts, _point, buildNetworkSeed]
126+ [_contracts, point, buildNetworkSeed]
134 ),
127 ),
135 useCallback(
128 useCallback(
136− () => Promise.all([syncDetails(_point), syncRekeyDate(_point)]),
129+ () => Promise.all([syncDetails(point.value), syncRekeyDate(point.value)]),
137− [_point, syncDetails, syncRekeyDate]
130+ [point, syncDetails, syncRekeyDate]
138 ),
131 ),
139 GAS_LIMITS.CONFIGURE_KEYS
132 GAS_LIMITS.CONFIGURE_KEYS
140 );
133 );
141
134
142 // only treat the transaction as completed once we also have keys to download
135 // only treat the transaction as completed once we also have keys to download
143− const completed = _completed && keyfileAvailable && !keyfileGenerating;
136+ const completed = _completed && !keyfileGenerating;
144
137
145 return {
138 return {
146 completed,
139 completed,
@@ -166,6 +159,7 @@ export default function UrbitOSNetworkKeys({
166 } = useRoller();
159 } = useRoller();
167 const [breach, setBreach] = useState(false);
160 const [breach, setBreach] = useState(false);
168 const [useCustomSeed, setUseCustomSeed] = useState(false);
161 const [useCustomSeed, setUseCustomSeed] = useState(false);
162+ const [l2Completed, setL2Completed] = useState(false);
169
163
170 const hasKeys = point.networkKeysSet;
164 const hasKeys = point.networkKeysSet;
171
165
@@ -191,6 +185,7 @@ export default function UrbitOSNetworkKeys({
191 hash: txHashes[0],
185 hash: txHashes[0],
192 time: new Date().getTime(),
186 time: new Date().getTime(),
193 },
187 },
188+ intervalTime: ONE_SECOND,
194 });
189 });
195 }
190 }
196 }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
191 }, [completed]); // eslint-disable-line react-hooks/exhaustive-deps
@@ -259,8 +254,9 @@ export default function UrbitOSNetworkKeys({
259 checkForUpdates({
254 checkForUpdates({
260 point: point.value,
255 point: point.value,
261 message: `${point.patp}'s Network Keys have been set!`,
256 message: `${point.patp}'s Network Keys have been set!`,
257+ intervalTime: ONE_SECOND,
262 });
258 });
263− pop();
259+ setTimeout(() => setL2Completed(true), ONE_SECOND);
264 } catch (error) {
260 } catch (error) {
265 // setError(error);
261 // setError(error);
266 } finally {
262 } finally {
@@ -271,7 +267,6 @@ export default function UrbitOSNetworkKeys({
271 manualNetworkSeed,
267 manualNetworkSeed,
272 getPendingTransactions,
268 getPendingTransactions,
273 point,
269 point,
274− pop,
275 configureNetworkKeys,
270 configureNetworkKeys,
276 setLoading,
271 setLoading,
277 checkForUpdates,
272 checkForUpdates,
@@ -279,10 +274,18 @@ export default function UrbitOSNetworkKeys({
279
274
280 const initialValues = useMemo(() => ({}), []);
275 const initialValues = useMemo(() => ({}), []);
281
276
282− const viewTitle = completed ? 'Network Keys are now set' : 'Set Network Keys';
277+ const viewTitle = completed
278+ ? 'Network Keys have been set'
279+ : hasKeys
280+ ? 'Set Network Keys'
281+ : 'Initiate Network Keys';
283
282
284 const usageMessage =
283 const usageMessage =
285− 'You need this keyfile to authenticate with your OS. Please Download.';
284+ 'With your new network keys, you also have a new keyfile. Download your new keyfile to boot your OS.';
285+
286+ const buttonText = `${hasKeys ? 'Reset' : 'Set'} Network Keys`;
287+
288+ const isComplete = completed || l2Completed;
286
289
287 return (
290 return (
288 <Window className="network-keys">
291 <Window className="network-keys">
@@ -290,98 +293,114 @@ export default function UrbitOSNetworkKeys({
290 <h5>{viewTitle}</h5>
293 <h5>{viewTitle}</h5>
291 </HeaderPane>
294 </HeaderPane>
292 <BodyPane>
295 <BodyPane>
293− {completed && (
296+ {isComplete && (
294− <>
297+ <Box className="flex-col col justify-between h-full">
298+ <Box className="flex-col download-message">
299+ <Icon icon="Download" className="download" />
300+ <Box>{usageMessage}</Box>
301+ </Box>
302+ {point.isL2 ? (
295− <Grid.Item
303+ <Grid.Item
296− full
304+ full
297− className="mb3"
305+ as={Button}
306+ solid
307+ center
308+ onClick={keyfileBind.download}>
309+ Download Keyfile
310+ </Grid.Item>
311+ ) : (
312+ <Grid.Item
313+ full
298− as={DownloadKeyfileButton}
314+ as={DownloadKeyfileButton}
299− solid
315+ solid
300− left
316+ center
301− {...keyfileBind}
317+ {...keyfileBind}
302− />
318+ />
303− <Grid.Item full as={AlertBox} className="mb8">
319+ )}
304− {usageMessage}
320+ </Box>
305− </Grid.Item>
306− </>
307 )}
321 )}
322+
323+ {!isComplete && (
308− <BridgeForm
324+ <BridgeForm
309− validate={validate}
325+ validate={validate}
310− onValues={onValues}
326+ onValues={onValues}
311− initialValues={initialValues}>
327+ initialValues={initialValues}>
312− {() => (
328+ {() => (
313− <Box className="contents">
329+ <Box className="contents">
314− {!completed && (
330+ {!isComplete && (
315− <Box>
331+ <Box>
332+ <Row
333+ className="check-row"
316− <Row className="check-row" onClick={() => setBreach(!breach)}>
334+ onClick={() => setBreach(!breach)}>
317− <Checkbox
318− className="checkbox"
319− selected={breach}
320− disabled={inputsLocked}
321− />
322− Factory Reset
323− </Row>
324− <Box className="info-text">
325− Use if your ship is corrupted, you lost your files, or
326− <br />
327− you want to erase your data
328− </Box>
329− <Row
330− className="check-row"
331− onClick={() => setUseCustomSeed(!useCustomSeed)}>
332− <Checkbox
335+ <Checkbox
333− className="checkbox"
336+ className="checkbox"
334− selected={useCustomSeed}
337+ selected={breach}
335− disabled={inputsLocked}
336− />
337− Custom Network Seed
338− </Row>
339− <Box className="info-text">
340− Enter your own custom network seed to derive from
341− </Box>
342− {useCustomSeed && (
343− <>
344− <Grid.Item full as={NoticeBox} className="mb2">
345− When using a custom network seed, you'll need to
346− download your Arvo keyfile immediately after this
347− transaction as Bridge does not store your seed.
348− </Grid.Item>
349− <Grid.Item
350− full
351− as={HexInput}
352− name="networkSeed"
353− label="Network Seed (32 bytes)"
354 disabled={inputsLocked}
338 disabled={inputsLocked}
355− className="mb5"
356 />
339 />
340+ Factory Reset
341+ </Row>
342+ <Box className="info-text">
343+ Use if your ship is corrupted, you lost your files, or
344+ <br />
345+ you want to erase your data
346+ </Box>
347+ <Row
348+ className="check-row"
349+ onClick={() => setUseCustomSeed(!useCustomSeed)}>
350+ <Checkbox
351+ className="checkbox"
352+ selected={useCustomSeed}
353+ disabled={inputsLocked}
354+ />
355+ Custom Network Seed
356+ </Row>
357+ <Box className="info-text">
358+ Enter your own custom network seed to derive from
359+ </Box>
360+ {useCustomSeed && (
361+ <>
362+ <Grid.Item full as={NoticeBox} className="mb2">
363+ When using a custom network seed, you'll need to
364+ download your Arvo keyfile immediately after this
365+ transaction as Bridge does not store your seed.
366+ </Grid.Item>
367+ <Grid.Item
368+ full
369+ as={HexInput}
370+ name="networkSeed"
371+ label="Network Seed (32 bytes)"
372+ disabled={inputsLocked}
373+ className="mb5"
374+ />
357− </>
375+ </>
358− )}
376+ )}
359− <Grid.Item full as={FormError} />
377+ <Grid.Item full as={FormError} />
360− </Box>
378+ </Box>
361− )}
379+ )}
362−
380+
363− {point.isL2 ? (
381+ {point.isL2 ? (
364− <Grid.Item
382+ <Grid.Item
365− as={Button}
383+ as={Button}
366− full
384+ full
367− className=""
385+ className=""
368− center
386+ center
369− solid
387+ solid
370− onClick={setNetworkKeys}>
388+ onClick={setNetworkKeys}>
371− {'Reset Network Keys'}
389+ {buttonText}
372− </Grid.Item>
390+ </Grid.Item>
373− ) : (
391+ ) : (
374− <Grid.Item
392+ <Grid.Item
375− full
393+ full
376− as={InlineEthereumTransaction}
394+ as={InlineEthereumTransaction}
377− {...bind}
395+ {...bind}
378− label={`${hasKeys ? 'Reset' : 'Set'} Network Keys`}
396+ label={buttonText}
379− onReturn={() => pop()}
397+ onReturn={() => pop()}
380− />
398+ />
381− )}
399+ )}
382− </Box>
400+ </Box>
383− )}
401+ )}
384− </BridgeForm>
402+ </BridgeForm>
403+ )}
385 </BodyPane>
404 </BodyPane>
386 </Window>
405 </Window>
387 );
406 );