on master help sign in
urbit-id: L2 support for edit management proxy
67d9ca6yosoyubik
migrations/1_migration.js+6
@@ -153,6 +153,12 @@ module.exports = async function(deployer) {
153 // ~marzod -> L1
153 // ~marzod -> L1
154 await ecliptic.spawn(256, own);
154 await ecliptic.spawn(256, own);
155 await ecliptic.configureKeys(256, '0xffff', '0xffff', 1, false);
155 await ecliptic.configureKeys(256, '0xffff', '0xffff', 1, false);
156+ // ~marzod -> L2
157+ await ecliptic.transferPoint(
158+ 256,
159+ '0x1111111111111111111111111111111111111111',
160+ false
161+ );
156 // ~wanzod -> L1
162 // ~wanzod -> L1
157 await ecliptic.spawn(768, own);
163 await ecliptic.spawn(768, own);
158 await ecliptic.configureKeys(768, '0xffff', '0xffff', 1, false);
164 await ecliptic.configureKeys(768, '0xffff', '0xffff', 1, false);
src/lib/useRoller.ts+52−15
@@ -24,7 +24,11 @@ import {
24} from 'store/storage/roller';
24} from 'store/storage/roller';
25import { usePointCache } from 'store/pointCache';
25import { usePointCache } from 'store/pointCache';
26import { getOutgoingPoints } from 'views/Points';
26import { getOutgoingPoints } from 'views/Points';
27+import {
28+ getSpawnNonce,
29+ getOwnerNonce,
30+ getManagementNonce,
27−import { getSpawnNonce, getOwnerNonce } from './utils/nonce';
31+} from './utils/nonce';
28import { isPlanet } from './utils/point';
32import { isPlanet } from './utils/point';
29
33
30import {
34import {
@@ -52,7 +56,7 @@ import {
52 getTimeToNextBatch,
56 getTimeToNextBatch,
53 spawn,
57 spawn,
54 transferPoint,
58 transferPoint,
55− setSpawnProxy,
59+ registerProxyAddress,
56} from './utils/roller';
60} from './utils/roller';
57
61
58function isSpawn(tx: L2Data | undefined): tx is SpawnParams {
62function isSpawn(tx: L2Data | undefined): tx is SpawnParams {
@@ -205,11 +209,12 @@ export default function useRoller() {
205 inviteWallet
209 inviteWallet
206 );
210 );
207
211
208− const setManagementProxyRequest = await setSpawnProxy(
212+ const setManagementProxyRequest = await registerProxyAddress(
209 api,
213 api,
210 _wallet,
214 _wallet,
211 planet,
215 planet,
212 operator.proxy!,
216 operator.proxy!,
217+ 'manage',
213 1,
218 1,
214 inviteWallet.management.keys.address
219 inviteWallet.management.keys.address
215 );
220 );
@@ -481,19 +486,14 @@ export default function useRoller() {
481 // increaseNonce(point, operator.proxy!);
486 // increaseNonce(point, operator.proxy!);
482
487
483 // 2. Set Management Proxy
488 // 2. Set Management Proxy
484− const managementData = { address: manager };
489+ await registerProxyAddress(
485− const managementHash: Hash = await api.hashTransaction(
490+ api,
491+ fromWallet,
492+ point,
493+ operator.proxy!,
494+ 'manage',
486 nonce + 1,
495 nonce + 1,
487− fromOwnerProxy,
496+ manager
488− 'setManagementProxy',
489− managementData
490− );
491−
492− await api.setManagementProxy(
493− signTransactionHash(managementHash, Buffer.from(keysSigningKey, 'hex')),
494− fromOwnerProxy,
495− ownerAddress,
496− managementData
497 );
497 );
498
498
499 // 3. Transfer point
499 // 3. Transfer point
@@ -515,6 +515,42 @@ export default function useRoller() {
515 return transferTxHash;
515 return transferTxHash;
516 };
516 };
517
517
518+ const setProxyAddress = useCallback(
519+ async (proxyType: Proxy, address: EthAddress) => {
520+ console.log(proxyType, address);
521+ const _point = need.point(pointCursor);
522+ const _wallet = wallet.getOrElse(null);
523+
524+ if (!_wallet) {
525+ // not using need because we want a custom error
526+ throw new Error('Internal Error: Missing Wallet');
527+ }
528+
529+ const pointDetails = nonces[_point];
530+ const operator =
531+ getManagementNonce(pointDetails, _wallet.address) ||
532+ getOwnerNonce(pointDetails, _wallet.address);
533+
534+ if (operator === undefined)
535+ throw new Error("Error: Address doesn't match proxy");
536+
537+ const txHash = await registerProxyAddress(
538+ api,
539+ _wallet,
540+ _point,
541+ operator.proxy!,
542+ proxyType,
543+ operator.nonce!,
544+ address
545+ );
546+
547+ const pendingTx = await api.getPendingTx(txHash);
548+
549+ return pendingTx;
550+ },
551+ [api, pointCursor, wallet, nonces]
552+ );
553+
518 // On load, get initial config
554 // On load, get initial config
519 useEffect(() => {
555 useEffect(() => {
520 if (config) {
556 if (config) {
@@ -550,5 +586,6 @@ export default function useRoller() {
550 getPendingTransactions,
586 getPendingTransactions,
551 generateInviteCodes,
587 generateInviteCodes,
552 transferPoint,
588 transferPoint,
589+ setProxyAddress,
553 };
590 };
554}
591}
src/lib/utils/roller.ts+50−5
@@ -2,6 +2,16 @@ import { signTransactionHash } from 'lib/authToken';
2import { Invite } from 'lib/types/Invite';
2import { Invite } from 'lib/types/Invite';
3import { Just, Nothing } from 'folktale/maybe';
3import { Just, Nothing } from 'folktale/maybe';
4import { randomHex } from 'web3-utils';
4import { randomHex } from 'web3-utils';
5+
6+import {
7+ Proxy,
8+ Signature,
9+ From,
10+ EthAddress,
11+ AddressParams,
12+ Ship,
13+} from '@urbit/roller-api';
14+
5import {
15import {
6 deriveNetworkSeedFromUrbitWallet,
16 deriveNetworkSeedFromUrbitWallet,
7 deriveNetworkKeys,
17 deriveNetworkKeys,
@@ -121,11 +131,25 @@ export const transferPoint = async (
121 return api.transferPoint(sig, from, _wallet.address, data);
131 return api.transferPoint(sig, from, _wallet.address, data);
122};
132};
123
133
134+const proxyType = (proxy: Proxy) => {
135+ switch (proxy) {
136+ case 'manage':
137+ return 'setManagementProxy';
138+ case 'spawn':
139+ return 'setSpawnProxy';
140+ case 'transfer':
141+ return 'setTransferProxy';
142+ default:
143+ throw new Error(`Unknown proxyType ${proxy}`);
144+ }
145+};
146+
124−export const setSpawnProxy = async (
147+export const registerProxyAddress = async (
125 api: any,
148 api: any,
126 _wallet: any,
149 _wallet: any,
127− _point: number,
150+ _point: Ship,
128 proxy: string,
151 proxy: string,
152+ proxyAddressType: string,
129 nonce: number,
153 nonce: number,
130 address: string
154 address: string
131) => {
155) => {
@@ -137,11 +161,12 @@ export const setSpawnProxy = async (
137 const managementHash = await api.hashTransaction(
161 const managementHash = await api.hashTransaction(
138 nonce,
162 nonce,
139 from,
163 from,
140− 'setManagementProxy',
164+ proxyType(proxyAddressType),
141 managementData
165 managementData
142 );
166 );
143−
167+ return setProxy(
144− return api.setManagementProxy(
168+ api,
169+ proxyAddressType,
145 signTransactionHash(managementHash, _wallet.privateKey),
170 signTransactionHash(managementHash, _wallet.privateKey),
146 from,
171 from,
147 _wallet.address,
172 _wallet.address,
@@ -149,5 +174,25 @@ export const setSpawnProxy = async (
149 );
174 );
150};
175};
151
176
177+const setProxy = async (
178+ api: any,
179+ proxyAddressType: string,
180+ sig: Signature,
181+ from: From,
182+ address: EthAddress,
183+ data: AddressParams
184+) => {
185+ switch (proxyAddressType) {
186+ case 'manage':
187+ return await api.setManagementProxy(sig, from, address, data);
188+ case 'spawn':
189+ return await api.setSpawnProxy(sig, from, address, data);
190+ case 'transfer':
191+ return await api.setTrasferProxy(sig, from, address, data);
192+ default:
193+ throw new Error(`Unknown proxyType ${proxyAddressType}`);
194+ }
195+};
196+
152export const hasPoint = (point: number) => (invite: Invite) =>
197export const hasPoint = (point: number) => (invite: Invite) =>
153 invite.planet === point;
198 invite.planet === point;
src/views/Point/Point.tsx+31−10
@@ -150,12 +150,12 @@ export default function Point() {
150 })();
150 })();
151
151
152 const address = need.addressFromWallet(wallet);
152 const address = need.addressFromWallet(wallet);
153−
154− const numPending = useMemo(() => {
155− return pendingTransactions.filter(
153+ const spawnedPending = pendingTransactions.filter(
156− ({ rawTx }) => rawTx?.tx?.type === 'spawn'
154+ ({ rawTx }) => rawTx?.tx?.type === 'spawn'
157− ).length;
155+ ).length;
158− }, [pendingTransactions]);
156+ const otherPending = pendingTransactions.filter(
157+ ({ rawTx }) => rawTx?.tx?.type !== 'spawn'
158+ );
159
159
160 return (
160 return (
161 <View
161 <View
@@ -165,18 +165,17 @@ export default function Point() {
165 hideBack
165 hideBack
166 header={
166 header={
167 <L2PointHeader
167 <L2PointHeader
168− hideTimer={!!numPending}
168+ hideTimer={!!spawnedPending}
169 numInvites={invites.length}
169 numInvites={invites.length}
170 hideInvites={!networkKeysSet}
170 hideInvites={!networkKeysSet}
171 />
171 />
172 }>
172 }>
173 <Greeting point={point} />
173 <Greeting point={point} />
174− {!!numPending && (
174+ {!!spawnedPending && (
175 <div className="transaction">
175 <div className="transaction">
176 <Row className="title-row">
176 <Row className="title-row">
177 <div className="title">
177 <div className="title">
178− {numPending} Planet
178+ {spawnedPending} Planet{spawnedPending > 1 ? 's' : ''} Spawned
179− {numPending > 1 ? 's' : ''} Spawned
180 </div>
179 </div>
181 <div className="rollup-timer">
180 <div className="rollup-timer">
182 <Icon icon="Clock" />
181 <Icon icon="Clock" />
@@ -189,6 +188,28 @@ export default function Point() {
189 </Row>
188 </Row>
190 </div>
189 </div>
191 )}
190 )}
191+ {!!otherPending.length &&
192+ otherPending.map(pendingTx => (
193+ <div className="transaction">
194+ <Row className="title-row">
195+ <div className="title">
196+ {pendingTx.rawTx?.tx?.type === 'set-management-proxy'
197+ ? 'Management Address Changed'
198+ : pendingTx.rawTx?.tx?.type === 'configure-keys'
199+ ? 'Network Keys Configured'
200+ : ''}
201+ </div>
202+ <div className="rollup-timer">
203+ <Icon icon="Clock" />
204+ {nextRoll}
205+ </div>
206+ </Row>
207+ <Row className="info-row">
208+ <LayerIndicator layer={2} size="sm" />
209+ <div className="date"></div>
210+ </Row>
211+ </div>
212+ ))}
192 <Passport
213 <Passport
193 point={Just(point)}
214 point={Just(point)}
194 address={Just(address)}
215 address={Just(address)}
src/views/UrbitID/SetProxy.js+64−9
@@ -1,11 +1,14 @@
1−import React, { useCallback, useMemo } from 'react';
1+import React, { useCallback, useMemo, useState } from 'react';
2import cn from 'classnames';
2import cn from 'classnames';
3−import { Grid, Text, Flex } from 'indigo-react';
3+import { Grid, Text, Flex, Button } from 'indigo-react';
4import * as azimuth from 'azimuth-js';
4import * as azimuth from 'azimuth-js';
5
5
6+import './SetProxy.scss';
7+
6import { useNetwork } from 'store/network';
8import { useNetwork } from 'store/network';
7import { usePointCache } from 'store/pointCache';
9import { usePointCache } from 'store/pointCache';
8import { usePointCursor } from 'store/pointCursor';
10import { usePointCursor } from 'store/pointCursor';
11+import { useRollerStore } from 'store/roller';
9
12
10import {
13import {
11 PROXY_TYPE,
14 PROXY_TYPE,
@@ -17,6 +20,7 @@ import { useLocalRouter } from 'lib/LocalRouter';
17import { eqAddr, isZeroAddress } from 'lib/utils/address';
20import { eqAddr, isZeroAddress } from 'lib/utils/address';
18import { capitalize } from 'lib/capitalize';
21import { capitalize } from 'lib/capitalize';
19import useEthereumTransaction from 'lib/useEthereumTransaction';
22import useEthereumTransaction from 'lib/useEthereumTransaction';
23+import useRoller from 'lib/useRoller';
20
24
21import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
25import InlineEthereumTransaction from 'components/InlineEthereumTransaction';
22import { ETH_ZERO_ADDR, GAS_LIMITS } from 'lib/constants';
26import { ETH_ZERO_ADDR, GAS_LIMITS } from 'lib/constants';
@@ -29,6 +33,15 @@ import {
29import BridgeForm from 'form/BridgeForm';
33import BridgeForm from 'form/BridgeForm';
30import FormError from 'form/FormError';
34import FormError from 'form/FormError';
31
35
36+const getL2ProxyName = proxy =>
37+ proxy === PROXY_TYPE.MANAGEMENT
38+ ? 'manage'
39+ : proxy === PROXY_TYPE.SPAWN
40+ ? 'spawn'
41+ : proxy === PROXY_TYPE.TRANSFER
42+ ? 'transfer'
43+ : '';
44+
32const proxyFromDetails = (details, contracts, proxyType) => {
45const proxyFromDetails = (details, contracts, proxyType) => {
33 switch (proxyType) {
46 switch (proxyType) {
34 case PROXY_TYPE.MANAGEMENT:
47 case PROXY_TYPE.MANAGEMENT:
@@ -97,6 +110,9 @@ export default function SetProxy() {
97 const { getDetails } = usePointCache();
110 const { getDetails } = usePointCache();
98 const { pointCursor } = usePointCursor();
111 const { pointCursor } = usePointCursor();
99 const { contracts } = useNetwork();
112 const { contracts } = useNetwork();
113+ const { currentL2 } = useRollerStore();
114+ const { setProxyAddress, getPendingTransactions } = useRoller();
115+ const [newAddress, setNewAddress] = useState('');
100
116
101 const _point = need.point(pointCursor);
117 const _point = need.point(pointCursor);
102 const _details = need.details(getDetails(_point));
118 const _details = need.details(getDetails(_point));
@@ -113,7 +129,30 @@ export default function SetProxy() {
113 bind,
129 bind,
114 } = useSetProxy(data.proxyType);
130 } = useSetProxy(data.proxyType);
115
131
132+ const setProxy = useCallback(async () => {
133+ // setLoading(true);
134+ console.log(newAddress);
135+ if (newAddress === '') return;
136+ try {
137+ await setProxyAddress(getL2ProxyName(data.proxyType), newAddress);
138+ getPendingTransactions(_point);
139+ pop();
140+ } catch (error) {
141+ // setError(error);
142+ } finally {
143+ // setLoading(false);
144+ }
145+ }, [
146+ newAddress,
147+ getPendingTransactions,
148+ _point,
149+ pop,
150+ data.proxyType,
151+ setProxyAddress,
152+ ]);
153+
116 const validateForm = useCallback((values, errors) => {
154 const validateForm = useCallback((values, errors) => {
155+ console.log('hola');
117 if (!values.unset && errors.address) {
156 if (!values.unset && errors.address) {
118 return errors;
157 return errors;
119 }
158 }
@@ -133,18 +172,21 @@ export default function SetProxy() {
133
172
134 const onValues = useCallback(
173 const onValues = useCallback(
135 ({ valid, values, form }) => {
174 ({ valid, values, form }) => {
175+ console.log('currentL2', currentL2);
136 if (valid) {
176 if (valid) {
137 if (values.unset) {
177 if (values.unset) {
138 unset();
178 unset();
139 form.change('address', '');
179 form.change('address', '');
180+ setNewAddress('');
140 } else {
181 } else {
141 construct(values.address);
182 construct(values.address);
183+ setNewAddress(values.address);
142 }
184 }
143 } else {
185 } else {
144 unconstruct();
186 unconstruct();
145 }
187 }
146 },
188 },
147− [construct, unconstruct, unset]
189+ [construct, unconstruct, unset, setNewAddress, currentL2]
148 );
190 );
149
191
150 const initialValues = useMemo(() => ({ unset: false }), []);
192 const initialValues = useMemo(() => ({ unset: false }), []);
@@ -213,12 +255,25 @@ export default function SetProxy() {
213
255
214 <Grid.Item full as={FormError} />
256 <Grid.Item full as={FormError} />
215
257
258+ {currentL2 ? (
216− <Grid.Item
259+ <Grid.Item
260+ as={Button}
217− full
261+ full
262+ className="set-proxy"
263+ center
264+ solid
265+ disabled={values.unset}
266+ onClick={setProxy}>
267+ {'Sign Transaction'}
268+ </Grid.Item>
269+ ) : (
270+ <Grid.Item
271+ full
218− as={InlineEthereumTransaction}
272+ as={InlineEthereumTransaction}
219− {...bind}
273+ {...bind}
220− onReturn={() => pop()}
274+ onReturn={() => pop()}
221− />
275+ />
276+ )}
222 </>
277 </>
223 )}
278 )}
224 </BridgeForm>
279 </BridgeForm>
src/views/UrbitID/SetProxy.scssnew file+57
@@ -0,0 +1,57 @@
1+@import 'style/shared/variables.scss';
2+
3+.cohort {
4+ font-size: 14px;
5+
6+ .number-emphasis {
7+ background-color: $light-gray;
8+ border-radius: $small-border-radius;
9+ margin: 0 4px;
10+ }
11+
12+ .body-pane {
13+ min-height: 212px;
14+ justify-content: space-between;
15+ font-size: 14px;
16+
17+ &.center {
18+ justify-content: center;
19+ }
20+
21+ .content {
22+ width: 100%;
23+ font-size: 14px;
24+ line-height: 24px;
25+
26+ &.center {
27+ text-align: center;
28+ margin-top: 20px;
29+ width: 320px;
30+ margin: auto;
31+ }
32+
33+ }
34+
35+
36+ }
37+
38+.set-proxy {
39+ border-radius: $medium-border-radius;
40+ width: 100%;
41+ height: 32px;
42+ font-size: 14px;
43+ background-color: black;
44+ color: white;
45+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, Roboto, Helvetica,
46+ Arial, sans-serif, 'Apple Color Emoji';
47+ border: none;
48+ margin-top: 8px;
49+ padding: 8px;
50+
51+ &.loading {
52+ background-color: $mid-gray;
53+ }
54+}
55+
56+
57+}