on master help sign in
WIP: worker
65d744fWill Galebach
package-lock.json+88
@@ -11521,6 +11521,52 @@
11521 "delayed-stream": "~1.0.0"
11521 "delayed-stream": "~1.0.0"
11522 }
11522 }
11523 },
11523 },
11524+ "comlink": {
11525+ "version": "4.3.1",
11526+ "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.3.1.tgz",
11527+ "integrity": "sha512-+YbhUdNrpBZggBAHWcgQMLPLH1KDF3wJpeqrCKieWQ8RL7atmgsgTQko1XEBK6PsecfopWNntopJ+ByYG1lRaA==",
11528+ "dev": true
11529+ },
11530+ "comlink-loader": {
11531+ "version": "2.0.0",
11532+ "resolved": "https://registry.npmjs.org/comlink-loader/-/comlink-loader-2.0.0.tgz",
11533+ "integrity": "sha512-106/k0JSssSCo2QO4ucbgbyWsb2zAEOSXFAVH2A+EO8kOz3n2BkvmkToxsoo+SWKUIKy8qubhAOLCS5mpFUvHw==",
11534+ "dev": true,
11535+ "requires": {
11536+ "comlink": "^4.2.0",
11537+ "loader-utils": "^1.1.0",
11538+ "slash": "^3.0.0",
11539+ "worker-loader": "^2.0.0"
11540+ },
11541+ "dependencies": {
11542+ "json5": {
11543+ "version": "1.0.1",
11544+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
11545+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
11546+ "dev": true,
11547+ "requires": {
11548+ "minimist": "^1.2.0"
11549+ }
11550+ },
11551+ "loader-utils": {
11552+ "version": "1.4.0",
11553+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
11554+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
11555+ "dev": true,
11556+ "requires": {
11557+ "big.js": "^5.2.2",
11558+ "emojis-list": "^3.0.0",
11559+ "json5": "^1.0.1"
11560+ }
11561+ },
11562+ "slash": {
11563+ "version": "3.0.0",
11564+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
11565+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
11566+ "dev": true
11567+ }
11568+ }
11569+ },
11524 "commander": {
11570 "commander": {
11525 "version": "2.20.3",
11571 "version": "2.20.3",
11526 "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
11572 "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -35992,6 +36038,48 @@
35992 "errno": "~0.1.7"
36038 "errno": "~0.1.7"
35993 }
36039 }
35994 },
36040 },
36041+ "worker-loader": {
36042+ "version": "2.0.0",
36043+ "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz",
36044+ "integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==",
36045+ "dev": true,
36046+ "requires": {
36047+ "loader-utils": "^1.0.0",
36048+ "schema-utils": "^0.4.0"
36049+ },
36050+ "dependencies": {
36051+ "json5": {
36052+ "version": "1.0.1",
36053+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
36054+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
36055+ "dev": true,
36056+ "requires": {
36057+ "minimist": "^1.2.0"
36058+ }
36059+ },
36060+ "loader-utils": {
36061+ "version": "1.4.0",
36062+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
36063+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
36064+ "dev": true,
36065+ "requires": {
36066+ "big.js": "^5.2.2",
36067+ "emojis-list": "^3.0.0",
36068+ "json5": "^1.0.1"
36069+ }
36070+ },
36071+ "schema-utils": {
36072+ "version": "0.4.7",
36073+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
36074+ "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
36075+ "dev": true,
36076+ "requires": {
36077+ "ajv": "^6.1.0",
36078+ "ajv-keywords": "^3.1.0"
36079+ }
36080+ }
36081+ }
36082+ },
35995 "worker-rpc": {
36083 "worker-rpc": {
35996 "version": "0.1.1",
36084 "version": "0.1.1",
35997 "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
36085 "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
package.json+1
@@ -92,6 +92,7 @@
92 "@types/keccak": "^3.0.1",
92 "@types/keccak": "^3.0.1",
93 "@types/lodash": "^4.14.170",
93 "@types/lodash": "^4.14.170",
94 "@types/react-transition-group": "^4.4.2",
94 "@types/react-transition-group": "^4.4.2",
95+ "comlink-loader": "^2.0.0",
95 "eslint-config-prettier": "^5.0.0",
96 "eslint-config-prettier": "^5.0.0",
96 "eslint-plugin-prettier": "^3.1.0",
97 "eslint-plugin-prettier": "^3.1.0",
97 "eslint-plugin-react-hooks": "^1.6.0",
98 "eslint-plugin-react-hooks": "^1.6.0",
src/components/Greeting.js+39−39
@@ -16,45 +16,45 @@ export default function ActivateDisclaimer({ point }) {
16 setWasGreeted(true);
16 setWasGreeted(true);
17 }, [setWasGreeted]);
17 }, [setWasGreeted]);
18
18
19+ if (wasGreeted) {
20+ return null;
21+ }
22+
19 return (
23 return (
20− !wasGreeted && (
21− <Grid gap={4} className="mb10">
24+ <Grid gap={4} className="mb10">
22− <Grid.Item full>
25+ <Grid.Item full>
23− <Text className={cn(TEXT_STYLE, 'block mb4')}>
26+ <Text className={cn(TEXT_STYLE, 'block mb4')}>
24− Welcome <code>{pointName}</code>,
27+ Welcome <code>{pointName}</code>,
25− </Text>
28+ </Text>
26−
29+
27− <Text className={cn(TEXT_STYLE, 'block mb2')}>
30+ <Text className={cn(TEXT_STYLE, 'block mb2')}>
28− As of this moment, you own a piece of Urbit. No one can take it from
31+ As of this moment, you own a piece of Urbit. No one can take it from
29− you, and you can keep it for the rest of your life.
32+ you, and you can keep it for the rest of your life.
30− </Text>
33+ </Text>
31− <Text className={cn(TEXT_STYLE, 'block mb2')}>
34+ <Text className={cn(TEXT_STYLE, 'block mb2')}>
32− Keep your Master Ticket safe. No one can recover it for you. But it
35+ Keep your Master Ticket safe. No one can recover it for you. But it
33− can get you back into Urbit at any time.
36+ can get you back into Urbit at any time.
34− </Text>
37+ </Text>
35− <Text className={cn(TEXT_STYLE, 'block mb2')}>
38+ <Text className={cn(TEXT_STYLE, 'block mb2')}>Right now you can:</Text>
36− Right now you can:
37− </Text>
38− </Grid.Item>
39+ </Grid.Item>
39−
40+
40− <Grid.Item
41+ <Grid.Item
41− full
42+ full
42− as={LinkButton}
43+ as={LinkButton}
43− href="https://urbit.org/docs/getting-started/">
44+ href="https://urbit.org/docs/getting-started/">
44− <Text className={cn(TEXT_STYLE, 'block mb2')}>
45+ <Text className={cn(TEXT_STYLE, 'block mb2')}>
45− Boot Arvo, the Urbit OS
46+ Boot Arvo, the Urbit OS
46− </Text>
47+ </Text>
47− </Grid.Item>
48+ </Grid.Item>
48−
49+
49− <Grid.Item full>
50+ <Grid.Item full>
50− <Text className={cn(TEXT_STYLE, 'block mb4')}>
51+ <Text className={cn(TEXT_STYLE, 'block mb4')}>
51− Welcome to Urbit. See you online.
52+ Welcome to Urbit. See you online.
52− </Text>
53+ </Text>
53− </Grid.Item>
54+ </Grid.Item>
54− <Grid.Item full as={LinkButton} onClick={dismiss}>
55+ <Grid.Item full as={LinkButton} onClick={dismiss}>
55− Close
56+ Close
56− </Grid.Item>
57+ </Grid.Item>
57− </Grid>
58+ </Grid>
58− )
59 );
59 );
60}
60}
src/components/InlineEthereumTransaction.js+3−3
@@ -121,7 +121,7 @@ export default function InlineEthereumTransaction({
121 } else if (showBroadcastButton) {
121 } else if (showBroadcastButton) {
122 return (
122 return (
123 <Grid.Item
123 <Grid.Item
124− className="eth-tx-button"
124+ className="ph4 eth-tx-button"
125 full
125 full
126 as={ForwardButton}
126 as={ForwardButton}
127 solid
127 solid
@@ -134,7 +134,7 @@ export default function InlineEthereumTransaction({
134 } else if (showLoadingButton) {
134 } else if (showLoadingButton) {
135 return (
135 return (
136 <Grid.Item
136 <Grid.Item
137− className="eth-tx-button"
137+ className="ph4 eth-tx-button"
138 full
138 full
139 as={ProgressButton}
139 as={ProgressButton}
140 success
140 success
@@ -146,7 +146,7 @@ export default function InlineEthereumTransaction({
146 } else {
146 } else {
147 return (
147 return (
148 <Grid.Item
148 <Grid.Item
149− className="eth-tx-button"
149+ className="ph4 eth-tx-button"
150 full
150 full
151 as={GenerateButton}
151 as={GenerateButton}
152 onClick={generateAndSign}
152 onClick={generateAndSign}
src/components/L2/Card.scss+18
@@ -7,6 +7,7 @@
7 line-height: 24px;
7 line-height: 24px;
8 border: 1px solid rgba(0, 0, 0, 0.2);
8 border: 1px solid rgba(0, 0, 0, 0.2);
9 padding: 16px 13px;
9 padding: 16px 13px;
10+ position: relative;
10
11
11 .card-title {
12 .card-title {
12 font-weight: bold;
13 font-weight: bold;
@@ -25,4 +26,21 @@
25 &:hover {
26 &:hover {
26 cursor: pointer;
27 cursor: pointer;
27 }
28 }
29+
30+ .card-warning {
31+ font-size: 12px;
32+ font-weight: bold;
33+ color: $warning-red;
34+ position: absolute;
35+ top: 4px;
36+ right: 8px;
37+
38+ svg {
39+ background-color: $warning-red;
40+ border-radius: 7px;
41+ width: 14px;
42+ height: 14px;
43+ margin: 4px 4px 0 0;
44+ }
45+ }
28}
46}
src/components/L2/Card.tsx+8
@@ -7,6 +7,7 @@ export interface CardProps {
7 icon?: typeof Icon;
7 icon?: typeof Icon;
8 title?: string;
8 title?: string;
9 subtitle?: string;
9 subtitle?: string;
10+ warning?: string;
10 onClick?: () => void;
11 onClick?: () => void;
11 disabled?: boolean;
12 disabled?: boolean;
12 children?: React.ReactNode;
13 children?: React.ReactNode;
@@ -16,6 +17,7 @@ export default function Card({
16 icon,
17 icon,
17 title,
18 title,
18 subtitle,
19 subtitle,
20+ warning,
19 onClick,
21 onClick,
20 children,
22 children,
21 disabled = false,
23 disabled = false,
@@ -30,6 +32,12 @@ export default function Card({
30 )}
32 )}
31 {!!subtitle && <Box className="card-subtitle">{subtitle}</Box>}
33 {!!subtitle && <Box className="card-subtitle">{subtitle}</Box>}
32 {children}
34 {children}
35+ {!!warning && (
36+ <Row className="card-warning">
37+ <Icon icon="ExclaimationMark" color="white" />
38+ <Box>{warning}</Box>
39+ </Row>
40+ )}
33 </Box>
41 </Box>
34 );
42 );
35}
43}
src/components/L2/Dropdowns/AccountsDropdown.tsx+25−41
@@ -1,6 +1,5 @@
1import { useCallback, useMemo, useState } from 'react';
1import { useCallback, useMemo, useState } from 'react';
2−import * as ob from 'urbit-ob';
3−import { Icon, Row, Box, Button } from '@tlon/indigo-react';
2+import { Icon, Row, Box } from '@tlon/indigo-react';
4import { Just } from 'folktale/maybe';
3import { Just } from 'folktale/maybe';
5
4
6import Sigil from 'components/Sigil';
5import Sigil from 'components/Sigil';
@@ -11,18 +10,14 @@ import { TooltipPosition } from 'components/WithTooltip';
11import { useWallet } from 'store/wallet';
10import { useWallet } from 'store/wallet';
12import { useHistory } from 'store/history';
11import { useHistory } from 'store/history';
13import { usePointCursor } from 'store/pointCursor';
12import { usePointCursor } from 'store/pointCursor';
14−import { usePointCache } from 'store/pointCache';
15import { clearInvitesStorage } from 'store/storage/roller';
13import { clearInvitesStorage } from 'store/storage/roller';
14+import { useRollerStore } from 'store/rollerStore';
16
15
17−import { PointLayer } from 'lib/types/PointLayer';
18import { abbreviateAddress } from 'lib/utils/address';
16import { abbreviateAddress } from 'lib/utils/address';
17+import useCurrentPermissions from 'lib/useCurrentPermissions';
19
18
20import Dropdown from './Dropdown';
19import Dropdown from './Dropdown';
21import './AccountsDropdown.scss';
20import './AccountsDropdown.scss';
22−import Modal from '../Modal';
23−import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
24−import { useRollerStore } from 'store/roller';
25−import useCurrentPermissions from 'lib/useCurrentPermissions';
26
21
27interface AccountsDropdownProps {
22interface AccountsDropdownProps {
28 showMigrate: boolean;
23 showMigrate: boolean;
@@ -33,22 +28,10 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
33 const walletInfo: any = useWallet();
28 const walletInfo: any = useWallet();
34 const { push, popTo, names, reset }: any = useHistory();
29 const { push, popTo, names, reset }: any = useHistory();
35 const { setPointCursor }: any = usePointCursor();
30 const { setPointCursor }: any = usePointCursor();
36− const { controlledPoints }: any = usePointCache();
37− const { currentL2 }: any = useRollerStore();
31+ const { pointList } = useRollerStore();
38−
39− const networkKeysSet = useHasNetworkKeysSet();
40− const showMigrateOption = useMemo(() => {
41− return showMigrate || (!currentL2 && networkKeysSet);
42− }, [currentL2, networkKeysSet, showMigrate]);
43−
44− const [showModal, setShowModal] = useState(false);
45
32
46 const canBitcoin = Just.hasInstance(walletInfo.urbitWallet);
33 const canBitcoin = Just.hasInstance(walletInfo.urbitWallet);
47
34
48− const points = (controlledPoints?.value?.value?.pointsWithLayers || []).sort(
49− (a: PointLayer, b: PointLayer) => a.point - b.point
50− );
51−
52 const currentAddress = walletInfo?.wallet?.value?.address || '';
35 const currentAddress = walletInfo?.wallet?.value?.address || '';
53 const displayAddress = abbreviateAddress(currentAddress);
36 const displayAddress = abbreviateAddress(currentAddress);
54
37
@@ -86,6 +69,11 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
86
69
87 const { canSpawn, isParent, isStar } = useCurrentPermissions();
70 const { canSpawn, isParent, isStar } = useCurrentPermissions();
88
71
72+ const hasL1Points = Boolean(pointList.find(({ layer }) => layer === 1));
73+ const showMigrateOption = useMemo(() => {
74+ return showMigrate || hasL1Points;
75+ }, [hasL1Points, showMigrate]);
76+
89 return (
77 return (
90 <Dropdown
78 <Dropdown
91 className="accounts-dropdown"
79 className="accounts-dropdown"
@@ -100,28 +88,24 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
100 </CopiableAddressWrap>
88 </CopiableAddressWrap>
101 <Box className="divider" />
89 <Box className="divider" />
102 <Box className="points">
90 <Box className="points">
103− {points.map(({ point, layer }: PointLayer) => {
91+ {pointList.map(({ value, layer, patp }) => (
104− const patp = ob.patp(point);
105−
106− return (
107− <Row
92+ <Row
108− className="entry point"
93+ className="entry point"
109− onClick={selectPoint(point)}
94+ onClick={selectPoint(value)}
110− key={`point-${point}`}>
95+ key={`point-${value}`}>
111− <Box>{patp}</Box>
96+ <Box>{patp}</Box>
112− <Row>
97+ <Row>
113− <LayerIndicator layer={layer} size="md" />
98+ <LayerIndicator layer={layer} size="md" />
114− <Box className="sigil">
99+ <Box className="sigil">
115− <Sigil patp={patp} size={1} colors={['#000000', '#FFFFFF']} />
100+ <Sigil patp={patp} size={1} colors={['#000000', '#FFFFFF']} />
116− </Box>
101+ </Box>
117− </Row>
118 </Row>
102 </Row>
119− );
103+ </Row>
120− })}
104+ ))}
121 </Box>
105 </Box>
122 <Box className="divider" />
106 <Box className="divider" />
123 {showMigrateOption && (
107 {showMigrateOption && (
124− <Row className="entry" onClick={() => setShowModal(true)}>
108+ <Row className="entry" onClick={goMigrate}>
125 <Box>Migrate</Box>
109 <Box>Migrate</Box>
126 <Row className="layer-migration">
110 <Row className="layer-migration">
127 <LayerIndicator layer={1} size="sm" />
111 <LayerIndicator layer={1} size="sm" />
@@ -146,7 +130,7 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
146 <Box>Logout</Box>
130 <Box>Logout</Box>
147 <Icon icon="LogOut" />
131 <Icon icon="LogOut" />
148 </Row>
132 </Row>
149− <Modal show={showModal} hide={() => setShowModal(false)}>
133+ {/* <Modal show={showModal} hide={() => setShowModal(false)}>
150 <Box className="migrate-modal">
134 <Box className="migrate-modal">
151 <Box className="close" onClick={() => setShowModal(false)}>
135 <Box className="close" onClick={() => setShowModal(false)}>
152 &#215;
136 &#215;
@@ -166,7 +150,7 @@ const AccountsDropdown = ({ showMigrate = false }: AccountsDropdownProps) => {
166 </Button>
150 </Button>
167 </Row>
151 </Row>
168 </Box>
152 </Box>
169− </Modal>
153+ </Modal> */}
170 </Dropdown>
154 </Dropdown>
171 );
155 );
172};
156};
src/components/L2/Headers/L2BackButton.scss → src/components/L2/Headers/HeaderButton.scssrenamed

The content is the same.

src/components/L2/Headers/HeaderButton.tsxnew file+23
@@ -0,0 +1,23 @@
1+import React from 'react';
2+import { Icon, IconIndex } from '@tlon/indigo-react';
3+import './HeaderButton.scss';
4+
5+interface HeaderButtonProps {
6+ icon: keyof IconIndex;
7+ onClick?: () => void;
8+ className?: string;
9+}
10+
11+export default function HeaderButton({
12+ icon,
13+ onClick,
14+ className = '',
15+}: HeaderButtonProps) {
16+ return (
17+ <Icon
18+ className={`back-button ${className}`}
19+ icon={icon}
20+ onClick={onClick}
21+ />
22+ );
23+}
src/components/L2/Headers/L2BackButton.tsxdeleted−21
@@ -1,21 +0,0 @@
1−import React from 'react';
2−import { Icon } from '@tlon/indigo-react';
3−import './L2BackButton.scss';
4−
5−interface L2BackButtonProps {
6− onBack?: () => void;
7− className?: string;
8−}
9−
10−export default function L2BackButton({
11− onBack,
12− className = '',
13−}: L2BackButtonProps) {
14− return (
15− <Icon
16− className={`back-button ${className}`}
17− icon="ChevronWest"
18− onClick={onBack}
19− />
20− );
21−}
src/components/L2/Headers/L2BackHeader.tsx+13−18
@@ -1,18 +1,17 @@
1import React, { useCallback, useEffect, useState } from 'react';
1import React, { useCallback, useEffect, useState } from 'react';
2import { Row, Icon, Box } from '@tlon/indigo-react';
2import { Row, Icon, Box } from '@tlon/indigo-react';
3+import { toBN } from 'web3-utils';
4+import BN from 'bn.js';
3
5
4−import useRoller from 'lib/useRoller';
5import { useHistory } from 'store/history';
6import { useHistory } from 'store/history';
6−import { useRollerStore } from 'store/roller';
7+import { useRollerStore } from 'store/rollerStore';
7−
8−import './L2BackHeader.scss';
9import { useNetwork } from 'store/network';
8import { useNetwork } from 'store/network';
10import { useWallet } from 'store/wallet';
9import { useWallet } from 'store/wallet';
11−import { toBN } from 'web3-utils';
10+import { useTimerStore } from 'store/timerStore';
12−import BN from 'bn.js';
11+
13−import { isDevelopment } from 'lib/flags';
14import { ReactComponent as Wallet } from 'assets/wallet.svg';
12import { ReactComponent as Wallet } from 'assets/wallet.svg';
15−import L2BackButton from './L2BackButton';
13+import HeaderButton from './HeaderButton';
14+import './L2BackHeader.scss';
16
15
17export interface L2BackHeaderProps {
16export interface L2BackHeaderProps {
18 back?: () => void;
17 back?: () => void;
@@ -25,12 +24,14 @@ const L2BackHeader = ({
25 className = '',
24 className = '',
26 hideBalance = false,
25 hideBalance = false,
27}: L2BackHeaderProps) => {
26}: L2BackHeaderProps) => {
28− const { config } = useRoller();
27+ const { point } = useRollerStore(store => store);
29− const { nextRoll, currentL2 } = useRollerStore(store => store);
28+ const { nextRoll } = useTimerStore();
30 const { pop }: any = useHistory();
29 const { pop }: any = useHistory();
31 const { wallet }: any = useWallet();
30 const { wallet }: any = useWallet();
32 const { web3 }: any = useNetwork();
31 const { web3 }: any = useNetwork();
33
32
33+ const currentL2 = !!point?.isL2;
34+
34 const [ethBalance, setEthBalance] = useState<BN>(toBN(0));
35 const [ethBalance, setEthBalance] = useState<BN>(toBN(0));
35
36
36 const goBack = useCallback(() => {
37 const goBack = useCallback(() => {
@@ -41,12 +42,6 @@ const L2BackHeader = ({
41 }
42 }
42 }, [back, pop]);
43 }, [back, pop]);
43
44
44− useEffect(() => {
45− if (isDevelopment) {
46− console.log('loaded config in L2BackHeader:', config);
47− }
48− }, [config]);
49−
50 useEffect(() => {
45 useEffect(() => {
51 const getEthBalance = async () => {
46 const getEthBalance = async () => {
52 const _web3 = web3.getOrElse(null);
47 const _web3 = web3.getOrElse(null);
@@ -60,7 +55,7 @@ const L2BackHeader = ({
60 }
55 }
61 };
56 };
62
57
63− if (!currentL2) {
58+ if (currentL2) {
64 getEthBalance();
59 getEthBalance();
65 }
60 }
66 }, [currentL2, setEthBalance, wallet, web3]);
61 }, [currentL2, setEthBalance, wallet, web3]);
@@ -69,7 +64,7 @@ const L2BackHeader = ({
69
64
70 return (
65 return (
71 <Row className={`l2-back-header ${className}`}>
66 <Row className={`l2-back-header ${className}`}>
72− <L2BackButton onBack={goBack} />
67+ <HeaderButton icon="ChevronWest" onClick={goBack} />
73 {currentL2 && !hideBalance ? (
68 {currentL2 && !hideBalance ? (
74 <Row className="rollup-timer">
69 <Row className="rollup-timer">
75 <Icon icon="Clock" />
70 <Icon icon="Clock" />
src/components/L2/Headers/L2PointHeader.scss+4
@@ -8,6 +8,10 @@
8 font-size: 14px;
8 font-size: 14px;
9 margin: 40px 0px 16px;
9 margin: 40px 0px 16px;
10
10
11+ .home {
12+ margin-right: 8px;
13+ }
14+
11 .dropdown {
15 .dropdown {
12 border-radius: $medium-border-radius;
16 border-radius: $medium-border-radius;
13 background-color: $washed-gray;
17 background-color: $washed-gray;
src/components/L2/Headers/L2PointHeader.tsx+27−21
@@ -1,42 +1,39 @@
1−import React, { useEffect, useCallback } from 'react';
1+import React, { useCallback } from 'react';
2import { Box, Row } from '@tlon/indigo-react';
2import { Box, Row } from '@tlon/indigo-react';
3
3
4−import useRoller from 'lib/useRoller';
4+import { isStar } from 'lib/utils/point';
5+
5import { useHistory } from 'store/history';
6import { useHistory } from 'store/history';
6−import { ReactComponent as InviteIcon } from 'assets/invite.svg';
7+import { useRollerStore } from 'store/rollerStore';
8+import { useTimerStore } from 'store/timerStore';
9+import { usePointCursor } from 'store/pointCursor';
10+
7import { ReactComponent as HistoryIcon } from 'assets/history.svg';
11import { ReactComponent as HistoryIcon } from 'assets/history.svg';
12+import { ReactComponent as InviteIcon } from 'assets/invite.svg';
13+import HeaderButton from './HeaderButton';
8import AccountsDropdown from '../Dropdowns/AccountsDropdown';
14import AccountsDropdown from '../Dropdowns/AccountsDropdown';
9−import { useRollerStore } from 'store/roller';
10−
11import './L2PointHeader.scss';
15import './L2PointHeader.scss';
12−import { isDevelopment } from 'lib/flags';
13−import { usePointCursor } from 'store/pointCursor';
14−import { isStar } from 'lib/utils/point';
15
16
16export interface L2PointHeaderProps {
17export interface L2PointHeaderProps {
17 numInvites: number;
18 numInvites: number;
19+ hideHome?: boolean;
18 hideTimer?: boolean;
20 hideTimer?: boolean;
19 hideInvites?: boolean;
21 hideInvites?: boolean;
20 showMigrate?: boolean;
22 showMigrate?: boolean;
21}
23}
22
24
23const L2PointHeader = ({
25const L2PointHeader = ({
26+ numInvites = 0,
27+ hideHome = false,
24 hideTimer = false,
28 hideTimer = false,
25 hideInvites = false,
29 hideInvites = false,
26 showMigrate = false,
30 showMigrate = false,
27− numInvites = 0,
28}: L2PointHeaderProps) => {
31}: L2PointHeaderProps) => {
29− const { config } = useRoller();
32+ const { point, pendingTransactions } = useRollerStore();
30− const { nextRoll, currentL2, pendingTransactions } = useRollerStore();
33+ const { nextRoll } = useTimerStore();
31 const { pointCursor }: any = usePointCursor();
34 const { pointCursor }: any = usePointCursor();
32
35
33− useEffect(() => {
34− if (isDevelopment) {
35− console.log('loaded config in L2PointHeader:', config);
36− }
37− }, [config]);
38−
39− const { push, names }: any = useHistory();
36+ const { popTo, push, names }: any = useHistory();
40
37
41 const goToInvites = useCallback(() => push(names.INVITE_COHORT), [
38 const goToInvites = useCallback(() => push(names.INVITE_COHORT), [
42 push,
39 push,
@@ -52,15 +49,24 @@ const L2PointHeader = ({
52
49
53 return (
50 return (
54 <Row className="l2-point-header">
51 <Row className="l2-point-header">
52+ <Row>
53+ {!hideHome && (
54+ <HeaderButton
55+ className="home"
56+ icon="Home"
57+ onClick={() => popTo(names.POINTS)}
58+ />
59+ )}
55− <AccountsDropdown showMigrate={showMigrate} />
60+ <AccountsDropdown showMigrate={showMigrate} />
61+ </Row>
56 <Row className="info">
62 <Row className="info">
57− {showInvites && (
63+ {showInvites && point.canSpawn && (
58 <Row onClick={goToInvites} className="invites">
64 <Row onClick={goToInvites} className="invites">
59 <InviteIcon />
65 <InviteIcon />
60 {numInvites}
66 {numInvites}
61 </Row>
67 </Row>
62 )}
68 )}
63− {!hideTimer && currentL2 && !pendingTransactions.length && (
69+ {!hideTimer && point?.isL2Spawn && !pendingTransactions.length && (
64 <Box className="rollup-timer" onClick={goToHistory}>
70 <Box className="rollup-timer" onClick={goToHistory}>
65 {nextRoll}
71 {nextRoll}
66 </Box>
72 </Box>
src/lib/types/L1Point.ts+2−1
@@ -1,10 +1,11 @@
1// Response returned by L1's azimuth#getPoint
1// Response returned by L1's azimuth#getPoint
2export type L1Point = {
2export type L1Point = {
3 active: boolean;
3 active: boolean;
4+ layer: 1 | 2;
5+ isL2Spawn?: boolean;
4 authenticationKey: string;
6 authenticationKey: string;
5 continuityNumber: string;
7 continuityNumber: string;
6 cryptoSuiteVersion: string;
8 cryptoSuiteVersion: string;
7− dominion?: string;
8 encryptionKey: string;
9 encryptionKey: string;
9 escapeRequested: boolean;
10 escapeRequested: boolean;
10 escapeRequestedTo: string;
11 escapeRequestedTo: string;
src/lib/types/L2Transaction.tsnew file+41
@@ -0,0 +1,41 @@
1+import RollerRPCAPI, { EthAddress, Proxy, Ship } from '@urbit/roller-api';
2+
3+export type TransactionType =
4+ | 'transferPoint'
5+ | 'spawn'
6+ | 'configureKeys'
7+ | 'escape';
8+
9+export interface SendL2Params {
10+ address?: string; // Destination address
11+ breach?: boolean;
12+ networkSeed?: any;
13+ newSponsor?: Ship;
14+ nonce: number;
15+ pointToSpawn?: number;
16+ proxy: Proxy;
17+ proxyAddressType?: string;
18+ reset?: boolean;
19+ ship: Ship;
20+ type: TransactionType;
21+}
22+
23+export interface L2TransactionArgs extends SendL2Params {
24+ api: RollerRPCAPI;
25+ wallet: any;
26+ walletType: symbol;
27+ web3: any;
28+}
29+
30+export interface TransactionData {
31+ data: any;
32+ method: Function;
33+}
34+
35+export interface ReticketParams {
36+ point: Ship;
37+ to: EthAddress;
38+ manager: EthAddress;
39+ fromWallet: any; // TODO: wallet type
40+ toWallet: any; // TODO: wallet type
41+}
src/lib/types/Point.tsnew file+125
@@ -0,0 +1,125 @@
1+import * as ob from 'urbit-ob';
2+import { isGalaxy, isPlanet, isStar } from 'lib/utils/point';
3+import { eqAddr, isZeroAddress } from 'lib/utils/address';
4+import { L1Point } from './L1Point';
5+
6+export interface Points {
7+ [key: number]: Point;
8+}
9+
10+export enum Relationship {
11+ own,
12+ transfer,
13+ manage,
14+ vote,
15+ spawn,
16+}
17+
18+export default class Point {
19+ value: number;
20+ patp: string;
21+ layer: 1 | 2;
22+ active: boolean;
23+ authenticationKey: string;
24+ continuityNumber: string;
25+ cryptoSuiteVersion: string;
26+ encryptionKey: string;
27+ escapeRequested: boolean;
28+ escapeRequestedTo: string;
29+ hasSponsor: boolean;
30+ keyRevisionNumber: string;
31+ managementProxy: string;
32+ owner: string;
33+ spawnProxy: string;
34+ sponsor: string;
35+ transferProxy: string;
36+ votingProxy: string;
37+ relationship: Relationship;
38+ // Derived details
39+ isL2: boolean;
40+ isL2Spawn: boolean;
41+ isL1: boolean;
42+ isGalaxy: boolean;
43+ isStar: boolean;
44+ isPlanet: boolean;
45+ isParent: boolean;
46+ canMigrate: boolean;
47+ networkKeysSet: boolean;
48+ // usePermissionsForPoint details
49+ isOwner: boolean;
50+ isActiveOwner: boolean;
51+ isManagementProxy: boolean;
52+ isSpawnProxy: boolean;
53+ isVotingProxy: boolean;
54+ isTransferProxy: boolean;
55+ isManagementProxySet: boolean;
56+ isSpawnProxySet: boolean;
57+ isVotingProxySet: boolean;
58+ isTransferProxySet: boolean;
59+ canManage: boolean;
60+ canSpawn: boolean;
61+ canTransfer: boolean;
62+ canVote: boolean;
63+
64+ constructor(
65+ value: number,
66+ relationship: Relationship,
67+ details: L1Point,
68+ address: string
69+ ) {
70+ this.value = value;
71+ this.patp = value < 0 ? 'null' : ob.patp(value);
72+ this.relationship = relationship;
73+
74+ this.layer = details.layer;
75+ this.isL2Spawn = Boolean(details.isL2Spawn);
76+ this.active = details.active;
77+ this.authenticationKey = details.authenticationKey;
78+ this.continuityNumber = details.continuityNumber;
79+ this.cryptoSuiteVersion = details.cryptoSuiteVersion;
80+ this.encryptionKey = details.encryptionKey;
81+ this.escapeRequested = details.escapeRequested;
82+ this.escapeRequestedTo = details.escapeRequestedTo;
83+ this.hasSponsor = details.hasSponsor;
84+ this.keyRevisionNumber = details.keyRevisionNumber;
85+ this.managementProxy = details.managementProxy;
86+ this.owner = details.owner;
87+ this.spawnProxy = details.spawnProxy;
88+ this.sponsor = details.sponsor;
89+ this.transferProxy = details.transferProxy;
90+ this.votingProxy = details.votingProxy;
91+ // Derived details here
92+ this.isL2 = this.layer === 2;
93+ this.isL1 = this.layer === 1;
94+ this.isGalaxy = isGalaxy(value);
95+ this.isStar = isStar(value);
96+ this.isPlanet = isPlanet(value);
97+ this.isParent = !this.isPlanet;
98+ this.networkKeysSet = Number(this.keyRevisionNumber) > 0;
99+ // usePermissionsForPoint
100+ this.isOwner = eqAddr(address, this.owner);
101+ this.isActiveOwner = this.isOwner && this.active;
102+ this.isManagementProxy = eqAddr(address, this.managementProxy);
103+ this.isSpawnProxy = eqAddr(address, this.spawnProxy);
104+ this.isVotingProxy = eqAddr(address, this.votingProxy);
105+ this.isTransferProxy = eqAddr(address, this.transferProxy);
106+ this.isManagementProxySet = !isZeroAddress(this.managementProxy);
107+ this.isSpawnProxySet = !isZeroAddress(this.spawnProxy);
108+ this.isVotingProxySet = !isZeroAddress(this.votingProxy);
109+ this.isTransferProxySet = !isZeroAddress(this.transferProxy);
110+ this.canManage = this.isOwner || this.isManagementProxy;
111+ this.canTransfer = this.isOwner || this.isTransferProxy;
112+ this.canSpawn =
113+ this.isParent &&
114+ (this.isOwner || this.isSpawnProxy) &&
115+ this.networkKeysSet;
116+ this.canVote =
117+ this.isGalaxy && this.active && (this.isOwner || this.isVotingProxy);
118+
119+ this.canMigrate =
120+ this.canManage &&
121+ this.networkKeysSet &&
122+ this.isL1 &&
123+ ((this.isGalaxy && !this.isL2Spawn) || this.isStar || this.isPlanet);
124+ }
125+}
src/lib/useRoller.ts+200−96
@@ -4,36 +4,39 @@ import * as need from 'lib/need';
4import SecureLS from 'secure-ls';
4import SecureLS from 'secure-ls';
5
5
6import { Invite } from 'lib/types/Invite';
6import { Invite } from 'lib/types/Invite';
7−import { convertToInt } from './convertToInt';
8−import { isDevelopment, isRopsten } from './flags';
9import {
7import {
10− adopt,
8+ deriveNetworkSeedFromUrbitWallet,
11− detach,
9+ attemptNetworkSeedDerivation,
12− generateInviteWallet,
10+} from 'lib/keys';
13− getPendingSpawns,
14− reject,
15−} from './utils/roller';
16−import { ROLLER_HOSTS } from './constants';
17
11
18import { useNetwork } from 'store/network';
12import { useNetwork } from 'store/network';
19import { usePointCursor } from 'store/pointCursor';
13import { usePointCursor } from 'store/pointCursor';
20−import { useRollerStore } from 'store/roller';
14+import { EMPTY_POINT, useRollerStore } from 'store/rollerStore';
21import { useWallet } from 'store/wallet';
15import { useWallet } from 'store/wallet';
22import { getStoredInvites, setStoredInvites } from 'store/storage/roller';
16import { getStoredInvites, setStoredInvites } from 'store/storage/roller';
23import { usePointCache } from 'store/pointCache';
17import { usePointCache } from 'store/pointCache';
24import { getOutgoingPoints } from 'views/Points';
18import { getOutgoingPoints } from 'views/Points';
19+
25import {
20import {
26 getSpawnProxy,
21 getSpawnProxy,
27 getManagerProxy,
22 getManagerProxy,
28 getAddressProxy,
23 getAddressProxy,
29 getTransferProxy,
24 getTransferProxy,
30} from './utils/proxy';
25} from './utils/proxy';
31−import { isPlanet } from './utils/point';
26+import { isPlanet, toL1Details } from './utils/point';
32−
27+import { convertToInt } from './convertToInt';
28+import { isDevelopment, isRopsten } from './flags';
33import {
29import {
34− deriveNetworkSeedFromUrbitWallet,
30+ generateInviteWallet,
35− attemptNetworkSeedDerivation,
31+ getPendingSpawns,
36−} from 'lib/keys';
32+ submitL2Transaction,
33+ getTimeToNextBatch,
34+ registerProxyAddress,
35+ adopt,
36+ detach,
37+ reject,
38+} from './utils/roller';
39+import { ROLLER_HOSTS } from './constants';
37
40
38import {
41import {
39 Config,
42 Config,
@@ -45,13 +48,9 @@ import {
45 UnspawnedPoints,
48 UnspawnedPoints,
46} from '@urbit/roller-api';
49} from '@urbit/roller-api';
47
50
48−import {
51+import Point, { Points, Relationship } from './types/Point';
49− configureKeys,
52+import { useTimerStore } from 'store/timerStore';
50− getTimeToNextBatch,
53+import { ReticketParams, SendL2Params } from './types/L2Transaction';
51− spawn,
52− transferPointRequest,
53− registerProxyAddress,
54−} from './utils/roller';
55
54
56const ONE_SECOND = 1000;
55const ONE_SECOND = 1000;
57
56
@@ -81,17 +80,19 @@ export default function useRoller() {
81 const controlledPoints = allPoints?.controlledPoints;
80 const controlledPoints = allPoints?.controlledPoints;
82 const getDetails = allPoints?.getDetails;
81 const getDetails = allPoints?.getDetails;
83
82
83+ const { setNextRoll } = useTimerStore();
84+
84 const {
85 const {
85 nextBatchTime,
86 nextBatchTime,
86− currentL2,
87+ point,
87 invitePoints,
88 invitePoints,
88 invites,
89 invites,
89 setNextBatchTime,
90 setNextBatchTime,
90− setNextRoll,
91 setPendingTransactions,
91 setPendingTransactions,
92 setInvites,
92 setInvites,
93 setInvitePoints,
93 setInvitePoints,
94 setInviteGeneratingNum,
94 setInviteGeneratingNum,
95+ setPoints,
95 } = useRollerStore();
96 } = useRollerStore();
96 const [config, setConfig] = useState<Config | null>(null);
97 const [config, setConfig] = useState<Config | null>(null);
97
98
@@ -158,6 +159,7 @@ export default function useRoller() {
158 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
159 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
159 }
160 }
160
161
162+ // TODO: remove when UI disables management proxy from spawning invites
161 // if (Just.hasInstance(authMnemonic)) {
163 // if (Just.hasInstance(authMnemonic)) {
162 // throw new Error(
164 // throw new Error(
163 // "Auth key Error: A management mnemonic can't create invite codes"
165 // "Auth key Error: A management mnemonic can't create invite codes"
@@ -173,7 +175,6 @@ export default function useRoller() {
173
175
174 const starInfo = await api.getPoint(_point);
176 const starInfo = await api.getPoint(_point);
175 const invites: Invite[] = [];
177 const invites: Invite[] = [];
176− const requests: Promise<string>[] = [];
177
178
178 const proxy = getSpawnProxy(starInfo.ownership!, _wallet.address);
179 const proxy = getSpawnProxy(starInfo.ownership!, _wallet.address);
179
180
@@ -195,33 +196,36 @@ export default function useRoller() {
195 _authToken
196 _authToken
196 );
197 );
197
198
198− const spawnRequest = spawn(
199+ await submitL2Transaction({
199 api,
200 api,
200− _wallet,
201+ address: _wallet.address,
202+ wallet: _wallet,
201− _point,
203+ ship: _point,
202 proxy,
204 proxy,
203− nonceInc,
205+ nonce: nonceInc,
204− planet,
206+ pointToSpawn: planet,
207+ type: 'spawn',
205 walletType,
208 walletType,
206− _web3
209+ web3: _web3,
207− );
210+ });
208
211
209 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
212 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
210 inviteWallet,
213 inviteWallet,
211 1
214 1
212 );
215 );
213− const configureKeysRequest = configureKeys(
216+ await submitL2Transaction({
214 api,
217 api,
215− _wallet,
218+ wallet: _wallet,
216− planet,
219+ ship: planet,
217− 'own',
220+ proxy: 'own',
218− 0,
221+ nonce: 0,
219 networkSeed,
222 networkSeed,
223+ type: 'configureKeys',
220 walletType,
224 walletType,
221− _web3
225+ web3: _web3,
222− );
226+ });
223
227
224− const setManagementProxyRequest = registerProxyAddress(
228+ await registerProxyAddress(
225 api,
229 api,
226 _wallet,
230 _wallet,
227 planet,
231 planet,
@@ -233,24 +237,17 @@ export default function useRoller() {
233 _web3
237 _web3
234 );
238 );
235
239
236− const transferRequest = transferPointRequest(
240+ await submitL2Transaction({
237 api,
241 api,
238− _wallet,
242+ wallet: _wallet,
239− planet,
243+ ship: planet,
240− 'own',
244+ proxy: 'own',
241− 2,
245+ type: 'transferPoint',
246+ nonce: 2,
242− inviteWallet.ownership.keys.address,
247+ address: inviteWallet.ownership.keys.address,
243 walletType,
248 walletType,
244− _web3
249+ web3: _web3,
245− );
250+ });
246−
247− requests.push(
248− spawnRequest,
249− configureKeysRequest,
250− setManagementProxyRequest,
251− transferRequest
252− );
253− await Promise.all(requests);
254
251
255 invites.push({
252 invites.push({
256 hash: '',
253 hash: '',
@@ -497,30 +494,23 @@ export default function useRoller() {
497 authToken,
494 authToken,
498 revision: nextRevision,
495 revision: nextRevision,
499 });
496 });
500− const txHash = await configureKeys(
497+ const txHash = await submitL2Transaction({
501 api,
498 api,
502− _wallet,
499+ wallet: _wallet,
503− _point,
500+ ship: _point,
504− proxy!,
501+ proxy: proxy!,
505 nonce,
502 nonce,
506 networkSeed,
503 networkSeed,
504+ type: 'configureKeys',
507 walletType,
505 walletType,
508− _web3,
506+ web3: _web3,
509− breach
507+ breach,
510− );
508+ });
511 const pendingTx = await api.getPendingTx(txHash);
509 const pendingTx = await api.getPendingTx(txHash);
512
510
513 return pendingTx;
511 return pendingTx;
514 };
512 };
515
513
516− interface ReticketParams {
517− point: Ship;
518− to: EthAddress;
519− manager: EthAddress;
520− fromWallet: any; // TODO: wallet type
521− toWallet: any; // TODO: wallet type
522− }
523−
524 const performL2Reticket = async ({
514 const performL2Reticket = async ({
525 point,
515 point,
526 to,
516 to,
@@ -542,16 +532,17 @@ export default function useRoller() {
542 toWallet,
532 toWallet,
543 nextRevision
533 nextRevision
544 );
534 );
545− const configureKeysRequest = await configureKeys(
535+ const configureKeysRequest = await submitL2Transaction({
546 api,
536 api,
547− fromWallet,
537+ wallet: fromWallet,
548− point,
538+ ship: point,
549 proxy,
539 proxy,
550 nonce,
540 nonce,
551 networkSeed,
541 networkSeed,
552 walletType,
542 walletType,
543+ type: 'configureKeys',
553− _web3
544+ web3: _web3,
554− );
545+ });
555 nonce = nonce + 1;
546 nonce = nonce + 1;
556 requests.push(configureKeysRequest);
547 requests.push(configureKeysRequest);
557
548
@@ -587,16 +578,17 @@ export default function useRoller() {
587 requests.push(registerSpawnRequest);
578 requests.push(registerSpawnRequest);
588 }
579 }
589 // 4. Transfer point
580 // 4. Transfer point
590− const transferTxRequest = await transferPointRequest(
581+ const transferTxRequest = await submitL2Transaction({
591 api,
582 api,
592− fromWallet,
583+ wallet: fromWallet,
593− point,
584+ ship: point,
594− proxy,
585+ proxy: 'own',
586+ type: 'transferPoint',
595 nonce,
587 nonce,
596− to,
588+ address: to,
597 walletType,
589 walletType,
598− _web3
590+ web3: _web3,
599− );
591+ });
600 requests.push(transferTxRequest);
592 requests.push(transferTxRequest);
601
593
602 const hashes = await Promise.all(requests);
594 const hashes = await Promise.all(requests);
@@ -724,17 +716,19 @@ export default function useRoller() {
724
716
725 const nonce = await api.getNonce({ ship: _point, proxy });
717 const nonce = await api.getNonce({ ship: _point, proxy });
726
718
727− const txHash = await transferPointRequest(
719+ const txHash = await submitL2Transaction({
728 api,
720 api,
729− _wallet,
721+ wallet: _wallet,
730− _point,
722+ ship: _point,
731 proxy,
723 proxy,
732 nonce,
724 nonce,
733 address,
725 address,
726+ type: 'transferPoint',
734 walletType,
727 walletType,
735− _web3,
728+ web3: _web3,
736− reset || false
729+ reset,
737− );
730+ });
731+
738 const pendingTx = await api.getPendingTx(txHash);
732 const pendingTx = await api.getPendingTx(txHash);
739
733
740 return pendingTx;
734 return pendingTx;
@@ -742,6 +736,114 @@ export default function useRoller() {
742 [api, pointCursor, wallet, web3, walletType]
736 [api, pointCursor, wallet, web3, walletType]
743 );
737 );
744
738
739+ const sendL2Transaction = useCallback(
740+ async (params: SendL2Params) => {
741+ const _point = need.point(pointCursor);
742+ const _wallet = wallet.getOrElse(null);
743+ const _web3 = web3.getOrElse(null);
744+
745+ if (!_wallet) {
746+ // not using need because we want a custom error
747+ throw new Error('Internal Error: Missing Wallet');
748+ }
749+
750+ return submitL2Transaction({
751+ ...params,
752+ api,
753+ wallet: _wallet,
754+ walletType,
755+ web3: _web3,
756+ ship: _point,
757+ });
758+ },
759+ [api, pointCursor, wallet, web3, walletType]
760+ );
761+
762+ const changeSponsor = useCallback(
763+ async (newSponsor: number) => {
764+ const _wallet = wallet.getOrElse(null);
765+ const _point = need.point(pointCursor);
766+
767+ if (!_wallet) {
768+ // not using need because we want a custom error
769+ throw new Error('Internal Error: Missing Wallet');
770+ }
771+
772+ const pointDetails = await api.getPoint(_point);
773+ const proxy = getManagerProxy(pointDetails.ownership!, _wallet.address);
774+
775+ if (proxy === undefined)
776+ throw new Error("Error: Address doesn't match proxy");
777+
778+ const nonce = await api.getNonce({ ship: _point, proxy });
779+
780+ sendL2Transaction({
781+ newSponsor,
782+ nonce,
783+ proxy,
784+ type: 'escape',
785+ ship: _point,
786+ });
787+ },
788+ [api, sendL2Transaction, wallet, pointCursor]
789+ );
790+
791+ const initPoint = useCallback(
792+ (relationship: Relationship) => async (point: string | number) => {
793+ const _wallet = wallet.getOrElse(null);
794+
795+ if (!_wallet) {
796+ return EMPTY_POINT;
797+ }
798+ try {
799+ const pointNum = Number(point);
800+ const rawDetails = await api.getPoint(pointNum);
801+ const details = toL1Details(rawDetails);
802+
803+ return new Point(pointNum, relationship, details, _wallet.address);
804+ } catch (e) {
805+ console.warn(e);
806+ }
807+ },
808+ [api, wallet]
809+ );
810+
811+ const getPointDetails = useCallback(
812+ async (
813+ ownedPoints: number[],
814+ incomingPoints: number[],
815+ managingPoints: number[],
816+ votingPoints: number[],
817+ spawningPoints: number[]
818+ ) => {
819+ try {
820+ const allPoints = [
821+ await Promise.all(ownedPoints.map(initPoint(Relationship.own))),
822+ await Promise.all(
823+ incomingPoints.map(initPoint(Relationship.transfer))
824+ ),
825+ await Promise.all(managingPoints.map(initPoint(Relationship.manage))),
826+ await Promise.all(votingPoints.map(initPoint(Relationship.vote))),
827+ await Promise.all(spawningPoints.map(initPoint(Relationship.spawn))),
828+ ];
829+
830+ setPoints(
831+ allPoints.reduce((acc: Points, cur: (Point | undefined)[]) => {
832+ cur.forEach(p => {
833+ if (p) {
834+ acc[p.value] = p;
835+ }
836+ });
837+ return acc;
838+ }, {})
839+ );
840+ } catch (e) {
841+ console.warn(e);
842+ }
843+ },
844+ [setPoints, initPoint]
845+ );
846+
745 // On load, get initial config
847 // On load, get initial config
746 useEffect(() => {
848 useEffect(() => {
747 if (config) {
849 if (config) {
@@ -759,30 +861,32 @@ export default function useRoller() {
759 if (nextBatchTime - ONE_SECOND <= new Date().getTime()) {
861 if (nextBatchTime - ONE_SECOND <= new Date().getTime()) {
760 api.getRollerConfig().then(response => {
862 api.getRollerConfig().then(response => {
761 setNextBatchTime(response.nextBatch);
863 setNextBatchTime(response.nextBatch);
762− getNumInvites(currentL2);
864+ getNumInvites(!!point?.isL2);
763 });
865 });
764 }
866 }
765 }, 10000000);
867 }, 10000000);
766
868
767 return () => clearInterval(interval);
869 return () => clearInterval(interval);
768− }, [nextBatchTime, getPendingTransactions, currentL2]); // eslint-disable-line react-hooks/exhaustive-deps
870+ }, [nextBatchTime, getPendingTransactions, point]); // eslint-disable-line react-hooks/exhaustive-deps
769
871
770 return {
872 return {
771 adoptPoint,
873 adoptPoint,
772 api,
874 api,
773− performL2Reticket,
875+ changeSponsor,
774 config,
876 config,
775− getPoints,
877+ configureNetworkingKeys,
776 getInvites,
878 getInvites,
879+ getNumInvites,
880+ getPoints,
881+ getPointDetails,
777 getPendingTransactions,
882 getPendingTransactions,
778 generateInviteCodes,
883 generateInviteCodes,
779 kickPoint,
884 kickPoint,
780 rejectPoint,
885 rejectPoint,
781 transferPoint,
886 transferPoint,
887+ ls,
888+ performL2Reticket,
782 setProxyAddress,
889 setProxyAddress,
783− configureNetworkingKeys,
784− getNumInvites,
785 showInvite,
890 showInvite,
786− ls,
787 };
891 };
788}
892}
src/lib/utils/address.ts+8−6
@@ -11,15 +11,15 @@ export const addHexPrefix = (hex: string) => {
11 return hex.startsWith('0x') ? hex : `0x${hex}`;
11 return hex.startsWith('0x') ? hex : `0x${hex}`;
12};
12};
13
13
14−export const isValidAddress = a => '0x0' === a || isAddress(a);
14+export const isValidAddress = (a: string) => '0x0' === a || isAddress(a);
15
15
16−export const abbreviateAddress = address =>
16+export const abbreviateAddress = (address: string) =>
17 `${address.slice(0, 6)}…${address.slice(-4)}`;
17 `${address.slice(0, 6)}…${address.slice(-4)}`;
18
18
19−export const isZeroAddress = a =>
19+export const isZeroAddress = (a: string) =>
20 a === ETH_ZERO_ADDR || a === ETH_ZERO_ADDR_SHORT;
20 a === ETH_ZERO_ADDR || a === ETH_ZERO_ADDR_SHORT;
21
21
22−export const toChecksumAddress = address => {
22+export const toChecksumAddress = (address: string) => {
23 const addr = stripHexPrefix(address).toLowerCase();
23 const addr = stripHexPrefix(address).toLowerCase();
24 const hash = keccak256(addr).toString('hex');
24 const hash = keccak256(addr).toString('hex');
25
25
@@ -31,8 +31,10 @@ export const toChecksumAddress = address => {
31 );
31 );
32};
32};
33
33
34−export const eqAddr = (addr0, addr1) =>
34+export const eqAddr = (addr0: string, addr1: string) =>
35+ !addr0 || !addr1
36+ ? false
35− toChecksumAddress(addr0) === toChecksumAddress(addr1);
37+ : toChecksumAddress(addr0) === toChecksumAddress(addr1);
36
38
37export const publicToAddress = (publicKey: Buffer): string => {
39export const publicToAddress = (publicKey: Buffer): string => {
38 return '0x' + pubToAddress(Buffer.from(publicKey), true).toString('hex');
40 return '0x' + pubToAddress(Buffer.from(publicKey), true).toString('hex');
src/lib/utils/invite.ts+2−2
@@ -30,5 +30,5 @@ export const generateCsvLine = (ind: number, ticket: string, planet: number) =>
30 planet
30 planet
31 )},${planet},${ticket}\n`;
31 )},${planet},${ticket}\n`;
32
32
33−export const generateCsvName = (defaultName: string, point: number) =>
33+export const generateCsvName = (defaultName: string, patp: string) =>
34− `${point ? ob.patp(point).slice(1) : 'bridge'}_${defaultName}`;
34+ `${patp}_${defaultName}`;
src/lib/utils/point.ts+24
@@ -1,4 +1,6 @@
1+import { L2Point } from '@urbit/roller-api';
1import { azimuth } from 'azimuth-js';
2import { azimuth } from 'azimuth-js';
3+import { L1Point } from 'lib/types/L1Point';
2
4
3export const isGalaxy = (point: number) =>
5export const isGalaxy = (point: number) =>
4 azimuth.getPointSize(point) === azimuth.PointSize.Galaxy;
6 azimuth.getPointSize(point) === azimuth.PointSize.Galaxy;
@@ -8,3 +10,25 @@ export const isStar = (point: number) =>
8
10
9export const isPlanet = (point: number) =>
11export const isPlanet = (point: number) =>
10 azimuth.getPointSize(point) === azimuth.PointSize.Planet;
12 azimuth.getPointSize(point) === azimuth.PointSize.Planet;
13+
14+export const toL1Details = (point: L2Point): L1Point => {
15+ return {
16+ layer: point?.dominion === 'l2' ? 2 : 1,
17+ isL2Spawn: point.dominion === 'spawn' || point.dominion === 'l2',
18+ active: true,
19+ authenticationKey: point?.network?.keys?.auth,
20+ continuityNumber: point?.network?.rift,
21+ cryptoSuiteVersion: point?.network?.keys?.suite,
22+ encryptionKey: point?.network?.keys?.crypt,
23+ escapeRequested: point?.network?.escape ? true : false,
24+ escapeRequestedTo: point?.network?.escape,
25+ hasSponsor: point?.network?.sponsor?.has,
26+ keyRevisionNumber: point?.network?.keys?.life,
27+ managementProxy: point?.ownership?.managementProxy?.address!,
28+ owner: point?.ownership?.owner?.address!,
29+ spawnProxy: point?.ownership?.spawnProxy?.address!,
30+ sponsor: point?.network?.sponsor?.who,
31+ transferProxy: point?.ownership?.transferProxy?.address!,
32+ votingProxy: point?.ownership?.votingProxy?.address!,
33+ };
34+};
src/lib/utils/roller.ts+61−4
@@ -1,8 +1,5 @@
1import { Just, Nothing } from 'folktale/maybe';
1import { Just, Nothing } from 'folktale/maybe';
2−import { Invite } from 'lib/types/Invite';
3−import { signTransactionHash } from 'lib/authToken';
4import { randomHex } from 'web3-utils';
2import { randomHex } from 'web3-utils';
5−
6import RollerRPCAPI, {
3import RollerRPCAPI, {
7 Proxy,
4 Proxy,
8 Signature,
5 Signature,
@@ -15,10 +12,13 @@ import RollerRPCAPI, {
15 PendingTransaction,
12 PendingTransaction,
16} from '@urbit/roller-api';
13} from '@urbit/roller-api';
17
14
15+import { Invite } from 'lib/types/Invite';
16+import { signTransactionHash } from 'lib/authToken';
18import { deriveNetworkKeys, CRYPTO_SUITE_VERSION } from 'lib/keys';
17import { deriveNetworkKeys, CRYPTO_SUITE_VERSION } from 'lib/keys';
19import { addHexPrefix } from 'lib/utils/address';
18import { addHexPrefix } from 'lib/utils/address';
20import { makeDeterministicTicket, generateWallet } from 'lib/walletgen';
19import { makeDeterministicTicket, generateWallet } from 'lib/walletgen';
21import { WALLET_TYPES } from 'lib/constants';
20import { WALLET_TYPES } from 'lib/constants';
21+import { L2TransactionArgs, TransactionData } from 'lib/types/L2Transaction';
22
22
23export const SECOND = 1000;
23export const SECOND = 1000;
24export const MINUTE = SECOND * 60;
24export const MINUTE = SECOND * 60;
@@ -41,7 +41,7 @@ export const getTimeToNextBatch = (nextBatch: number, now: number) => {
41 return `${padZero(hours)}h ${padZero(minutes)}m ${padZero(seconds)}s`;
41 return `${padZero(hours)}h ${padZero(minutes)}m ${padZero(seconds)}s`;
42};
42};
43
43
44−export const isL2 = (dom?: string) => dom === 'l2' || dom === 'spawn';
44+export const isL2 = (dom?: string) => dom === 'l2';
45
45
46export const isL2Spawn = (dom?: string) => dom === 'spawn';
46export const isL2Spawn = (dom?: string) => dom === 'spawn';
47
47
@@ -319,6 +319,63 @@ export const registerProxyAddress = async (
319 return setProxy(api, proxyAddressType, sig, from, wallet.address, data);
319 return setProxy(api, proxyAddressType, sig, from, wallet.address, data);
320};
320};
321
321
322+const getDataAndMethod = (args: L2TransactionArgs) => {
323+ const { address, api, breach, networkSeed, reset, pointToSpawn } = args;
324+ const result: TransactionData = { data: { address }, method: () => null };
325+
326+ switch (args.type) {
327+ case 'transferPoint':
328+ result.method = api.transferPoint;
329+ result.data.reset = reset || false;
330+ return result;
331+ case 'spawn':
332+ result.method = api.spawn;
333+ result.data.ship = pointToSpawn;
334+ return result;
335+ case 'configureKeys':
336+ // TODO: do something here?
337+ if (Nothing.hasInstance(networkSeed)) {
338+ console.log("Network key Error: couldn't derive network keys");
339+ // throw new Error("Network key Error: couldn't derive network keys");
340+ }
341+
342+ const seed = Just.hasInstance(networkSeed)
343+ ? networkSeed.value
344+ : randomHex(32);
345+
346+ const pair = deriveNetworkKeys(seed);
347+
348+ result.data = {
349+ encrypt: addHexPrefix(pair.crypt.public),
350+ auth: addHexPrefix(pair.auth.public),
351+ cryptoSuite: String(CRYPTO_SUITE_VERSION),
352+ breach: breach || false,
353+ };
354+ result.method = api.configureKeys;
355+ return result;
356+ }
357+};
358+
359+export const submitL2Transaction = async (args: L2TransactionArgs) => {
360+ const { api, wallet, ship, type, proxy, nonce, walletType, web3 } = args;
361+
362+ const from = { ship, proxy };
363+ const { data, method } = getDataAndMethod(args);
364+
365+ const sig = await generateHashAndSign(
366+ api,
367+ wallet,
368+ nonce,
369+ from,
370+ type,
371+ data,
372+ walletType,
373+ web3
374+ );
375+
376+ return method(sig, from, wallet.address, data);
377+};
378+
322export const reticketL2Point = async () => {};
379export const reticketL2Point = async () => {};
323
380
324export const hasInvite = (point: number) => (invite: Invite) =>
381export const hasInvite = (point: number) => (invite: Invite) =>
src/lib/worker/MyWebworker.jsnew file+8
@@ -0,0 +1,8 @@
1+export default function MyWorker(args) {
2+ console.log('ARGS', args)
3+ let onmessage = e => { // eslint-disable-line no-unused-vars
4+ // Write your code here...
5+ console.log('MESSAGE', e)
6+ postMessage("Response");
7+ };
8+}
src/lib/worker/WebWorker.jsnew file+9
@@ -0,0 +1,9 @@
1+export default class WebWorker {
2+ constructor(worker) {
3+ let code = worker.toString();
4+ code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}"));
5+
6+ const blob = new Blob([code], { type: "application/javascript" });
7+ return new Worker(URL.createObjectURL(blob));
8+ }
9+}
src/store/lib/useControlledPointsStore.js+35−39
@@ -6,14 +6,13 @@ import * as azimuth from 'azimuth-js';
6import { useNetwork } from '../network';
6import { useNetwork } from '../network';
7import { useWallet } from 'store/wallet';
7import { useWallet } from 'store/wallet';
8import useRoller from 'lib/useRoller';
8import useRoller from 'lib/useRoller';
9−import { isL2 } from 'lib/utils/roller';
10
9
11const onlyUnique = (value, index, self) => self.indexOf(value) === index;
10const onlyUnique = (value, index, self) => self.indexOf(value) === index;
12
11
13export default function useControlledPointsStore() {
12export default function useControlledPointsStore() {
14 const { contracts } = useNetwork();
13 const { contracts } = useNetwork();
15 const { wallet } = useWallet();
14 const { wallet } = useWallet();
16− const { api, getPoints } = useRoller();
15+ const { getPoints, getPointDetails } = useRoller();
17
16
18 const [controlledPoints, _setControlledPoints] = useState(Nothing());
17 const [controlledPoints, _setControlledPoints] = useState(Nothing());
19
18
@@ -30,79 +29,76 @@ export default function useControlledPointsStore() {
30
29
31 try {
30 try {
32 const [
31 const [
33− ownedPoints,
34− incomingPoints,
35− managingPoints,
36− votingPoints,
37− spawningPoints,
38 ownedPointsL1,
32 ownedPointsL1,
39 incomingPointsL1,
33 incomingPointsL1,
40 managingPointsL1,
34 managingPointsL1,
41 votingPointsL1,
35 votingPointsL1,
42 spawningPointsL1,
36 spawningPointsL1,
37+ ownedPointsL2,
38+ incomingPointsL2,
39+ managingPointsL2,
40+ votingPointsL2,
41+ spawningPointsL2,
43 ] = await Promise.all([
42 ] = await Promise.all([
44− getPoints('own', address),
45− getPoints('transfer', address),
46− getPoints('manage', address),
47− getPoints('vote', address),
48− getPoints('spawn', address),
49 azimuth.azimuth.getOwnedPoints(_contracts, address),
43 azimuth.azimuth.getOwnedPoints(_contracts, address),
50 azimuth.azimuth.getTransferringFor(_contracts, address),
44 azimuth.azimuth.getTransferringFor(_contracts, address),
51 azimuth.azimuth.getManagerFor(_contracts, address),
45 azimuth.azimuth.getManagerFor(_contracts, address),
52 azimuth.azimuth.getVotingFor(_contracts, address),
46 azimuth.azimuth.getVotingFor(_contracts, address),
53 azimuth.azimuth.getSpawningFor(_contracts, address),
47 azimuth.azimuth.getSpawningFor(_contracts, address),
48+ getPoints('own', address),
49+ getPoints('transfer', address),
50+ getPoints('manage', address),
51+ getPoints('vote', address),
52+ getPoints('spawn', address),
54 ]);
53 ]);
55
54
56− const pointsWithLayers = await Promise.all(
57− ownedPoints.map(async ship => {
58− try {
59− const pointInfo = await api.getPoint(Number(ship));
60− return {
61− point: Number(ship),
62− layer: isL2(pointInfo?.dominion) ? 2 : 1,
63− };
64− } catch (error) {
65− console.warn(error);
66− return { point: ship, layer: 1 };
67− }
68− })
69− );
70−
71 if (
55 if (
72− ownedPoints === null &&
56+ ownedPointsL1 === null &&
73− incomingPoints === null &&
57+ incomingPointsL1 === null &&
74− managingPoints === null &&
58+ managingPointsL1 === null &&
75− votingPoints === null &&
59+ votingPointsL1 === null &&
76− spawningPoints === null
60+ spawningPointsL1 === null &&
61+ ownedPointsL2 === null &&
62+ incomingPointsL2 === null &&
63+ managingPointsL2 === null &&
64+ votingPointsL2 === null &&
65+ spawningPointsL2 === null
77 ) {
66 ) {
78 _setControlledPoints(
67 _setControlledPoints(
79 Just(Result.Error('Failed to read the blockchain.'))
68 Just(Result.Error('Failed to read the blockchain.'))
80 );
69 );
81 } else {
70 } else {
71+ getPointDetails(
72+ (ownedPointsL1 || []).concat(ownedPointsL2 || []),
73+ (incomingPointsL1 || []).concat(incomingPointsL2 || []),
74+ (managingPointsL1 || []).concat(managingPointsL2 || []),
75+ (votingPointsL1 || []).concat(votingPointsL2 || []),
76+ (spawningPointsL1 || []).concat(spawningPointsL2 || [])
77+ );
78+
82 _setControlledPoints(
79 _setControlledPoints(
83 Just(
80 Just(
84 Result.Ok({
81 Result.Ok({
85− ownedPoints: ownedPoints
82+ ownedPoints: ownedPointsL2
86 .concat(ownedPointsL1)
83 .concat(ownedPointsL1)
87 .map(Number)
84 .map(Number)
88 .filter(onlyUnique),
85 .filter(onlyUnique),
89− incomingPoints: incomingPoints
86+ incomingPoints: incomingPointsL2
90 .concat(incomingPointsL1)
87 .concat(incomingPointsL1)
91 .map(Number)
88 .map(Number)
92 .filter(onlyUnique),
89 .filter(onlyUnique),
93− managingPoints: managingPoints
90+ managingPoints: managingPointsL2
94 .concat(managingPointsL1)
91 .concat(managingPointsL1)
95 .map(Number)
92 .map(Number)
96 .filter(onlyUnique),
93 .filter(onlyUnique),
97− votingPoints: votingPoints
94+ votingPoints: votingPointsL2
98 .concat(votingPointsL1)
95 .concat(votingPointsL1)
99 .map(Number)
96 .map(Number)
100 .filter(onlyUnique),
97 .filter(onlyUnique),
101− spawningPoints: spawningPoints
98+ spawningPoints: spawningPointsL2
102 .concat(spawningPointsL1)
99 .concat(spawningPointsL1)
103 .map(Number)
100 .map(Number)
104 .filter(onlyUnique),
101 .filter(onlyUnique),
105− pointsWithLayers,
106 })
102 })
107 )
103 )
108 );
104 );
@@ -111,7 +107,7 @@ export default function useControlledPointsStore() {
111 console.error('failed to fetch controlled points', error);
107 console.error('failed to fetch controlled points', error);
112 _setControlledPoints(Just(Result.Error(error)));
108 _setControlledPoints(Just(Result.Error(error)));
113 }
109 }
114− }, [contracts, wallet, getPoints, api]);
110+ }, [contracts, wallet, getPoints, getPointDetails]);
115
111
116 // sync controlled points whenever wallet or contracts changes
112 // sync controlled points whenever wallet or contracts changes
117 useEffect(() => {
113 useEffect(() => {
src/store/lib/useDetailsStore.ts+3−25
@@ -2,32 +2,10 @@ import { useState, useCallback } from 'react';
2import { Just, Nothing } from 'folktale/maybe';
2import { Just, Nothing } from 'folktale/maybe';
3import * as azimuth from 'azimuth-js';
3import * as azimuth from 'azimuth-js';
4
4
5−import useRoller from '../../lib/useRoller';
6−import { useNetwork } from '../network';
7−import { L1Point } from 'lib/types/L1Point';
8−import { L2Point } from '@urbit/roller-api';
9import { isDevelopment } from 'lib/flags';
5import { isDevelopment } from 'lib/flags';
10−
6+import { toL1Details } from 'lib/utils/point';
11−const toL1Details = (point: L2Point): L1Point => {
7+import useRoller from 'lib/useRoller';
12− return {
8+import { useNetwork } from '../network';
13− dominion: 'l2',
14− active: true,
15− authenticationKey: point.network.keys.auth,
16− continuityNumber: point.network.rift,
17− cryptoSuiteVersion: point.network.keys.suite,
18− encryptionKey: point.network.keys.crypt,
19− escapeRequested: point.network.escape ? true : false,
20− escapeRequestedTo: point.network.escape,
21− hasSponsor: point.network.sponsor.has,
22− keyRevisionNumber: point.network.keys.life,
23− managementProxy: point.ownership?.managementProxy?.address!,
24− owner: point.ownership?.owner?.address!,
25− spawnProxy: point.ownership?.spawnProxy?.address!,
26− sponsor: point.network.sponsor.who,
27− transferProxy: point.ownership?.transferProxy?.address!,
28− votingProxy: point.ownership?.votingProxy?.address!,
29− };
30−};
31
9
32export default function useDetailsStore() {
10export default function useDetailsStore() {
33 const { contracts } = useNetwork();
11 const { contracts } = useNetwork();
src/store/pointCursor.js+18−2
@@ -1,15 +1,31 @@
1−import React, { createContext, forwardRef, useContext, useState } from 'react';
1+import React, {
2+ createContext,
3+ forwardRef,
4+ useCallback,
5+ useContext,
6+ useState,
7+} from 'react';
2import { Nothing } from 'folktale/maybe';
8import { Nothing } from 'folktale/maybe';
9+import { useRollerStore } from './rollerStore';
3
10
4export const PointCursorContext = createContext(null);
11export const PointCursorContext = createContext(null);
5
12
6// pointCursor is Maybe<number>
13// pointCursor is Maybe<number>
7function _usePointCursor(initialPointCursor = Nothing()) {
14function _usePointCursor(initialPointCursor = Nothing()) {
8 const [pointCursor, setPointCursor] = useState(initialPointCursor);
15 const [pointCursor, setPointCursor] = useState(initialPointCursor);
16+ const { setPoint } = useRollerStore();
17+
18+ const handleSetPointCursor = useCallback(
19+ point => {
20+ setPoint(Number(point?.value));
21+ setPointCursor(point);
22+ },
23+ [setPoint, setPointCursor]
24+ );
9
25
10 return {
26 return {
11 pointCursor,
27 pointCursor,
12− setPointCursor,
28+ setPointCursor: handleSetPointCursor,
13 };
29 };
14}
30}
15
31
src/store/roller.ts → src/store/rollerStore.tsrenamed+18−19
@@ -1,24 +1,27 @@
1import create from 'zustand';
1import create from 'zustand';
2−import { L2Point, PendingTransaction } from '@urbit/roller-api';
2+import { PendingTransaction } from '@urbit/roller-api';
3
3
4−import { HOUR, isL2, isL2Spawn } from 'lib/utils/roller';
4+import { HOUR } from 'lib/utils/roller';
5import { Invite } from 'lib/types/Invite';
5import { Invite } from 'lib/types/Invite';
6+import Point, { Points, Relationship } from 'lib/types/Point';
7+import { toL1Details } from 'lib/utils/point';
8+
9+export const EMPTY_POINT = new Point(-1, Relationship.own, toL1Details({}), '');
6
10
7export interface RollerStore {
11export interface RollerStore {
8 nextBatchTime: number;
12 nextBatchTime: number;
9− nextRoll: string;
10 pendingTransactions: PendingTransaction[];
13 pendingTransactions: PendingTransaction[];
11− currentPoint: L2Point | null;
14+ point: Point;
12− currentL2: boolean;
15+ points: Points;
13− currentL2Spawn: boolean;
16+ pointList: Point[];
14 invitePoints: number[];
17 invitePoints: number[];
15 invites: Invite[];
18 invites: Invite[];
16 recentlyCompleted: number;
19 recentlyCompleted: number;
17 inviteGeneratingNum: number;
20 inviteGeneratingNum: number;
18 setNextBatchTime: (nextBatchTime: number) => void;
21 setNextBatchTime: (nextBatchTime: number) => void;
19− setNextRoll: (nextRoll: string) => void;
20 setPendingTransactions: (pendingTransactions: PendingTransaction[]) => void;
22 setPendingTransactions: (pendingTransactions: PendingTransaction[]) => void;
21− setCurrentPoint: (currentPoint: L2Point) => void;
23+ setPoint: (point: number) => void;
24+ setPoints: (points: Points) => void;
22 setInvitePoints: (points: number[]) => void;
25 setInvitePoints: (points: number[]) => void;
23 setInvites: (invites: Invite[]) => void;
26 setInvites: (invites: Invite[]) => void;
24 setInviteGeneratingNum: (numGenerating: number) => void;
27 setInviteGeneratingNum: (numGenerating: number) => void;
@@ -26,17 +29,15 @@ export interface RollerStore {
26
29
27export const useRollerStore = create<RollerStore>(set => ({
30export const useRollerStore = create<RollerStore>(set => ({
28 nextBatchTime: new Date().getTime() + HOUR,
31 nextBatchTime: new Date().getTime() + HOUR,
29− nextRoll: '0h 00m 00s',
30 pendingTransactions: [],
32 pendingTransactions: [],
31− currentPoint: null,
33+ point: EMPTY_POINT,
32− currentL2: false,
34+ pointList: [],
33− currentL2Spawn: false,
35+ points: {},
34 invitePoints: [],
36 invitePoints: [],
35 invites: [],
37 invites: [],
36 recentlyCompleted: 0,
38 recentlyCompleted: 0,
37 inviteGeneratingNum: 0,
39 inviteGeneratingNum: 0,
38 setNextBatchTime: (nextBatchTime: number) => set(() => ({ nextBatchTime })),
40 setNextBatchTime: (nextBatchTime: number) => set(() => ({ nextBatchTime })),
39− setNextRoll: (nextRoll: string) => set(() => ({ nextRoll })),
40 setPendingTransactions: (pendingTransactions: PendingTransaction[]) =>
41 setPendingTransactions: (pendingTransactions: PendingTransaction[]) =>
41 set(state => {
42 set(state => {
42 const oldPending = state.pendingTransactions.length;
43 const oldPending = state.pendingTransactions.length;
@@ -49,12 +50,10 @@ export const useRollerStore = create<RollerStore>(set => ({
49 }
50 }
50 return { ...state, pendingTransactions };
51 return { ...state, pendingTransactions };
51 }),
52 }),
52− setCurrentPoint: (currentPoint: L2Point) =>
53+ setPoint: (point: number) =>
53− set(() => ({
54+ set(state => ({ point: state.points[point] || EMPTY_POINT })),
54− currentPoint,
55+ setPoints: (points: Points) =>
55− currentL2: isL2(currentPoint?.dominion),
56+ set(() => ({ points, pointList: Object.values(points) })),
56− currentL2Spawn: isL2Spawn(currentPoint?.dominion),
57− })),
58 setInvitePoints: (invitePoints: number[]) => set(() => ({ invitePoints })),
57 setInvitePoints: (invitePoints: number[]) => set(() => ({ invitePoints })),
59 setInvites: (invites: Invite[]) => set(() => ({ invites })),
58 setInvites: (invites: Invite[]) => set(() => ({ invites })),
60 setInviteGeneratingNum: (inviteGeneratingNum: number) =>
59 setInviteGeneratingNum: (inviteGeneratingNum: number) =>
src/store/timerStore.tsnew file+16
@@ -0,0 +1,16 @@
1+import create from 'zustand';
2+import { HOUR } from 'lib/utils/roller';
3+
4+export interface TimerStore {
5+ nextBatchTime: number;
6+ nextRoll: string;
7+ setNextBatchTime: (nextBatchTime: number) => void;
8+ setNextRoll: (nextRoll: string) => void;
9+}
10+
11+export const useTimerStore = create<TimerStore>(set => ({
12+ nextBatchTime: new Date().getTime() + HOUR,
13+ nextRoll: '0h 00m 00s',
14+ setNextBatchTime: (nextBatchTime: number) => set(() => ({ nextBatchTime })),
15+ setNextRoll: (nextRoll: string) => set(() => ({ nextRoll })),
16+}));
src/views/Invite/Cohort.tsx+16−11
@@ -8,11 +8,10 @@ import {
8 Button as Button2,
8 Button as Button2,
9} from '@tlon/indigo-react';
9} from '@tlon/indigo-react';
10
10
11−import { usePointCursor } from 'store/pointCursor';
12import { usePointCache } from 'store/pointCache';
11import { usePointCache } from 'store/pointCache';
13import { useWallet } from 'store/wallet';
12import { useWallet } from 'store/wallet';
14import { useNetwork } from 'store/network';
13import { useNetwork } from 'store/network';
15−import { useRollerStore } from 'store/roller';
14+import { useRollerStore } from 'store/rollerStore';
16
15
17import { useLocalRouter } from 'lib/LocalRouter';
16import { useLocalRouter } from 'lib/LocalRouter';
18import * as need from 'lib/need';
17import * as need from 'lib/need';
@@ -45,6 +44,7 @@ import {
45} from 'lib/constants';
44} from 'lib/constants';
46import { isPlanet } from 'lib/utils/point';
45import { isPlanet } from 'lib/utils/point';
47import { generateInviteWallet } from 'lib/utils/roller';
46import { generateInviteWallet } from 'lib/utils/roller';
47+import { useTimerStore } from 'store/timerStore';
48
48
49interface L1Invite {
49interface L1Invite {
50 ticket: string;
50 ticket: string;
@@ -55,14 +55,16 @@ export default function InviteCohort() {
55 const { pop }: any = useLocalRouter();
55 const { pop }: any = useLocalRouter();
56 const { authToken }: any = useWallet();
56 const { authToken }: any = useWallet();
57 const {
57 const {
58− nextRoll,
58+ point,
59− currentL2,
60 pendingTransactions,
59 pendingTransactions,
61 invites,
60 invites,
62 invitePoints,
61 invitePoints,
63 setInviteGeneratingNum,
62 setInviteGeneratingNum,
64 setInvites,
63 setInvites,
65 } = useRollerStore();
64 } = useRollerStore();
65+
66+ const { nextRoll } = useTimerStore();
67+
66 const {
68 const {
67 ls,
69 ls,
68 generateInviteCodes,
70 generateInviteCodes,
@@ -70,11 +72,10 @@ export default function InviteCohort() {
70 getInvites,
72 getInvites,
71 showInvite,
73 showInvite,
72 } = useRoller();
74 } = useRoller();
73− const { pointCursor }: any = usePointCursor();
74 const { contracts }: any = useNetwork();
75 const { contracts }: any = useNetwork();
75 const { syncControlledPoints }: any = usePointCache();
76 const { syncControlledPoints }: any = usePointCache();
77+ const currentL2 = Boolean(point?.isL2);
76
78
77− const point = pointCursor.getOrElse(null);
78 const _contracts = need.contracts(contracts);
79 const _contracts = need.contracts(contracts);
79
80
80 const [numInvites, setNumInvites] = useState(
81 const [numInvites, setNumInvites] = useState(
@@ -232,7 +233,10 @@ export default function InviteCohort() {
232 hiddenElement.target = '_blank';
233 hiddenElement.target = '_blank';
233
234
234 //provide the name for the CSV file to be downloaded
235 //provide the name for the CSV file to be downloaded
235− hiddenElement.download = generateCsvName(DEFAULT_CSV_NAME, point);
236+ hiddenElement.download = generateCsvName(
237+ DEFAULT_CSV_NAME,
238+ point?.patp?.slice(1) || 'bridge'
239+ );
236 hiddenElement.click();
240 hiddenElement.click();
237 } else {
241 } else {
238 setError('There was an error creating the CSV');
242 setError('There was an error creating the CSV');
@@ -343,10 +347,11 @@ export default function InviteCohort() {
343 <StatelessTextInput
347 <StatelessTextInput
344 className="input-box"
348 className="input-box"
345 value={numInvites}
349 value={numInvites}
346− maxLength="3"
350+ maxLength={3}
347− onChange={e =>
351+ onChange={e => {
352+ const target = e.target as HTMLInputElement;
348− setNumInvites(Number(e.target.value.replace(/\D/g, '')))
353+ setNumInvites(Number(target.value.replace(/\D/g, '')));
349− }
354+ }}
350 />
355 />
351 planet invite code{numInvites > 1 ? 's' : ''}
356 planet invite code{numInvites > 1 ? 's' : ''}
352 </Row>
357 </Row>
src/views/Login/LoginSelector.tsx+22−17
@@ -21,7 +21,7 @@ import Window from 'components/L2/Window/Window';
21import HeaderPane from 'components/L2/Window/HeaderPane';
21import HeaderPane from 'components/L2/Window/HeaderPane';
22import BodyPane from 'components/L2/Window/BodyPane';
22import BodyPane from 'components/L2/Window/BodyPane';
23import Modal from 'components/L2/Modal';
23import Modal from 'components/L2/Modal';
24−import L2BackButton from 'components/L2/Headers/L2BackButton';
24+import HeaderButton from 'components/L2/Headers/HeaderButton';
25import './LoginSelector.scss';
25import './LoginSelector.scss';
26import { NAMES } from './Other';
26import { NAMES } from './Other';
27
27
@@ -147,21 +147,22 @@ export default function LoginSelector({
147 <Box className="close" onClick={() => setShowModal(false)}>
147 <Box className="close" onClick={() => setShowModal(false)}>
148 &#215;
148 &#215;
149 </Box>
149 </Box>
150− <div className="fw-bold">Other Wallet Types</div>
150+ <div className="fw-bold">Other Wallet Types</div>
151+ <div className="mb5">
151− <div className="mb5">All other wallet types are now supported via Metamask or
152+ All other wallet types are now supported via Metamask or
152− WalletConnect.</div>
153+ WalletConnect.
154+ </div>
153− <div className="mb5">
155+ <div className="mb5">
154− If you are you using a{' '}
156+ If you are you using a{' '}
155− <span className="fw-bold">
157+ <span className="fw-bold">
156− Hardware Wallet, Ethereum Keystore, or Ethereum Private Key
158+ Hardware Wallet, Ethereum Keystore, or Ethereum Private Key
157− </span>{' '}
159+ </span>{' '}
158− please use Metamask going forward.
160+ please use Metamask going forward.
159− </div>
161+ </div>
160− <div>
162+ <div>
161− If you are using a{' '}
162− <span className="fw-bold">mobile wallet</span>, please use
163+ If you are using a <span className="fw-bold">mobile wallet</span>,
163− WalletConnect going forward.
164+ please use WalletConnect going forward.
164− </div>
165+ </div>
165 </Box>
166 </Box>
166 </Modal>
167 </Modal>
167 </Grid>
168 </Grid>
@@ -173,7 +174,11 @@ export default function LoginSelector({
173
174
174 return (
175 return (
175 <Box display="flex" flexDirection="column">
176 <Box display="flex" flexDirection="column">
177+ <HeaderButton
178+ className="mb4"
179+ icon="ChevronWest"
176− <L2BackButton className="mb4" onBack={() => setCurrentTab(undefined)} />
180+ onClick={() => setCurrentTab(undefined)}
181+ />
177 <Window>
182 <Window>
178 <HeaderPane>
183 <HeaderPane>
179 <Row className="header-row">
184 <Row className="header-row">
src/views/Point/MigrateL2.tsx+51−36
@@ -3,6 +3,9 @@ import { Grid } from 'indigo-react';
3import { ecliptic } from 'azimuth-js';
3import { ecliptic } from 'azimuth-js';
4import * as ob from 'urbit-ob';
4import * as ob from 'urbit-ob';
5
5
6+import { useLocalRouter } from 'lib/LocalRouter';
7+import * as need from 'lib/need';
8+
6import View from 'components/View';
9import View from 'components/View';
7import L2BackHeader from 'components/L2/Headers/L2BackHeader';
10import L2BackHeader from 'components/L2/Headers/L2BackHeader';
8import BodyPane from 'components/L2/Window/BodyPane';
11import BodyPane from 'components/L2/Window/BodyPane';
@@ -10,11 +13,6 @@ import HeaderPane from 'components/L2/Window/HeaderPane';
10import Window from 'components/L2/Window/Window';
13import Window from 'components/L2/Window/Window';
11import { ReactComponent as StarIcon } from 'assets/star.svg';
14import { ReactComponent as StarIcon } from 'assets/star.svg';
12
15
13−import { useLocalRouter } from 'lib/LocalRouter';
14−import * as need from 'lib/need';
15−
16−import { usePointCursor } from 'store/pointCursor';
17−
18import {
16import {
19 Box,
17 Box,
20 Button,
18 Button,
@@ -33,11 +31,12 @@ import { usePointCache } from 'store/pointCache';
33import useEthereumTransaction from 'lib/useEthereumTransaction';
31import useEthereumTransaction from 'lib/useEthereumTransaction';
34import { DUMMY_L2_ADDRESS, GAS_LIMITS } from 'lib/constants';
32import { DUMMY_L2_ADDRESS, GAS_LIMITS } from 'lib/constants';
35import Dropdown from 'components/L2/Dropdowns/Dropdown';
33import Dropdown from 'components/L2/Dropdowns/Dropdown';
36−import { PointLayer } from 'lib/types/PointLayer';
37import Sigil from 'components/Sigil';
34import Sigil from 'components/Sigil';
38−import { isPlanet, isStar } from 'lib/utils/point';
35+import { isPlanet } from 'lib/utils/point';
39
36
40import './MigrateL2.scss';
37import './MigrateL2.scss';
38+import { useRollerStore } from 'store/rollerStore';
39+import Point from 'lib/types/Point';
41
40
42const PointEntry = ({
41const PointEntry = ({
43 point,
42 point,
@@ -46,6 +45,14 @@ const PointEntry = ({
46 point: number;
45 point: number;
47 select?: () => void;
46 select?: () => void;
48}) => {
47}) => {
48+ if (point < 0) {
49+ return (
50+ <Row className="entry" onClick={select}>
51+ <Box>Select a point</Box>
52+ </Row>
53+ );
54+ }
55+
49 const patp = ob.patp(point);
56 const patp = ob.patp(point);
50
57
51 return (
58 return (
@@ -69,6 +76,9 @@ const useMigrate = () => {
69 return useEthereumTransaction(
76 return useEthereumTransaction(
70 useCallback(
77 useCallback(
71 (selectedPoint: number, transfer: boolean) => {
78 (selectedPoint: number, transfer: boolean) => {
79+ if (selectedPoint < 0) {
80+ return;
81+ }
72 setSelectedPoint(selectedPoint);
82 setSelectedPoint(selectedPoint);
73 return transfer || isPlanet(selectedPoint)
83 return transfer || isPlanet(selectedPoint)
74 ? ecliptic.transferPoint(
84 ? ecliptic.transferPoint(
@@ -88,32 +98,27 @@ const useMigrate = () => {
88
98
89export default function MigrateL2() {
99export default function MigrateL2() {
90 const { pop }: any = useLocalRouter();
100 const { pop }: any = useLocalRouter();
91− const { pointCursor }: any = usePointCursor();
101+ const { point, pointList } = useRollerStore();
92− const { controlledPoints }: any = usePointCache();
102+ const l1Points = pointList.filter(({ canMigrate }) => canMigrate);
93
103
94 const [proceed, setProceed] = useState(false);
104 const [proceed, setProceed] = useState(false);
95 const [hideMessage, setHideMessage] = useState(false);
105 const [hideMessage, setHideMessage] = useState(false);
96 const [showDropdown, setShowDropdown] = useState(false);
106 const [showDropdown, setShowDropdown] = useState(false);
97 const [transfer, setTransfer] = useState(true);
107 const [transfer, setTransfer] = useState(true);
98
108
99− const points = controlledPoints?.value?.value?.pointsWithLayers || [];
100− const l1Points = points.filter(({ layer }: PointLayer) => layer === 1);
101−
102− let { point } =
103− l1Points.find((p: any) => isStar(p.point)) || controlledPoints[0];
104−
105− try {
106− point = need.point(pointCursor);
107− } catch (e) {}
108−
109− const [selectedPoint, setSelectedPoint] = useState(point);
109+ const [selectedPoint, setSelectedPoint] = useState(
110+ point.isL1 ? point : l1Points[0]
111+ );
110 const hideInfo = getHideMigrationMessage();
112 const hideInfo = getHideMigrationMessage();
111
113
112 const { construct, unconstruct, bind, completed } = useMigrate();
114 const { construct, unconstruct, bind, completed } = useMigrate();
113
115
114 useEffect(() => {
116 useEffect(() => {
115− if (point) {
117+ if (point.isGalaxy && point.isL1) {
118+ setTransfer(false);
119+ construct(point.value, false);
120+ } else if (point.isL1) {
116− construct(point, transfer);
121+ construct(point.value, transfer);
117 } else {
122 } else {
118 unconstruct();
123 unconstruct();
119 }
124 }
@@ -142,35 +147,38 @@ export default function MigrateL2() {
142 }, [hideMessage]);
147 }, [hideMessage]);
143
148
144 const selectPoint = useCallback(
149 const selectPoint = useCallback(
145− (point: number) => {
150+ (point: Point) => {
146− if (!isStar(point)) {
151+ if (point.isPlanet || (point.isStar && point.isL2Spawn)) {
147 setTransfer(true);
152 setTransfer(true);
153+ } else if (point.isGalaxy) {
154+ setTransfer(false);
148 }
155 }
149 setSelectedPoint(point);
156 setSelectedPoint(point);
150 setShowDropdown(false);
157 setShowDropdown(false);
151− construct(point, transfer);
158+ construct(point.value, transfer);
152 },
159 },
153 [setSelectedPoint, setShowDropdown, construct, transfer]
160 [setSelectedPoint, setShowDropdown, construct, transfer]
154 );
161 );
155
162
156 const toggleTransfer = useCallback(() => {
163 const toggleTransfer = useCallback(() => {
157− construct(selectedPoint, !transfer);
164+ construct(selectedPoint.value, !transfer);
158 setTransfer(!transfer);
165 setTransfer(!transfer);
159 }, [transfer, setTransfer, construct, selectedPoint]);
166 }, [transfer, setTransfer, construct, selectedPoint]);
160
167
161 const getContent = () => {
168 const getContent = () => {
162− const star = isStar(selectedPoint);
163−
164 if (l1Points.length === 0) {
169 if (l1Points.length === 0) {
165 return (
170 return (
166 <Box className="content">
171 <Box className="content">
167 <Box className="ship-selector">
172 <Box className="ship-selector">
168− <Box>All of your ships are already on Layer 2.</Box>
173+ <Box>All of your eligible ships are already on Layer 2.</Box>
169 </Box>
174 </Box>
170 </Box>
175 </Box>
171 );
176 );
172 }
177 }
173
178
179+ const isStar = Boolean(selectedPoint?.isStar);
180+ const isGalaxy = Boolean(selectedPoint?.isGalaxy);
181+
174 if (proceed) {
182 if (proceed) {
175 return (
183 return (
176 <Box className="content">
184 <Box className="content">
@@ -180,17 +188,17 @@ export default function MigrateL2() {
180 <Dropdown
188 <Dropdown
181 open={showDropdown}
189 open={showDropdown}
182 toggleOpen={() => setShowDropdown(!showDropdown)}
190 toggleOpen={() => setShowDropdown(!showDropdown)}
183− value={<PointEntry point={selectedPoint} />} // change this to include the sigil
191+ value={<PointEntry point={selectedPoint.value} />}
184 className="migrate-selector">
192 className="migrate-selector">
185− {l1Points.map(({ point }: PointLayer) => (
193+ {l1Points.map((p: Point) => (
186 <PointEntry
194 <PointEntry
187− point={point}
195+ point={p.value}
188− key={point}
196+ key={p.value}
189− select={() => selectPoint(point)}
197+ select={() => selectPoint(p)}
190 />
198 />
191 ))}
199 ))}
192 </Dropdown>
200 </Dropdown>
193− {star && (
201+ {isStar && !point.isL2Spawn && (
194 <Box className="transfer-spawn-selector">
202 <Box className="transfer-spawn-selector">
195 <Row onClick={toggleTransfer} className="transfer">
203 <Row onClick={toggleTransfer} className="transfer">
196 <RadioButton
204 <RadioButton
@@ -212,7 +220,7 @@ export default function MigrateL2() {
212 </Row>
220 </Row>
213 </Box>
221 </Box>
214 )}
222 )}
215− {star && (
223+ {isStar && (
216 <Box>
224 <Box>
217 Transferring this point will allow you to conduct all
225 Transferring this point will allow you to conduct all
218 transactions on Layer 2. Setting the spawn proxy of a star to
226 transactions on Layer 2. Setting the spawn proxy of a star to
@@ -220,6 +228,13 @@ export default function MigrateL2() {
220 will require you to do all other transactions on Layer 1.
228 will require you to do all other transactions on Layer 1.
221 </Box>
229 </Box>
222 )}
230 )}
231+ {isGalaxy && (
232+ <Box className="mt4">
233+ Setting the spawn proxy of this galaxy to Layer 2 will allow you
234+ to spawn stars on Layer 2 for free, but will still require you
235+ to do all other transactions on Layer 1.
236+ </Box>
237+ )}
223 </Box>
238 </Box>
224 <Grid.Item
239 <Grid.Item
225 full
240 full
src/views/Point/Point.tsx+49−75
@@ -3,9 +3,10 @@ import { Just } from 'folktale/maybe';
3import { Grid, Flex } from 'indigo-react';
3import { Grid, Flex } from 'indigo-react';
4import { Box, Icon, Row, Button, Checkbox } from '@tlon/indigo-react';
4import { Box, Icon, Row, Button, Checkbox } from '@tlon/indigo-react';
5
5
6−import { usePointCursor } from 'store/pointCursor';
6+import { usePointCache } from 'store/pointCache';
7import { useWallet } from 'store/wallet';
7import { useWallet } from 'store/wallet';
8−import { useRollerStore } from 'store/roller';
8+import { useRollerStore } from 'store/rollerStore';
9+import { useTimerStore } from 'store/timerStore';
9
10
10import View from 'components/View';
11import View from 'components/View';
11import Greeting from 'components/Greeting';
12import Greeting from 'components/Greeting';
@@ -21,34 +22,21 @@ import useCurrentPermissions from 'lib/useCurrentPermissions';
21import { useLocalRouter } from 'lib/LocalRouter';
22import { useLocalRouter } from 'lib/LocalRouter';
22import useRoller from 'lib/useRoller';
23import useRoller from 'lib/useRoller';
23import useSeenMissingKeys from 'lib/useSeenMissingKeys';
24import useSeenMissingKeys from 'lib/useSeenMissingKeys';
24−
25−import './Point.scss';
26−import { isL2 } from 'lib/utils/roller';
27−import { isDevelopment } from 'lib/flags';
28−import { usePointCache } from 'store/pointCache';
29−import Modal from 'components/L2/Modal';
30−import { isPlanet, isStar } from 'lib/utils/point';
31import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
25import { useHasNetworkKeysSet } from 'lib/useHasNetworkKeysSet';
26+
32import { InviteForm } from './InviteForm';
27import { InviteForm } from './InviteForm';
28+import Modal from 'components/L2/Modal';
29+import './Point.scss';
33import LoadingOverlay from 'components/L2/LoadingOverlay';
30import LoadingOverlay from 'components/L2/LoadingOverlay';
34
31
35export default function Point() {
32export default function Point() {
36 const { pop, push, names }: any = useLocalRouter();
33 const { pop, push, names }: any = useLocalRouter();
37 const { wallet }: any = useWallet();
34 const { wallet }: any = useWallet();
38− const { pointCursor }: any = usePointCursor();
39− const point = need.point(pointCursor);
40− const networkKeysSet = useHasNetworkKeysSet();
41 const { syncExtras }: any = usePointCache();
35 const { syncExtras }: any = usePointCache();
42−
43− const { api, getNumInvites, getPendingTransactions } = useRoller();
36+ const { getNumInvites, getPendingTransactions } = useRoller();
44− const {
45− pendingTransactions,
37+ const { pendingTransactions, point, invites } = useRollerStore();
46− nextRoll,
38+ const { nextRoll } = useTimerStore();
47− invitePoints,
39+ const networkKeysSet = useHasNetworkKeysSet();
48− setCurrentPoint,
49− setInvites,
50− } = useRollerStore();
51−
52 const [seenMissingKeys, setSeenMissingKeys] = useSeenMissingKeys();
40 const [seenMissingKeys, setSeenMissingKeys] = useSeenMissingKeys();
53 const [showModal, setShowModal] = useState(false);
41 const [showModal, setShowModal] = useState(false);
54 const [hideMessage, setHideMessage] = useState(false);
42 const [hideMessage, setHideMessage] = useState(false);
@@ -56,6 +44,8 @@ export default function Point() {
56
44
57 const pointRef = useRef<number | null>(null);
45 const pointRef = useRef<number | null>(null);
58
46
47+ const invitePoints = invites[point.value] || [];
48+
59 const hideModal = useCallback(() => {
49 const hideModal = useCallback(() => {
60 if (hideMessage) {
50 if (hideMessage) {
61 setSeenMissingKeys(true);
51 setSeenMissingKeys(true);
@@ -65,54 +55,36 @@ export default function Point() {
65 }, [hideMessage, setShowModal, setSeenMissingKeys]);
55 }, [hideMessage, setShowModal, setSeenMissingKeys]);
66
56
67 useEffect(() => {
57 useEffect(() => {
68− if (pointRef.current !== point) {
58+ if (!pointRef.current || pointRef.current !== point.value) {
69− pointRef.current = point;
59+ pointRef.current = point.value;
70− setShowModal(!networkKeysSet && !seenMissingKeys);
60+ setShowModal(!point.networkKeysSet && !seenMissingKeys);
71 }
61 }
72− }, [networkKeysSet, seenMissingKeys, point]);
62+ }, [seenMissingKeys, point]);
73
63
74 const loadL1Info = useCallback(async () => {
64 const loadL1Info = useCallback(async () => {
75− await syncExtras(point);
65+ await syncExtras(point.value);
76 }, [point, syncExtras]);
66 }, [point, syncExtras]);
77
67
78 const loadL2Info = useCallback(async () => {
68 const loadL2Info = useCallback(async () => {
79 const getTransactions = async () => {
69 const getTransactions = async () => {
80 setLoading(true);
70 setLoading(true);
81− const pointInfo = await api.getPoint(Number(point));
82−
83− if (isDevelopment) {
84− console.log('POINT INFO', pointInfo);
85− }
86 getPendingTransactions();
71 getPendingTransactions();
87− setCurrentPoint(pointInfo);
88− getNumInvites(isL2(pointInfo.dominion));
72+ getNumInvites(point.isL2);
89−
90 setTimeout(() => setLoading(false), 100);
73 setTimeout(() => setLoading(false), 100);
91 };
74 };
92
75
93 await getTransactions();
76 await getTransactions();
94− }, [
95− api,
96− point,
97− getNumInvites,
98− setCurrentPoint,
99− getPendingTransactions,
77+ }, [point, getNumInvites, getPendingTransactions, setLoading]);
100− setLoading,
101− ]);
102
78
103 useEffect(() => {
79 useEffect(() => {
104 loadL1Info();
80 loadL1Info();
105 loadL2Info();
81 loadL2Info();
106 }, [point]); // eslint-disable-line react-hooks/exhaustive-deps
82 }, [point]); // eslint-disable-line react-hooks/exhaustive-deps
107
83
108− useEffect(() => {
109− setInvites([]);
110− }, []); // eslint-disable-line react-hooks/exhaustive-deps
111−
112− const { isParent, canManage } = useCurrentPermissions();
84+ const { isParent, canManage, isSpawnProxy } = useCurrentPermissions();
113
85
114 // L1 invites
86 // L1 invites
115− const l1Invites = useInvites(point);
87+ const l1Invites = useInvites(point.value);
116 const {
88 const {
117 availableInvites,
89 availableInvites,
118 sentInvites,
90 sentInvites,
@@ -141,8 +113,6 @@ export default function Point() {
141
113
142 const goOps = useCallback(() => push(names.OPS), [push, names]);
114 const goOps = useCallback(() => push(names.OPS), [push, names]);
143
115
144− const planet = isPlanet(point);
145−
146 const [showInviteForm, setShowInviteForm] = useState(false);
116 const [showInviteForm, setShowInviteForm] = useState(false);
147
117
148 const address = need.addressFromWallet(wallet);
118 const address = need.addressFromWallet(wallet);
@@ -166,7 +136,7 @@ export default function Point() {
166 hideInvites={!networkKeysSet}
136 hideInvites={!networkKeysSet}
167 />
137 />
168 }>
138 }>
169− <Greeting point={point} />
139+ <Greeting point={point.value} />
170 {!!spawnedPending && (
140 {!!spawnedPending && (
171 <div className="transaction">
141 <div className="transaction">
172 <Row className="title-row">
142 <Row className="title-row">
@@ -215,7 +185,7 @@ export default function Point() {
215 </div>
185 </div>
216 ))}
186 ))}
217 <Passport
187 <Passport
218− point={Just(point)}
188+ point={Just(point.value)}
219 address={Just(address)}
189 address={Just(address)}
220 animationMode={'slide'}
190 animationMode={'slide'}
221 />
191 />
@@ -224,9 +194,9 @@ export default function Point() {
224 <Grid.Item full as={Flex} justify="between">
194 <Grid.Item full as={Flex} justify="between">
225 <Card
195 <Card
226 icon={(<Icon icon="ShipActivated" />) as any}
196 icon={(<Icon icon="ShipActivated" />) as any}
227− title={`${isStar(point) ? 'Star' : 'Galaxy'} Ops`}
197+ title={`${point.isStar ? 'Star' : 'Galaxy'} Ops`}
228 subtitle={
198 subtitle={
229− isStar(point)
199+ point.isStar
230 ? 'Residents, Requests, Spawn Planets'
200 ? 'Residents, Requests, Spawn Planets'
231 : 'Residents, Requests, Spawn Stars, Vote'
201 : 'Residents, Requests, Spawn Stars, Vote'
232 }
202 }
@@ -234,7 +204,7 @@ export default function Point() {
234 />
204 />
235 </Grid.Item>
205 </Grid.Item>
236 )}
206 )}
237− {planet && hasInvites && (
207+ {point.isPlanet && hasInvites && (
238 <InviteForm
208 <InviteForm
239 showInviteForm={showInviteForm}
209 showInviteForm={showInviteForm}
240 setShowInviteForm={setShowInviteForm}
210 setShowInviteForm={setShowInviteForm}
@@ -247,30 +217,34 @@ export default function Point() {
247 showInvites={showInvites}
217 showInvites={showInvites}
248 />
218 />
249 )}
219 )}
250− {!loadedInvites && planet && (
220+ {!loadedInvites && point.isPlanet && (
251 <Grid.Item className="mv2" full>
221 <Grid.Item className="mv2" full>
252 Invite Group <Blinky />
222 Invite Group <Blinky />
253 </Grid.Item>
223 </Grid.Item>
254 )}
224 )}
255 {/* {inviteButton} */}
225 {/* {inviteButton} */}
226+ {(canManage || isSpawnProxy) && (
256− <Grid.Item full as={Flex} justify="between">
227+ <Grid.Item full as={Flex} justify="between">
257− <Card
228+ <Card
258− title="ID"
229+ title="ID"
259− subtitle="Master Key, Passport, Proxy Addresses, etc"
230+ subtitle="Master Key, Passport, Proxy Addresses, etc"
260− icon={(<Icon icon="User" />) as any}
231+ icon={(<Icon icon="User" />) as any}
261− onClick={goUrbitID}
232+ onClick={goUrbitID}
262− disabled={!canManage}
233+ disabled={!canManage}
263− />
234+ />
264− </Grid.Item>
235+ </Grid.Item>
236+ )}
237+ {canManage && (
265− <Grid.Item full as={Flex} justify="between">
238+ <Grid.Item full as={Flex} justify="between">
266− <Card
239+ <Card
267− title="OS"
240+ title="OS"
268− subtitle="Sponsor, Network Keys, Access Key"
241+ subtitle="Sponsor, Network Keys, Access Key"
269− icon={(<Icon icon="Server" />) as any}
242+ icon={(<Icon icon="Server" />) as any}
270− onClick={goUrbitOS}
243+ onClick={goUrbitOS}
271− disabled={!canManage}
244+ disabled={!canManage}
272− />
245+ />
273− </Grid.Item>
246+ </Grid.Item>
247+ )}
274 </Grid>
248 </Grid>
275 <Modal show={showModal} hide={hideModal}>
249 <Modal show={showModal} hide={hideModal}>
276 <Box className="network-keys-modal">
250 <Box className="network-keys-modal">
src/views/Points.js+2−1
@@ -317,7 +317,7 @@ export default function Points() {
317 inset
317 inset
318 pop={pop}
318 pop={pop}
319 hideBack
319 hideBack
320− header={<L2PointHeader hideTimer hideInvites showMigrate />}>
320+ header={<L2PointHeader hideHome hideTimer hideInvites showMigrate />}>
321 <NavHeader>
321 <NavHeader>
322 <CopiableAddress
322 <CopiableAddress
323 text={address}
323 text={address}
@@ -327,6 +327,7 @@ export default function Points() {
327 </NavHeader>
327 </NavHeader>
328 {incomingPoints.map(point => (
328 {incomingPoints.map(point => (
329 <IncomingPoint
329 <IncomingPoint
330+ key={point}
330 point={point}
331 point={point}
331 accept={() => {
332 accept={() => {
332 setPointCursor(Just(point));
333 setPointCursor(Just(point));
src/views/Transactions/TransactionRow.tsx+1−1
@@ -11,7 +11,7 @@ import {
11} from 'lib/constants';
11} from 'lib/constants';
12import { abbreviateAddress } from 'lib/utils/address';
12import { abbreviateAddress } from 'lib/utils/address';
13import { useMemo } from 'react';
13import { useMemo } from 'react';
14−import { useRollerStore } from 'store/roller';
14+import { useRollerStore } from 'store/rollerStore';
15import LayerIndicator from 'components/L2/LayerIndicator';
15import LayerIndicator from 'components/L2/LayerIndicator';
16import { isPlanet, isStar } from 'lib/utils/point';
16import { isPlanet, isStar } from 'lib/utils/point';
17
17
src/views/UrbitID/Home.tsx+9−18
@@ -1,5 +1,4 @@
1import React, { useCallback } from 'react';
1import React, { useCallback } from 'react';
2−import { azimuth } from 'azimuth-js';
3import { Just } from 'folktale/maybe';
2import { Just } from 'folktale/maybe';
4import { Box, Button, Icon, Row } from '@tlon/indigo-react';
3import { Box, Button, Icon, Row } from '@tlon/indigo-react';
5
4
@@ -11,10 +10,9 @@ import { convertToInt } from 'lib/convertToInt';
11import { ETH_ZERO_ADDR } from 'lib/constants';
10import { ETH_ZERO_ADDR } from 'lib/constants';
12import { abbreviateAddress } from 'lib/utils/address';
11import { abbreviateAddress } from 'lib/utils/address';
13
12
14−import { usePointCursor } from 'store/pointCursor';
15import { usePointCache } from 'store/pointCache';
13import { usePointCache } from 'store/pointCache';
16import { useWallet } from 'store/wallet';
14import { useWallet } from 'store/wallet';
17−import { useRollerStore } from 'store/roller';
15+import { useRollerStore } from 'store/rollerStore';
18
16
19import Window from 'components/L2/Window/Window';
17import Window from 'components/L2/Window/Window';
20import HeaderPane from 'components/L2/Window/HeaderPane';
18import HeaderPane from 'components/L2/Window/HeaderPane';
@@ -25,10 +23,9 @@ import './UrbitID.scss';
25
23
26export default function UrbitIDHome() {
24export default function UrbitIDHome() {
27 const { push, names }: any = useLocalRouter();
25 const { push, names }: any = useLocalRouter();
28− const { pointCursor }: any = usePointCursor();
29 const { getDetails }: any = usePointCache();
26 const { getDetails }: any = usePointCache();
30 const { urbitWallet }: any = useWallet();
27 const { urbitWallet }: any = useWallet();
31− const { currentPoint } = useRollerStore();
28+ const { point } = useRollerStore();
32
29
33 const goDownloadKeys = useCallback(() => push(names.DOWNLOAD_KEYS), [
30 const goDownloadKeys = useCallback(() => push(names.DOWNLOAD_KEYS), [
34 push,
31 push,
@@ -40,15 +37,10 @@ export default function UrbitIDHome() {
40 const goResetKeys = useCallback(() => push(names.RESET_KEYS), [push, names]);
37 const goResetKeys = useCallback(() => push(names.RESET_KEYS), [push, names]);
41
38
42 const isMasterTicket = Just.hasInstance(urbitWallet);
39 const isMasterTicket = Just.hasInstance(urbitWallet);
43− const point = need.point(pointCursor);
44− const details = need.details(getDetails(point));
40+ const details = need.details(getDetails(point.value));
45− const { isOwner } = useCurrentPermissions();
41+ const { isOwner, canManage } = useCurrentPermissions();
46 const networkRevision = convertToInt(details.keyRevisionNumber, 10);
42 const networkRevision = convertToInt(details.keyRevisionNumber, 10);
47
43
48− const pointSize = azimuth.getPointSize(point);
49− const isParent = pointSize !== azimuth.PointSize.Planet;
50− const isSenate = pointSize === azimuth.PointSize.Galaxy;
51−
52 const goSetProxy = useCallback(
44 const goSetProxy = useCallback(
53 proxyType => push(names.SET_PROXY, { proxyType }),
45 proxyType => push(names.SET_PROXY, { proxyType }),
54 [push, names]
46 [push, names]
@@ -58,8 +50,7 @@ export default function UrbitIDHome() {
58
50
59 const noManagement = details.managementProxy === ETH_ZERO_ADDR;
51 const noManagement = details.managementProxy === ETH_ZERO_ADDR;
60 const noSpawn = details.spawnProxy === ETH_ZERO_ADDR;
52 const noSpawn = details.spawnProxy === ETH_ZERO_ADDR;
61− const showSpawn = currentPoint?.dominion === 'l1';
53+ const showSpawn = point.isL1 && !point.isL2Spawn;
62− const showManage = showSpawn || currentPoint?.dominion === 'spawn';
63
54
64 return (
55 return (
65 <Window className="id-home">
56 <Window className="id-home">
@@ -94,7 +85,7 @@ export default function UrbitIDHome() {
94 </Row>
85 </Row>
95 )}
86 )}
96
87
97− {isOwner && showManage && (
88+ {canManage && (
98 <Row className="between-row management">
89 <Row className="between-row management">
99 <Box>
90 <Box>
100 <Box>Management Address</Box>
91 <Box>Management Address</Box>
@@ -120,7 +111,7 @@ export default function UrbitIDHome() {
120 </Row>
111 </Row>
121 )}
112 )}
122
113
123− {isParent && networkRevision !== 0 && showSpawn && (
114+ {point.isParent && networkRevision !== 0 && showSpawn && (
124 <Row className="between-row management">
115 <Row className="between-row management">
125 <Box>
116 <Box>
126 <Box>Spawn Proxy Address</Box>
117 <Box>Spawn Proxy Address</Box>
@@ -160,7 +151,7 @@ export default function UrbitIDHome() {
160 </Row>
151 </Row>
161 )}
152 )}
162
153
163− {isParent && networkRevision === 0 && (
154+ {point.isParent && networkRevision === 0 && (
164 <Row className="between-row management">
155 <Row className="between-row management">
165 <Box>
156 <Box>
166 <Box>Network Keys Required</Box>
157 <Box>Network Keys Required</Box>
@@ -179,7 +170,7 @@ export default function UrbitIDHome() {
179 <Icon className="arrow-button" icon="ArrowEast" />
170 <Icon className="arrow-button" icon="ArrowEast" />
180 </Row>
171 </Row>
181
172
182− {isSenate && (
173+ {point.isGalaxy && (
183 <Row className="between-row management">
174 <Row className="between-row management">
184 <Box>
175 <Box>
185 <Box>Edit Voting Key</Box>
176 <Box>Edit Voting Key</Box>
src/views/UrbitID/ResetKeys/ResetExecute.js+5−3
@@ -16,7 +16,7 @@ import { useWallet } from 'store/wallet';
16import { usePointCursor } from 'store/pointCursor';
16import { usePointCursor } from 'store/pointCursor';
17import { usePointCache } from 'store/pointCache';
17import { usePointCache } from 'store/pointCache';
18import { useHistory } from 'store/history';
18import { useHistory } from 'store/history';
19−import { useRollerStore } from 'store/roller';
19+import { useRollerStore } from 'store/rollerStore';
20
20
21import useRoller from 'lib/useRoller';
21import useRoller from 'lib/useRoller';
22import { useWalletConnect } from 'lib/useWalletConnect';
22import { useWalletConnect } from 'lib/useWalletConnect';
@@ -56,7 +56,9 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
56 walletHdPath,
56 walletHdPath,
57 } = useWallet();
57 } = useWallet();
58 const { performL2Reticket } = useRoller();
58 const { performL2Reticket } = useRoller();
59+ const {
60+ point: { isL2 },
59− const { currentL2 } = useRollerStore();
61+ } = useRollerStore();
60 const { pointCursor } = usePointCursor();
62 const { pointCursor } = usePointCursor();
61 const { getDetails } = usePointCache();
63 const { getDetails } = usePointCache();
62 const {
64 const {
@@ -96,7 +98,7 @@ export default function ResetExecute({ newWallet, setNewWallet }) {
96 walletType === WALLET_TYPES.WALLET_CONNECT ? wcSend : undefined;
98 walletType === WALLET_TYPES.WALLET_CONNECT ? wcSend : undefined;
97
99
98 try {
100 try {
99− if (currentL2) {
101+ if (isL2) {
100 console.log(newWallet.value);
102 console.log(newWallet.value);
101 // FIXME: the useEffect is called twice, which is fine since the duplicate
103 // FIXME: the useEffect is called twice, which is fine since the duplicate
102 // L2 txs will be discarded
104 // L2 txs will be discarded
src/views/UrbitID/SetProxy.js+23−12
@@ -8,7 +8,7 @@ import './SetProxy.scss';
8import { useNetwork } from 'store/network';
8import { useNetwork } from 'store/network';
9import { usePointCache } from 'store/pointCache';
9import { usePointCache } from 'store/pointCache';
10import { usePointCursor } from 'store/pointCursor';
10import { usePointCursor } from 'store/pointCursor';
11−import { useRollerStore } from 'store/roller';
11+import { useRollerStore } from 'store/rollerStore';
12
12
13import {
13import {
14 PROXY_TYPE,
14 PROXY_TYPE,
@@ -65,6 +65,19 @@ const proxyFromDetails = (details, contracts, proxyType) => {
65 }
65 }
66};
66};
67
67
68+const getButtonText = proxyType => {
69+ switch (proxyType) {
70+ case PROXY_TYPE.MANAGEMENT:
71+ return 'Set Management Proxy';
72+ case PROXY_TYPE.SPAWN:
73+ return 'Set Spawn Proxy';
74+ case PROXY_TYPE.TRANSFER:
75+ return 'Set Transfer Proxy';
76+ default:
77+ return 'Set Management Proxy';
78+ }
79+};
80+
68function useSetProxy(proxyType) {
81function useSetProxy(proxyType) {
69 const { contracts } = useNetwork();
82 const { contracts } = useNetwork();
70 const { pointCursor } = usePointCursor();
83 const { pointCursor } = usePointCursor();
@@ -114,7 +127,9 @@ export default function SetProxy() {
114 const { getDetails } = usePointCache();
127 const { getDetails } = usePointCache();
115 const { pointCursor } = usePointCursor();
128 const { pointCursor } = usePointCursor();
116 const { contracts } = useNetwork();
129 const { contracts } = useNetwork();
130+ const {
131+ point: { isL2, isL2Spawn },
117− const { currentL2, currentL2Spawn } = useRollerStore();
132+ } = useRollerStore();
118 const { setProxyAddress, getPendingTransactions } = useRoller();
133 const { setProxyAddress, getPendingTransactions } = useRoller();
119 const [newAddress, setNewAddress] = useState('');
134 const [newAddress, setNewAddress] = useState('');
120
135
@@ -124,14 +139,10 @@ export default function SetProxy() {
124
139
125 const properProxyType = capitalize(proxyTypeToHuman(data.proxyType));
140 const properProxyType = capitalize(proxyTypeToHuman(data.proxyType));
126
141
127− const txType =
142+ const isL2Tx =
128− data.proxyType === 'MANAGEMENT' && (currentL2Spawn || !currentL2)
143+ (data.proxyType === 'MANAGEMENT' && isL2) ||
129− ? 'ETH_TX'
130− : data.proxyType === 'MANAGEMENT' && currentL2 && !currentL2Spawn
131− ? 'L2'
132− : data.proxyType === 'SPAWN' && currentL2
144+ (data.proxyType === 'SPAWN' && isL2Spawn) ||
133− ? 'L2'
145+ (data.proxyType === 'TRANSFER' && isL2);
134− : 'ETH_TX';
135
146
136 const {
147 const {
137 construct,
148 construct,
@@ -269,7 +280,7 @@ export default function SetProxy() {
269
280
270 <Grid.Item full as={FormError} />
281 <Grid.Item full as={FormError} />
271
282
272− {txType === 'L2' ? (
283+ {isL2Tx ? (
273 <Grid.Item
284 <Grid.Item
274 as={Button}
285 as={Button}
275 full
286 full
@@ -278,7 +289,7 @@ export default function SetProxy() {
278 solid
289 solid
279 disabled={values.unset}
290 disabled={values.unset}
280 onClick={setProxy}>
291 onClick={setProxy}>
281− {'Sign Transaction'}
292+ {getButtonText(data.proxyType)}
282 </Grid.Item>
293 </Grid.Item>
283 ) : (
294 ) : (
284 <Grid.Item
295 <Grid.Item
src/views/UrbitID/Transfer.js+8−6
@@ -8,7 +8,7 @@ import { useNetwork } from 'store/network';
8import { usePointCursor } from 'store/pointCursor';
8import { usePointCursor } from 'store/pointCursor';
9import { usePointCache } from 'store/pointCache';
9import { usePointCache } from 'store/pointCache';
10import { useStarReleaseCache } from 'store/starRelease';
10import { useStarReleaseCache } from 'store/starRelease';
11−import { useRollerStore } from 'store/roller';
11+import { useRollerStore } from 'store/rollerStore';
12
12
13import * as need from 'lib/need';
13import * as need from 'lib/need';
14import { useLocalRouter } from 'lib/LocalRouter';
14import { useLocalRouter } from 'lib/LocalRouter';
@@ -50,7 +50,9 @@ export default function AdminTransfer() {
50 const name = useCurrentPointName();
50 const name = useCurrentPointName();
51 const { pointCursor } = usePointCursor();
51 const { pointCursor } = usePointCursor();
52 const { starReleaseDetails } = useStarReleaseCache();
52 const { starReleaseDetails } = useStarReleaseCache();
53+ const {
54+ point: { isL2 },
53− const { currentL2 } = useRollerStore();
55+ } = useRollerStore();
54 const { setProxyAddress, getPendingTransactions } = useRoller();
56 const { setProxyAddress, getPendingTransactions } = useRoller();
55 const [owner, setNewOwner] = useState('');
57 const [owner, setNewOwner] = useState('');
56
58
@@ -77,7 +79,7 @@ export default function AdminTransfer() {
77 []
79 []
78 );
80 );
79
81
80− const onSignTx = useCallback(async () => {
82+ const transferPoint = useCallback(async () => {
81 // setLoading(true);
83 // setLoading(true);
82 if (owner === '') return;
84 if (owner === '') return;
83 try {
85 try {
@@ -157,7 +159,7 @@ export default function AdminTransfer() {
157
159
158 <Grid.Item full as={FormError} />
160 <Grid.Item full as={FormError} />
159
161
160− {currentL2 ? (
162+ {isL2 ? (
161 <Grid.Item
163 <Grid.Item
162 as={Button}
164 as={Button}
163 full
165 full
@@ -165,8 +167,8 @@ export default function AdminTransfer() {
165 center
167 center
166 solid
168 solid
167 disabled={values.unset}
169 disabled={values.unset}
168− onClick={onSignTx}>
170+ onClick={transferPoint}>
169− {'Sign Transaction'}
171+ {'Transfer'}
170 </Grid.Item>
172 </Grid.Item>
171 ) : (
173 ) : (
172 <Grid.Item
174 <Grid.Item
src/views/UrbitOS/ChangeSponsor.js+39−9
@@ -1,5 +1,5 @@
1import React, { useState, useMemo, useCallback, useEffect } from 'react';
1import React, { useState, useMemo, useCallback, useEffect } from 'react';
2−import { Grid } from 'indigo-react';
2+import { Grid, Button } from 'indigo-react';
3import * as ob from 'urbit-ob';
3import * as ob from 'urbit-ob';
4import { azimuth, ecliptic } from 'azimuth-js';
4import { azimuth, ecliptic } from 'azimuth-js';
5
5
@@ -27,6 +27,8 @@ import Window from 'components/L2/Window/Window';
27import HeaderPane from 'components/L2/Window/HeaderPane';
27import HeaderPane from 'components/L2/Window/HeaderPane';
28import { Box, Row } from '@tlon/indigo-react';
28import { Box, Row } from '@tlon/indigo-react';
29import BodyPane from 'components/L2/Window/BodyPane';
29import BodyPane from 'components/L2/Window/BodyPane';
30+import { useRollerStore } from 'store/rollerStore';
31+import useRoller from 'lib/useRoller';
30
32
31function useChangeSponsor() {
33function useChangeSponsor() {
32 const { contracts } = useNetwork();
34 const { contracts } = useNetwork();
@@ -50,11 +52,23 @@ function useChangeSponsor() {
50function ChangeSponsor({ onDone }) {
52function ChangeSponsor({ onDone }) {
51 const { contracts } = useNetwork();
53 const { contracts } = useNetwork();
52 const { pointCursor } = usePointCursor();
54 const { pointCursor } = usePointCursor();
55+ const { changeSponsor } = useRoller();
56+ const {
57+ point: { isL2 },
58+ } = useRollerStore();
53
59
54 const _contracts = need.contracts(contracts);
60 const _contracts = need.contracts(contracts);
55 const point = need.point(pointCursor);
61 const point = need.point(pointCursor);
56 const { construct, unconstruct, inputsLocked, bind } = useChangeSponsor();
62 const { construct, unconstruct, inputsLocked, bind } = useChangeSponsor();
57
63
64+ const [newSponsor, setNewSponsor] = useState();
65+
66+ const requestNewSponsor = useCallback(async () => {
67+ if (newSponsor !== undefined) {
68+ changeSponsor(newSponsor);
69+ }
70+ }, [newSponsor, changeSponsor]);
71+
58 const validateFormAsync = useCallback(
72 const validateFormAsync = useCallback(
59 async values => {
73 async values => {
60 const sponsor = ob.patp2dec(values.sponsor);
74 const sponsor = ob.patp2dec(values.sponsor);
@@ -92,13 +106,17 @@ function ChangeSponsor({ onDone }) {
92 const onValues = useCallback(
106 const onValues = useCallback(
93 ({ valid, values }) => {
107 ({ valid, values }) => {
94 if (valid) {
108 if (valid) {
95− construct(ob.patp2dec(values.sponsor));
109+ const sponsorPoint = ob.patp2dec(values.sponsor);
110+ construct(sponsorPoint);
111+ setNewSponsor(Number(sponsorPoint));
96 } else {
112 } else {
97 unconstruct();
113 unconstruct();
114+ setNewSponsor(undefined);
98 }
115 }
99 },
116 },
100 [construct, unconstruct]
117 [construct, unconstruct]
101 );
118 );
119+
102 return (
120 return (
103 <Window>
121 <Window>
104 <HeaderPane>
122 <HeaderPane>
@@ -119,13 +137,25 @@ function ChangeSponsor({ onDone }) {
119 className="mv4"
137 className="mv4"
120 />
138 />
121 <Grid.Item full as={FormError} />
139 <Grid.Item full as={FormError} />
140+ {isL2 ? (
122− <Grid.Item
141+ <Grid.Item
142+ as={Button}
123− full
143+ full
144+ className=""
145+ center
146+ solid
147+ onClick={requestNewSponsor}>
148+ {'Request New Sponsor'}
149+ </Grid.Item>
150+ ) : (
151+ <Grid.Item
152+ full
124− as={InlineEthereumTransaction}
153+ as={InlineEthereumTransaction}
125− {...bind}
154+ {...bind}
126− onReturn={onDone}
155+ onReturn={onDone}
127− label="Request"
156+ label="Request"
128− />
157+ />
158+ )}
129 </Box>
159 </Box>
130 )}
160 )}
131 </BridgeForm>
161 </BridgeForm>
src/views/UrbitOS/NetworkingKeys.js+7−5
@@ -8,7 +8,7 @@ import { usePointCursor } from 'store/pointCursor';
8import { usePointCache } from 'store/pointCache';
8import { usePointCache } from 'store/pointCache';
9import { useNetwork } from 'store/network';
9import { useNetwork } from 'store/network';
10import { useWallet } from 'store/wallet';
10import { useWallet } from 'store/wallet';
11−import { useRollerStore } from 'store/roller';
11+import { useRollerStore } from 'store/rollerStore';
12
12
13import useRoller from 'lib/useRoller';
13import useRoller from 'lib/useRoller';
14import { useLocalRouter } from 'lib/LocalRouter';
14import { useLocalRouter } from 'lib/LocalRouter';
@@ -146,7 +146,9 @@ export default function UrbitOSNetworkingKeys({
146 const { pop } = useLocalRouter();
146 const { pop } = useLocalRouter();
147 const { pointCursor } = usePointCursor();
147 const { pointCursor } = usePointCursor();
148 const { getDetails } = usePointCache();
148 const { getDetails } = usePointCache();
149+ const {
150+ point: { isL2 },
149− const { currentL2, currentL2Spawn } = useRollerStore();
151+ } = useRollerStore();
150 const { configureNetworkingKeys, getPendingTransactions } = useRoller();
152 const { configureNetworkingKeys, getPendingTransactions } = useRoller();
151 const [breach, setBreach] = useState(false);
153 const [breach, setBreach] = useState(false);
152
154
@@ -213,7 +215,7 @@ export default function UrbitOSNetworkingKeys({
213 const setNetworkingKeys = useCallback(async () => {
215 const setNetworkingKeys = useCallback(async () => {
214 // setLoading(true);
216 // setLoading(true);
215 try {
217 try {
216− const txHash = await configureNetworkingKeys({
218+ await configureNetworkingKeys({
217 breach,
219 breach,
218 manualNetworkSeed,
220 manualNetworkSeed,
219 });
221 });
@@ -328,7 +330,7 @@ export default function UrbitOSNetworkingKeys({
328 </>
330 </>
329 )}
331 )}
330
332
331− {currentL2 && !currentL2Spawn ? (
333+ {isL2 ? (
332 <Grid.Item
334 <Grid.Item
333 as={Button}
335 as={Button}
334 full
336 full
@@ -336,7 +338,7 @@ export default function UrbitOSNetworkingKeys({
336 center
338 center
337 solid
339 solid
338 onClick={setNetworkingKeys}>
340 onClick={setNetworkingKeys}>
339− {'Sign Transaction'}
341+ {'Reset Networking Keys'}
340 </Grid.Item>
342 </Grid.Item>
341 ) : (
343 ) : (
342 <Grid.Item
344 <Grid.Item