@@ -1,4 +1,5 @@
11import { useCallback, useEffect, useMemo, useState } from 'react';
2+import _ from 'lodash';
23import * as azimuth from 'azimuth-js';
34import * as need from 'lib/need';
45import * as ob from 'urbit-ob';
@@ -7,17 +8,24 @@ import { Just } from 'folktale/maybe';
78import { Invite } from 'lib/types/Invite';
89import { convertToInt } from './convertToInt';
910import { ensureHexPrefix } from 'form/formatters';
10−import { getOutgoingPoints } from 'views/Points';
1111import { isDevelopment, isRopsten } from './flags';
12−import { isPlanet } from './utils/point';
1312import { hasPoint, generateInviteWallet } from './utils/roller';
1413import { ROLLER_HOSTS } from './constants';
1514import { signTransactionHash } from './authToken';
15+
1616import { useNetwork } from 'store/network';
17−import { usePointCache } from 'store/pointCache';
1817import { usePointCursor } from 'store/pointCursor';
1918import { useRollerStore } from 'store/roller';
2019import { useWallet } from 'store/wallet';
20+import {
21+ getStoredInvites,
22+ setPendingInvites,
23+ setStoredInvites,
24+} from 'store/storage/roller';
25+import { usePointCache } from 'store/pointCache';
26+import { getOutgoingPoints } from 'views/Points';
27+import { getSpawnNonce, getOwnerNonce } from './utils/nonce';
28+import { isPlanet } from './utils/point';
2129
2230import {
2331 deriveNetworkKeys,
@@ -28,7 +36,6 @@ import {
2836import {
2937 Config,
3038 Ship,
31− L2Point,
3239 Proxy,
3340 RollerRPCAPI,
3441 Options,
@@ -36,6 +43,8 @@ import {
3643 UnspawnedPoints,
3744 From,
3845 Hash,
46+ SpawnParams,
47+ L2Data,
3948} from '@urbit/roller-api';
4049
4150import {
@@ -43,33 +52,16 @@ import {
4352 getTimeToNextBatch,
4453 spawn,
4554 transferPoint,
55+ setSpawnProxy,
4656} from './utils/roller';
4757
48−import {
49− getStoredInvites,
50− setPendingInvites,
51− setStoredInvites,
52−} from 'store/storage/roller';
58+function isSpawn(tx: L2Data | undefined): tx is SpawnParams {
59+ return (tx as SpawnParams) !== undefined;
60+}
5361
54−const getProxyAndNonce = (
55− point: L2Point,
56− address: string
57−): { proxy?: string; nonce?: number } => {
58− return point.ownership?.managementProxy?.address === address
59− ? { proxy: 'manage', nonce: point.ownership?.managementProxy.nonce }
60− : point.ownership?.owner?.address === address
61− ? { proxy: 'own', nonce: point.ownership?.owner.nonce }
62− : point.ownership?.spawnProxy?.address === address
63− ? { proxy: 'spawn', nonce: point.ownership?.spawn?.owner.nonce }
64− : point.ownership?.votingProxy?.address === address
65− ? { proxy: 'vote', nonce: point.ownership?.votingProxy?.owner.nonce }
66− : point.ownership?.transferProxy?.address === address
67− ? {
68− proxy: 'transfer',
69− nonce: point.transferProxy?.votingProxy?.owner.nonce,
70− }
71− : { proxy: undefined, nonce: undefined };
72−};
62+function isShipNumber(ship: number | string | undefined): ship is number {
63+ return (ship as number) !== undefined;
64+}
7365
7466export default function useRoller() {
7567 const { wallet, authToken, authMnemonic, urbitWallet }: any = useWallet();
@@ -86,6 +78,8 @@ export default function useRoller() {
8678 setPendingTransactions,
8779 setInvites,
8880 currentL2,
81+ nonces,
82+ increaseNonce,
8983 } = useRollerStore();
9084 const [config, setConfig] = useState<Config | null>(null);
9185
@@ -137,6 +131,7 @@ export default function useRoller() {
137131 const _wallet = wallet.getOrElse(null);
138132 const _authToken = authToken.getOrElse(null);
139133 const _details = getDetails(_point);
134+
140135 if (!_contracts || !_web3 || !_wallet || !_authToken || !_details) {
141136 // not using need because we want a custom error
142137 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
@@ -147,9 +142,27 @@ export default function useRoller() {
147142 "Auth key Error: A management mnemonic can't create invite codes"
148143 );
149144 }
150− const planets: UnspawnedPoints = await api.getUnspawned(_point);
151− const starInfo = await api.getPoint(_point);
145+ let planets: UnspawnedPoints = await api.getUnspawned(_point);
146+
147+ const pendingTxs = await api.getPendingByShip(_point);
148+
149+ const pendingSpawns = _.reduce(
150+ pendingTxs,
151+ (acc, pending) => {
152+ return isSpawn(pending.rawTx?.tx) &&
153+ isShipNumber(pending.rawTx?.tx.data.ship)
154+ ? acc.add(pending.rawTx?.tx.data.ship!)
155+ : acc;
156+ },
157+ new Set<number>()
158+ );
152159
160+ console.log(pendingTxs, pendingSpawns);
161+
162+ planets = planets.filter((point: number) => !pendingSpawns.has(point));
163+
164+ const starInfo = nonces[_point];
165+ console.log(starInfo);
153166 const tickets: {
154167 ticket: string;
155168 planet: number;
@@ -157,37 +170,37 @@ export default function useRoller() {
157170 }[] = [];
158171 const requests: Promise<string>[] = [];
159172
160− // TODO: replace with "currentNonce" from store, that would reflect the latest nonce
161− // Bridge has used for the star _wallet.address
162− //
163− const { proxy, nonce } = getProxyAndNonce(starInfo, _wallet.address);
173+ const operator =
174+ getSpawnNonce(starInfo, _wallet.address) ||
175+ getOwnerNonce(starInfo, _wallet.address);
164176
165− if (!(proxy === 'own' || proxy === 'spawn') || nonce === undefined)
177+ if (operator === undefined)
166178 throw new Error("Error: Address doesn't match proxy");
167−
179+ console.log('operator', operator);
168180 for (let i = 0; i < numInvites && planets[i]; i++) {
169181 const planet = planets[i];
170− const nonceInc = i + nonce;
182+ const nonceInc = i + operator.nonce!;
171183
172− const { ticket, owner } = await generateInviteWallet(
184+ const { ticket, inviteWallet } = await generateInviteWallet(
173185 planet,
174186 _authToken
175187 );
176−
188+ console.log(inviteWallet);
177189 const spawnRequest = await spawn(
178190 api,
179191 _wallet,
180192 _point,
181− proxy,
193+ operator.proxy!,
182194 nonceInc,
183195 planet
184196 );
197+ increaseNonce(_point, operator.proxy!);
185198
186199 const configureKeysRequest = await configureKeys(
187200 api,
188201 _wallet,
189202 planet,
190− proxy,
203+ operator.proxy!,
191204 0,
192205 _details,
193206 authToken,
@@ -196,20 +209,34 @@ export default function useRoller() {
196209 urbitWallet
197210 );
198211
199− const transferPointRequest = await transferPoint(
212+ const setManagementProxyRequest = await setSpawnProxy(
200213 api,
201214 _wallet,
202215 planet,
203− proxy,
216+ operator.proxy!,
204217 1,
205− owner.keys.address
218+ inviteWallet.management.keys.address
206219 );
207220
208− requests.push(spawnRequest, configureKeysRequest, transferPointRequest);
221+ const transferPointRequest = await transferPoint(
222+ api,
223+ _wallet,
224+ planet,
225+ operator.proxy!,
226+ 2,
227+ inviteWallet.ownership.keys.address
228+ );
229+
230+ requests.push(
231+ spawnRequest,
232+ configureKeysRequest,
233+ setManagementProxyRequest,
234+ transferPointRequest
235+ );
209236 tickets.push({
210237 ticket,
211238 planet,
212− owner: owner.keys.address,
239+ owner: inviteWallet.ownership.keys.address,
213240 });
214241 }
215242
@@ -221,6 +248,7 @@ export default function useRoller() {
221248 status: 'pending',
222249 }));
223250
251+ console.log('latest update', nonces[_point]);
224252 setPendingInvites(_point, pendingInvites);
225253 },
226254 [
@@ -233,6 +261,8 @@ export default function useRoller() {
233261 getDetails,
234262 authMnemonic,
235263 urbitWallet,
264+ nonces,
265+ increaseNonce,
236266 ]
237267 );
238268
@@ -343,7 +373,7 @@ export default function useRoller() {
343373 const planet = possibleMissingInvites[i];
344374 if (!availableInvites.find(hasPoint(planet))) {
345375 console.log('MISSING IN AVAILABLE', planet);
346− const { ticket, owner } = await generateInviteWallet(
376+ const { ticket, inviteWallet } = await generateInviteWallet(
347377 planet,
348378 _authToken
349379 );
@@ -353,7 +383,7 @@ export default function useRoller() {
353383 status: 'available',
354384 planet,
355385 hash: '',
356− owner: owner.keys.address,
386+ owner: inviteWallet.ownership.keys.address,
357387 });
358388 }
359389 }
@@ -371,14 +401,14 @@ export default function useRoller() {
371401 }
372402 },
373403 [
374− api,
375− pointCursor,
376− setInvites,
377− setPendingTransactions,
378− authToken,
379− contracts,
380− controlledPoints,
381− getDetails,
404+ // api,
405+ // pointCursor,
406+ // setInvites,
407+ // setPendingTransactions,
408+ // authToken,
409+ // contracts,
410+ // controlledPoints,
411+ // getDetails,
382412 ]
383413 );
384414
@@ -394,6 +424,7 @@ export default function useRoller() {
394424 interface AcceptInviteParams {
395425 point: Ship;
396426 to: EthAddress;
427+ manager: EthAddress;
397428 fromWallet: any; // TODO: wallet type
398429 toWallet: any; // TODO: wallet type
399430 }
@@ -401,6 +432,7 @@ export default function useRoller() {
401432 const acceptInvite = async ({
402433 point,
403434 to,
435+ manager,
404436 fromWallet,
405437 toWallet,
406438 }: AcceptInviteParams) => {
@@ -408,10 +440,14 @@ export default function useRoller() {
408440 console.log('azimuthPoint', azimuthPoint);
409441 const ownerAddress = azimuthPoint?.ownership?.owner?.address!;
410442
411− const { nonce } = getProxyAndNonce(azimuthPoint, ownerAddress);
412− if (nonce === undefined) {
413− throw new Error('Nonce unavailable');
414− }
443+ const operator =
444+ azimuthPoint.ownership &&
445+ getOwnerNonce(azimuthPoint.ownership, ownerAddress);
446+
447+ if (operator === undefined)
448+ throw new Error("Error: Address doesn't match proxy");
449+
450+ const nonce = operator.nonce!;
415451
416452 // 1. Update networking keys
417453 // TODO: extract this to a useRoller#configureKeys function?
@@ -445,11 +481,30 @@ export default function useRoller() {
445481 ownerAddress,
446482 keysData
447483 );
484+ // TODO: not needed? next time we operate with this point we will login, and get
485+ // the latest nonce (updated with any nonce increase from possible pending txs)
486+ // increaseNonce(point, operator.proxy!);
448487
449− // 2. Transfer point
488+ // 2. Set Management Proxy
489+ const managementData = { address: manager };
490+ const managementHash: Hash = await api.hashTransaction(
491+ nonce + 1,
492+ fromOwnerProxy,
493+ 'setManagementProxy',
494+ managementData
495+ );
496+
497+ await api.setManagementProxy(
498+ signTransactionHash(managementHash, Buffer.from(keysSigningKey, 'hex')),
499+ fromOwnerProxy,
500+ ownerAddress,
501+ managementData
502+ );
503+
504+ // 3. Transfer point
450505 const transferData = { address: to, reset: false };
451506 const transferHash: Hash = await api.hashTransaction(
452− nonce + 1,
507+ nonce + 2,
453508 fromOwnerProxy,
454509 'transferPoint',
455510 transferData