1import { useCallback, useEffect, useMemo, useState } from 'react';
3import * as azimuth from 'azimuth-js';
4import * as need from 'lib/need';
5import { Just } from 'folktale/maybe';
7import { Invite } from 'lib/types/Invite';
8import { convertToInt } from './convertToInt';
9import { isDevelopment, isRopsten } from './flags';
10import { hasInvite, generateInviteWallet } from './utils/roller';
11import { INVITES_PER_PAGE, ROLLER_HOSTS } from './constants';
13import { useNetwork } from 'store/network';
14import { usePointCursor } from 'store/pointCursor';
15import { useRollerStore } from 'store/roller';
16import { useWallet } from 'store/wallet';
17import { getStoredInvites, setStoredInvites } from 'store/storage/roller';
18import { usePointCache } from 'store/pointCache';
19import { getOutgoingPoints } from 'views/Points';
25} from './utils/proxy';
26import { isPlanet } from './utils/point';
29 deriveNetworkSeedFromUrbitWallet,
30 attemptNetworkSeedDerivation,
43} from '@urbit/roller-api';
51} from './utils/roller';
53function isSpawn(tx: L2Data | undefined): tx is SpawnParams {
54 return (tx as SpawnParams) !== undefined;
57function isShipNumber(ship: number | string | undefined): ship is number {
58 return (ship as number) !== undefined;
61const inviteTemplate = (
73export default function useRoller() {
74 const { wallet, authToken, authMnemonic, urbitWallet }: any = useWallet();
75 const { pointCursor }: any = usePointCursor();
76 const { web3, contracts }: any = useNetwork();
77 const allPoints: any = usePointCache();
78 const controlledPoints = allPoints?.controlledPoints;
79 const getDetails = allPoints?.getDetails;
87 setPendingTransactions,
90 setInviteGeneratingNum,
92 const [config, setConfig] = useState<Config | null>(null);
94 const options: Options = useMemo(() => {
95 const type = isRopsten || !isDevelopment ? 'https' : 'http';
96 const host = isRopsten
97 ? ROLLER_HOSTS.ROPSTEN
100 : ROLLER_HOSTS.MAINNET;
101 const port = isRopsten ? 443 : 8080;
102 const path = '/v1/roller';
114 const api = useMemo(() => {
115 return new RollerRPCAPI(options);
118 const fetchConfig = useCallback(async () => {
123 setNextBatchTime(response.nextBatch);
126 // TODO: more elegant error handling
128 '[fetchConfig:failed] check the roller connection?\n',
132 }, [api]); // eslint-disable-line react-hooks/exhaustive-deps
134 const generateInviteCodes = useCallback(
135 async (numInvites: number) => {
136 const _point = need.point(pointCursor);
137 const _contracts = contracts.getOrElse(null);
138 const _web3 = web3.getOrElse(null);
139 const _wallet = wallet.getOrElse(null);
140 const _authToken = authToken.getOrElse(null);
141 const _details = getDetails(_point);
143 if (!_contracts || !_web3 || !_wallet || !_authToken || !_details) {
144 // not using need because we want a custom error
145 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
148 if (Just.hasInstance(authMnemonic)) {
150 "Auth key Error: A management mnemonic can't create invite codes"
153 let planets: UnspawnedPoints = await api.getUnspawned(_point);
155 const pendingTxs = await api.getPendingByShip(_point);
157 const pendingSpawns = _.reduce(
160 return isSpawn(pending.rawTx?.tx) &&
161 isShipNumber(pending.rawTx?.tx.data.ship)
162 ? acc.add(pending.rawTx?.tx.data.ship!)
168 planets = planets.filter((point: number) => !pendingSpawns.has(point));
170 const starInfo = await api.getPoint(_point);
176 const requests: Promise<string>[] = [];
178 const proxy = getSpawnProxy(starInfo.ownership!, _wallet.address);
180 if (proxy === undefined)
181 throw new Error("Error: Address doesn't match proxy");
183 const nonce = await api.getNonce({
188 for (let i = 0; i < numInvites && planets[i]; i++) {
189 setInviteGeneratingNum(i + 1);
190 const planet = planets[i];
191 const nonceInc = i + nonce;
193 const { ticket, inviteWallet } = await generateInviteWallet(
198 const spawnRequest = spawn(
207 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
211 const configureKeysRequest = configureKeys(
220 const setManagementProxyRequest = registerProxyAddress(
227 inviteWallet.management.keys.address
230 const transferRequest = transferPointRequest(
236 inviteWallet.ownership.keys.address
241 configureKeysRequest,
242 setManagementProxyRequest,
245 await Promise.all(requests);
250 owner: inviteWallet.ownership.keys.address,
263 setInviteGeneratingNum,
267 const getPoints = useCallback(
268 async (proxy: Proxy, address: EthAddress) => {
269 const points: Ship[] =
271 ? await api.getOwnedPoints(address)
273 ? await api.getManagerFor(address)
275 ? await api.getVotingFor(address)
276 : proxy === 'transfer'
277 ? await api.getTransferringFor(address)
279 ? await api.getSpawningFor(address)
287 const getPendingTransactions = useCallback(async () => {
289 const curPoint = Number(need.point(pointCursor));
290 const newPending = await api.getPendingByShip(curPoint);
292 setPendingTransactions(newPending);
294 // const allTransactions = await api.getHistory()
296 console.warn('ERROR GETTING PENDING', error);
298 }, [api, setPendingTransactions, pointCursor]);
300 const getInvites = useCallback(
301 async (isL2: boolean, page: number, getAll = false) => {
303 const curPoint: number = Number(need.point(pointCursor));
304 const storedInvites = getStoredInvites(curPoint);
305 if (storedInvites.length) {
306 setInvites(storedInvites);
308 setInvites(invitePoints.map(p => inviteTemplate(p)));
311 const _authToken = authToken.getOrElse(null);
312 const _contracts = contracts.getOrElse(null);
314 if (_authToken && _contracts) {
315 const spawnedPoints = isL2 ? await api.getSpawned(curPoint) : [];
317 const availablePoints = await azimuth.azimuth.getUnspawnedChildren(
322 const outgoingPoints = getOutgoingPoints(
325 ).filter((p: number) => isPlanet(p) && availablePoints.includes(p));
327 const invitePlanets = spawnedPoints.concat(outgoingPoints);
330 console.log('INVITED PLANETS', invitePlanets);
333 const newInvites: Invite[] = [];
334 // Iterate over all of the stored invites, generating wallet info as necessary
335 for (let i = 0; i < invitePlanets.length; i++) {
336 setInviteGeneratingNum(i + 1);
337 const planet = invitePlanets[i];
338 const invite = storedInvites.find(hasInvite(planet));
340 const start = page * INVITES_PER_PAGE;
341 const end = start + INVITES_PER_PAGE;
344 newInvites.push(invite);
345 } else if (getAll || (i >= start && i < end)) {
347 console.log('MISSING INVITE INFO', planet);
349 const { ticket, inviteWallet } = await generateInviteWallet(
358 inviteWallet.ownership.keys.address
362 newInvites.push(inviteTemplate(planet));
366 setStoredInvites(curPoint, newInvites);
367 setInvites(newInvites);
372 console.warn('ERROR GETTING INVITES', error);
384 setInviteGeneratingNum,
388 const getNumInvites = useCallback(
389 async (isL2: boolean) => {
391 const curPoint: number = Number(need.point(pointCursor));
392 const _authToken = authToken.getOrElse(null);
393 const _contracts = contracts.getOrElse(null);
395 if (_authToken && _contracts) {
396 let invitePoints = isL2 ? await api.getSpawned(curPoint) : [];
398 const availablePoints = await azimuth.azimuth.getUnspawnedChildren(
403 const outgoingPoints = getOutgoingPoints(
406 ).filter((p: number) => isPlanet(p) && availablePoints.includes(p));
408 setInvitePoints(invitePoints.concat(outgoingPoints));
427 interface ConfigureKeysParams {
429 customNetworkSeed?: string;
432 const configureNetworkingKeys = async ({
435 }: ConfigureKeysParams) => {
436 const _point = need.point(pointCursor);
437 const azimuthPoint = await api.getPoint(_point);
438 const _wallet = wallet.getOrElse(null);
439 const _details = getDetails(_point);
440 if (!_wallet || !_details) {
441 // not using need because we want a custom error
442 throw new Error('Internal Error: Missing Wallet/Details');
445 const pointDetails = await api.getPoint(_point);
446 const proxy = getManagerProxy(pointDetails.ownership!, _wallet.address);
448 if (proxy === undefined)
449 throw new Error("Error: Address doesn't match proxy");
451 const nonce = await api.getNonce({
456 const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10);
457 const nextRevision = networkRevision + 1;
458 const networkSeed = customNetworkSeed
460 : await attemptNetworkSeedDerivation({
467 revision: nextRevision,
469 const txHash = await configureKeys(
478 const pendingTx = await api.getPendingTx(txHash);
483 interface ReticketParams {
487 fromWallet: any; // TODO: wallet type
488 toWallet: any; // TODO: wallet type
491 const performL2Reticket = async ({
497 }: ReticketParams) => {
498 const azimuthPoint = await api.getPoint(point);
500 let nonce = await api.getNonce({ ship: point, proxy });
504 // 1. Update networking keys
505 const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10);
506 const nextRevision = networkRevision + 1;
507 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
511 const configureKeysRequest = await configureKeys(
520 requests.push(configureKeysRequest);
522 // 2. Set Management Proxy
523 const registerMgmtRequest = await registerProxyAddress(
533 requests.push(registerMgmtRequest);
535 // 3. Set Spawn Proxy
536 if (!isPlanet(Number(point))) {
537 const registerSpawnRequest = await registerProxyAddress(
544 toWallet.spawn.keys.address
547 requests.push(registerSpawnRequest);
550 const transferTxRequest = await transferPointRequest(
558 requests.push(transferTxRequest);
560 const hashes = await Promise.all(requests);
564 const setProxyAddress = useCallback(
565 async (proxyType: Proxy, address: EthAddress) => {
566 const _point = need.point(pointCursor);
567 const _wallet = wallet.getOrElse(null);
570 // not using need because we want a custom error
571 throw new Error('Internal Error: Missing Wallet');
574 const pointDetails = await api.getPoint(_point);
575 const ownership = pointDetails.ownership!;
576 const proxy = getAddressProxy(ownership, _wallet.address, proxyType);
578 if (proxy === undefined)
579 throw new Error("Error: Address doesn't match expected proxy");
581 const nonce = await api.getNonce({ ship: _point, proxy });
583 const txHash = await registerProxyAddress(
593 const pendingTx = await api.getPendingTx(txHash);
597 [api, pointCursor, wallet]
600 const transferPoint = useCallback(
601 async (address: EthAddress, reset?: boolean) => {
602 const _point = need.point(pointCursor);
603 const _wallet = wallet.getOrElse(null);
606 // not using need because we want a custom error
607 throw new Error('Internal Error: Missing Wallet');
610 const pointDetails = await api.getPoint(_point);
611 const proxy = getTransferProxy(pointDetails.ownership!, _wallet.address);
613 if (proxy === undefined)
614 throw new Error("Error: Address doesn't match proxy");
616 const nonce = await api.getNonce({ ship: _point, proxy });
618 const txHash = await transferPointRequest(
627 const pendingTx = await api.getPendingTx(txHash);
631 [api, pointCursor, wallet]
634 // On load, get initial config
641 }, [config, fetchConfig]);
644 const interval = setInterval(() => {
645 const nextRoll = getTimeToNextBatch(nextBatchTime, new Date().getTime());
646 setNextRoll(nextRoll);
648 if (nextBatchTime <= new Date().getTime()) {
649 api.getRollerConfig().then(response => {
650 setNextBatchTime(response.nextBatch);
653 getInvites(currentL2, 0);
657 return () => clearInterval(interval);
658 }, [nextBatchTime, getPendingTransactions, currentL2]); // eslint-disable-line react-hooks/exhaustive-deps
666 getPendingTransactions,
670 configureNetworkingKeys,
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list