on master help sign in
Updating the spawn invite flow
46c79eaWill Galebach
src/components/L2/Dropdowns/AccountsDropdown.tsx−1
@@ -1,6 +1,5 @@
1import React, { useCallback, useState } from 'react';
1import React, { useCallback, useState } from 'react';
2import * as ob from 'urbit-ob';
2import * as ob from 'urbit-ob';
3−import { azimuth } from 'azimuth-js';
4import * as need from 'lib/need';
3import * as need from 'lib/need';
5import { Icon, Row, Box, Button } from '@tlon/indigo-react';
4import { Icon, Row, Box, Button } from '@tlon/indigo-react';
6import { Just } from 'folktale/maybe';
5import { Just } from 'folktale/maybe';
src/components/L2/LayerIndicator.scss+4
@@ -16,6 +16,8 @@
16 height: 16px;
16 height: 16px;
17 padding: 2px 4px 0;
17 padding: 2px 4px 0;
18 font-size: 12px;
18 font-size: 12px;
19+ width: 14px;
20+ text-align: center;
19 }
21 }
20
22
21 &.md {
23 &.md {
@@ -23,5 +25,7 @@
23 padding: 4px 4px 0;
25 padding: 4px 4px 0;
24 font-size: 14px;
26 font-size: 14px;
25 margin-right: 4px;
27 margin-right: 4px;
28+ width: 18px;
29+ text-align: center;
26 }
30 }
27}
31}
src/lib/useRoller.ts+149−26
@@ -3,6 +3,15 @@ import * as wg from 'lib/walletgen';
3import * as need from 'lib/need';
3import * as need from 'lib/need';
4import * as azimuth from 'azimuth-js';
4import * as azimuth from 'azimuth-js';
5import * as ob from 'urbit-ob';
5import * as ob from 'urbit-ob';
6+import { Just } from 'folktale/maybe';
7+import { randomHex } from 'web3-utils';
8+
9+import {
10+ attemptNetworkSeedDerivation,
11+ deriveNetworkKeys,
12+ CRYPTO_SUITE_VERSION,
13+} from 'lib/keys';
14+import { addHexPrefix } from 'lib/utils/address';
6
15
7import {
16import {
8 Config,
17 Config,
@@ -33,6 +42,97 @@ import { usePointCache } from 'store/pointCache';
33import { getOutgoingPoints, maybeGetResult } from 'views/Points';
42import { getOutgoingPoints, maybeGetResult } from 'views/Points';
34import { isPlanet } from './utils/point';
43import { isPlanet } from './utils/point';
35
44
45+const spawn = async (
46+ api: any,
47+ _wallet: any,
48+ _point: number,
49+ proxy: string,
50+ nonce: number,
51+ planet: number
52+) => {
53+ const from = {
54+ ship: _point, //ship that is spawning the planet
55+ proxy,
56+ };
57+
58+ const data = {
59+ address: _wallet.address,
60+ ship: planet, // ship to spawn
61+ };
62+
63+ const hash = await api.hashTransaction(nonce, from, 'spawn', data);
64+ const sSig = signTransactionHash(hash, _wallet.privateKey);
65+ return api.spawn(sSig, from, _wallet.address, data);
66+};
67+
68+const configureKeys = async (
69+ api: any,
70+ _wallet: any,
71+ _point: number,
72+ proxy: string,
73+ nonce: number,
74+ details: any,
75+ authToken: string,
76+ authMnemonic: string,
77+ wallet: any,
78+ urbitWallet: any
79+) => {
80+ const from = {
81+ ship: _point, //ship that is spawning the planet
82+ proxy,
83+ };
84+
85+ const networkSeed = await attemptNetworkSeedDerivation({
86+ urbitWallet,
87+ wallet,
88+ authMnemonic,
89+ details,
90+ point: _point,
91+ authToken,
92+ revision: 1,
93+ });
94+
95+ const seed = Just.hasInstance(networkSeed)
96+ ? networkSeed.value
97+ : randomHex(32);
98+
99+ const pair = deriveNetworkKeys(seed);
100+
101+ const data = {
102+ encrypt: addHexPrefix(pair.crypt.public),
103+ auth: addHexPrefix(pair.auth.public),
104+ cryptoSuite: String(CRYPTO_SUITE_VERSION),
105+ breach: false,
106+ };
107+
108+ const hash = await api.hashTransaction(nonce, from, 'configureKeys', data);
109+ const sig = signTransactionHash(hash, _wallet.privateKey);
110+ return api.configureKeys(sig, from, _wallet.address, data);
111+};
112+
113+const transferPoint = async (
114+ api: any,
115+ _wallet: any,
116+ _point: number,
117+ proxy: string,
118+ nonce: number,
119+ address: string
120+) => {
121+ const from = {
122+ ship: _point, //ship that is spawning the planet
123+ proxy,
124+ };
125+
126+ const data = {
127+ address,
128+ reset: false,
129+ };
130+
131+ const hash = await api.hashTransaction(nonce, from, 'transferPoint', data);
132+ const sig = signTransactionHash(hash, _wallet.privateKey);
133+ return api.transferPoint(sig, from, _wallet.address, data);
134+};
135+
36const hasPoint = (point: number) => (invite: Invite) => invite.planet === point;
136const hasPoint = (point: number) => (invite: Invite) => invite.planet === point;
37
137
38const getProxyAndNonce = (
138const getProxyAndNonce = (
@@ -55,7 +155,7 @@ const getProxyAndNonce = (
55 : { proxy: undefined, nonce: undefined };
155 : { proxy: undefined, nonce: undefined };
56
156
57export default function useRoller() {
157export default function useRoller() {
58− const { wallet, authToken }: any = useWallet();
158+ const { wallet, authToken, authMnemonic, urbitWallet }: any = useWallet();
59 const { pointCursor }: any = usePointCursor();
159 const { pointCursor }: any = usePointCursor();
60 const { web3, contracts }: any = useNetwork();
160 const { web3, contracts }: any = useNetwork();
61 const allPoints: any = usePointCache();
161 const allPoints: any = usePointCache();
@@ -119,7 +219,8 @@ export default function useRoller() {
119 const _web3 = web3.getOrElse(null);
219 const _web3 = web3.getOrElse(null);
120 const _wallet = wallet.getOrElse(null);
220 const _wallet = wallet.getOrElse(null);
121 const _authToken = authToken.getOrElse(null);
221 const _authToken = authToken.getOrElse(null);
222+ const _details = getDetails(_point);
122− if (!_contracts || !_web3 || !_wallet || !_authToken) {
223+ if (!_contracts || !_web3 || !_wallet || !_authToken || !_details) {
123 // not using need because we want a custom error
224 // not using need because we want a custom error
124 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
225 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
125 }
226 }
@@ -137,31 +238,45 @@ export default function useRoller() {
137
238
138 for (let i = 0; i < numInvites && planets[i]; i++) {
239 for (let i = 0; i < numInvites && planets[i]; i++) {
139 const planet = planets[i];
240 const planet = planets[i];
241+ const nonceInc = i * 3;
140
242
141 const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
243 const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
142 planet,
244 planet,
143 _authToken
245 _authToken
144 );
246 );
145
247
146− const from = {
248+ const spawnRequest = await spawn(
147− ship: _point, //ship that is spawning the planet
249+ api,
250+ _wallet,
251+ _point,
148 proxy,
252 proxy,
149− };
253+ nonceInc,
150−
151− const data = {
152− address: owner.keys.address, // the new owner of the star (invite wallet)
153− ship: planet, // ship to spawn
254+ planet
154− };
155−
156− const txHash = await api.hashTransaction(
157− nonce + i,
158− from,
159− 'spawn',
160− data
161 );
255 );
162
256
163− const signature = signTransactionHash(txHash, _wallet.privateKey);
257+ const configureKeysRequest = await configureKeys(
164− requests.push(api.spawn(signature, from, _wallet.address, data));
258+ api,
259+ _wallet,
260+ _point,
261+ proxy,
262+ nonceInc + 1,
263+ _details,
264+ authToken,
265+ authMnemonic,
266+ wallet,
267+ urbitWallet
268+ );
269+
270+ const transferPointRequest = await transferPoint(
271+ api,
272+ _wallet,
273+ _point,
274+ proxy,
275+ nonceInc + 2,
276+ owner.keys.address
277+ );
278+
279+ requests.push(spawnRequest, configureKeysRequest, transferPointRequest);
165 tickets.push({
280 tickets.push({
166 ticket,
281 ticket,
167 planet,
282 planet,
@@ -170,13 +285,13 @@ export default function useRoller() {
170 }
285 }
171
286
172 const hashes = await Promise.all(requests);
287 const hashes = await Promise.all(requests);
288+
173− const pendingInvites = hashes.map(
289+ const pendingInvites = tickets.map((ticket, ind) => ({
174− (hash, i): Invite => ({
290+ ...ticket,
175− hash,
291+ hash: hashes[ind * 3 + 2],
176− ...tickets[i],
177− status: 'pending',
292+ status: 'pending',
178− })
293+ }));
179− );
294+
180 setPendingInvites(_point, pendingInvites);
295 setPendingInvites(_point, pendingInvites);
181 },
296 },
182 [
297 [
@@ -188,6 +303,9 @@ export default function useRoller() {
188 // walletHdPath,
303 // walletHdPath,
189 // walletType,
304 // walletType,
190 web3,
305 web3,
306+ getDetails,
307+ authMnemonic,
308+ urbitWallet,
191 ]
309 ]
192 );
310 );
193
311
@@ -240,9 +358,14 @@ export default function useRoller() {
240 const completed = !pendingTransactions.find(
358 const completed = !pendingTransactions.find(
241 p => `~${p?.rawTx?.tx?.tx?.data?.ship}` === ob.patp(invite.planet)
359 p => `~${p?.rawTx?.tx?.tx?.data?.ship}` === ob.patp(invite.planet)
242 );
360 );
361+
362+ if (
243− if (completed) {
363+ completed &&
364+ !availableInvites.find(({ ship }) => invite.ship === ship)
365+ ) {
244 availableInvites.push({ ...invite, status: 'available' });
366 availableInvites.push({ ...invite, status: 'available' });
245 }
367 }
368+
246 return !completed;
369 return !completed;
247 });
370 });
248
371
src/store/lib/useControlledPointsStore.js+2
@@ -53,8 +53,10 @@ export default function useControlledPointsStore() {
53 azimuth.azimuth.getSpawningFor(_contracts, address),
53 azimuth.azimuth.getSpawningFor(_contracts, address),
54 ]);
54 ]);
55
55
56+ console.log(1)
56 const pointsWithLayers = await Promise.all(
57 const pointsWithLayers = await Promise.all(
57 ownedPoints.map(async ship => {
58 ownedPoints.map(async ship => {
59+ console.log(2)
58 try {
60 try {
59 const pointInfo = await api.getPoint(Number(ship));
61 const pointInfo = await api.getPoint(Number(ship));
60 return {
62 return {
src/views/Point/MigrateL2.tsx+11−5
@@ -1,6 +1,6 @@
1import React, { useState, useEffect, useCallback } from 'react';
1import React, { useState, useEffect, useCallback } from 'react';
2import { Grid } from 'indigo-react';
2import { Grid } from 'indigo-react';
3−import { azimuth, ecliptic } from 'azimuth-js';
3+import { ecliptic } from 'azimuth-js';
4import * as ob from 'urbit-ob';
4import * as ob from 'urbit-ob';
5
5
6import View from 'components/View';
6import View from 'components/View';
@@ -83,21 +83,27 @@ const useMigrate = () => {
83export default function MigrateL2() {
83export default function MigrateL2() {
84 const { pop }: any = useLocalRouter();
84 const { pop }: any = useLocalRouter();
85 const { pointCursor }: any = usePointCursor();
85 const { pointCursor }: any = usePointCursor();
86− const point = need.point(pointCursor);
87 const { controlledPoints }: any = usePointCache();
86 const { controlledPoints }: any = usePointCache();
88
87
89 const [proceed, setProceed] = useState(false);
88 const [proceed, setProceed] = useState(false);
90 const [hideMessage, setHideMessage] = useState(false);
89 const [hideMessage, setHideMessage] = useState(false);
91− const [selectedPoint, setSelectedPoint] = useState(point);
92 const [showDropdown, setShowDropdown] = useState(false);
90 const [showDropdown, setShowDropdown] = useState(false);
93 const [transfer, setTransfer] = useState(true);
91 const [transfer, setTransfer] = useState(true);
94
92
95 const points = controlledPoints?.value?.value?.pointsWithLayers || [];
93 const points = controlledPoints?.value?.value?.pointsWithLayers || [];
96− const l1Points = points.filter(({ point, layer }: PointLayer) => layer === 1);
94+ const l1Points = points.filter(({ layer }: PointLayer) => layer === 1);
95+
96+ let { point } =
97+ l1Points.find((p: any) => isStar(p.point)) || controlledPoints[0];
97
98
99+ try {
100+ point = need.point(pointCursor);
101+ } catch (e) {}
102+
103+ const [selectedPoint, setSelectedPoint] = useState(point);
98 const hideInfo = getHideMigrationMessage();
104 const hideInfo = getHideMigrationMessage();
99
105
100− const { construct, unconstruct, completed, bind } = useMigrate();
106+ const { construct, unconstruct, bind, completed } = useMigrate();
101
107
102 useEffect(() => {
108 useEffect(() => {
103 if (point) {
109 if (point) {
src/views/Point/Point.js+3−2
@@ -162,7 +162,6 @@ export default function Point() {
162 console.log('POINT INFO', pointInfo);
162 console.log('POINT INFO', pointInfo);
163 }
163 }
164 setCurrentPoint(pointInfo);
164 setCurrentPoint(pointInfo);
165−
166 getInvites(isL2(pointInfo.dominion));
165 getInvites(isL2(pointInfo.dominion));
167 };
166 };
168
167
@@ -235,7 +234,9 @@ export default function Point() {
235 useSyncExtras([point]);
234 useSyncExtras([point]);
236
235
237 const address = need.addressFromWallet(wallet);
236 const address = need.addressFromWallet(wallet);
238− const numPending = pendingTransactions.length;
237+ const numPending = pendingTransactions.filter(
238+ ({ rawTx }) => rawTx?.tx?.tx?.type === 'spawn'
239+ ).length;
239
240
240 return (
241 return (
241 <View
242 <View