1import { useCallback, useEffect, useMemo, useState } from 'react';
2import * as wg from 'lib/walletgen';
3import * as need from 'lib/need';
4import * as azimuth from 'azimuth-js';
5import * as ob from 'urbit-ob';
6import { Just } from 'folktale/maybe';
7import { randomHex } from 'web3-utils';
10 attemptNetworkSeedDerivation,
14import { addHexPrefix } from 'lib/utils/address';
25} from '@urbit/roller-api';
27import { isDevelopment, isRopsten } from './flags';
28import { ROLLER_HOSTS } from './constants';
29import { useRollerStore } from 'store/roller';
30import { getTimeToNextBatch } from './utils/roller';
31import { useWallet } from 'store/wallet';
32import { usePointCursor } from 'store/pointCursor';
33import { useNetwork } from 'store/network';
34import { signTransactionHash } from './authToken';
35import { Invite } from 'types/Invite';
40} from 'store/storage/roller';
41import { usePointCache } from 'store/pointCache';
42import { getOutgoingPoints, maybeGetResult } from 'views/Points';
43import { isPlanet } from './utils/point';
54 ship: _point, //ship that is spawning the planet
59 address: _wallet.address,
60 ship: planet, // ship to spawn
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);
68const configureKeys = async (
81 ship: _point, //ship that is spawning the planet
85 const networkSeed = await attemptNetworkSeedDerivation({
95 const seed = Just.hasInstance(networkSeed)
99 const pair = deriveNetworkKeys(seed);
102 encrypt: addHexPrefix(pair.crypt.public),
103 auth: addHexPrefix(pair.auth.public),
104 cryptoSuite: String(CRYPTO_SUITE_VERSION),
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);
113const transferPoint = async (
122 ship: _point, //ship that is spawning the planet
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);
136const hasPoint = (point: number) => (invite: Invite) => invite.planet === point;
138const getProxyAndNonce = (
141): { proxy?: string; nonce?: number } =>
142 point.ownership?.managementProxy?.address === address
143 ? { proxy: 'manage', nonce: point.ownership?.managementProxy.nonce }
144 : point.ownership?.owner?.address === address
145 ? { proxy: 'own', nonce: point.ownership?.owner.nonce }
146 : point.ownership?.spawnProxy?.address === address
147 ? { proxy: 'spawn', nonce: point.ownership?.spawn?.owner.nonce }
148 : point.ownership?.votingProxy?.address === address
149 ? { proxy: 'vote', nonce: point.ownership?.votingProxy?.owner.nonce }
150 : point.ownership?.transferProxy?.address === address
153 nonce: point.transferProxy?.votingProxy?.owner.nonce,
155 : { proxy: undefined, nonce: undefined };
157export default function useRoller() {
158 const { wallet, authToken, authMnemonic, urbitWallet }: any = useWallet();
159 const { pointCursor }: any = usePointCursor();
160 const { web3, contracts }: any = useNetwork();
161 const allPoints: any = usePointCache();
162 const controlledPoints = allPoints?.controlledPoints;
163 const getDetails = allPoints?.getDetails;
169 setPendingTransactions,
172 } = useRollerStore();
173 const [config, setConfig] = useState<Config | null>(null);
175 const options: Options = useMemo(() => {
176 const type = isRopsten || !isDevelopment ? 'https' : 'http';
177 const host = isRopsten
178 ? ROLLER_HOSTS.ROPSTEN
181 : ROLLER_HOSTS.MAINNET;
182 const port = isDevelopment ? 8080 : 80;
183 const path = '/v1/roller';
195 const api = useMemo(() => {
196 return new RollerRPCAPI(options);
199 const fetchConfig = useCallback(async () => {
204 setNextBatchTime(response.nextBatch);
207 // TODO: more elegant error handling
209 '[fetchConfig:failed] is roller running on localhost?\n',
213 }, [api]); // eslint-disable-line react-hooks/exhaustive-deps
215 const generateInviteCodes = useCallback(
216 async (numInvites: number) => {
217 const _point = need.point(pointCursor);
218 const _contracts = contracts.getOrElse(null);
219 const _web3 = web3.getOrElse(null);
220 const _wallet = wallet.getOrElse(null);
221 const _authToken = authToken.getOrElse(null);
222 const _details = getDetails(_point);
223 if (!_contracts || !_web3 || !_wallet || !_authToken || !_details) {
224 // not using need because we want a custom error
225 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
228 const planets: UnspawnedPoints = await api.getUnspawned(_point);
229 const starInfo = await api.getPoint(_point);
231 const tickets: { ticket: string; planet: number; owner: string }[] = [];
232 const requests: Promise<string>[] = [];
234 const { proxy, nonce } = getProxyAndNonce(starInfo, _wallet.address);
236 if (!(proxy === 'own' || proxy === 'spawn') || nonce === undefined)
237 throw new Error("Error: Address doesn't match proxy");
239 for (let i = 0; i < numInvites && planets[i]; i++) {
240 const planet = planets[i];
241 const nonceInc = i * 3;
243 const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
248 const spawnRequest = await spawn(
257 const configureKeysRequest = await configureKeys(
270 const transferPointRequest = await transferPoint(
279 requests.push(spawnRequest, configureKeysRequest, transferPointRequest);
283 owner: owner.keys.address,
287 const hashes = await Promise.all(requests);
289 const pendingInvites = tickets.map((ticket, ind) => ({
291 hash: hashes[ind * 3 + 2],
295 setPendingInvites(_point, pendingInvites);
312 const getPoints = useCallback(
313 async (proxy: Proxy, address: EthAddress) => {
314 const points: Ship[] =
316 ? await api.getOwnedPoints(address)
318 ? await api.getManagerFor(address)
320 ? await api.getVotingFor(address)
321 : proxy === 'transfer'
322 ? await api.getTransferringFor(address)
324 ? await api.getSpawningFor(address)
332 const getPendingTransactions = useCallback(async () => {
334 const curPoint = Number(need.point(pointCursor));
335 const newPending = await api.getPendingByShip(curPoint);
336 setPendingTransactions(newPending);
338 // const allTransactions = await api.getHistory()
340 console.warn('ERROR GETTING PENDING', error);
342 }, [api, setPendingTransactions, pointCursor]);
344 const getInvites = useCallback(
345 async (isL2: boolean) => {
347 const curPoint: number = Number(need.point(pointCursor));
348 const invites = getStoredInvites(curPoint);
349 const availableInvites = invites.available;
351 const pendingTransactions = await api.getPendingByShip(curPoint);
353 console.log('PENDING', pendingTransactions);
355 setPendingTransactions(pendingTransactions);
357 const stillPending = invites.pending.filter(invite => {
358 const completed = !pendingTransactions.find(
359 p => `~${p?.rawTx?.tx?.tx?.data?.ship}` === ob.patp(invite.planet)
364 !availableInvites.find(({ ship }) => invite.ship === ship)
366 availableInvites.push({ ...invite, status: 'available' });
372 setStoredInvites(curPoint, {
373 available: availableInvites,
374 pending: stillPending,
376 setInvites(availableInvites);
378 const _authToken = authToken.getOrElse(null);
379 const _contracts = contracts.getOrElse(null);
381 if (_authToken && _contracts) {
382 let possibleMissingInvites: number[] = [];
384 const allSpawned = await api.getSpawned(curPoint);
385 const ownedPoints = maybeGetResult(
390 possibleMissingInvites = allSpawned.filter(
391 (p: number) => ownedPoints.includes(p) && isPlanet(p)
395 const outgoingPoints = getOutgoingPoints(
400 const availablePoints = await azimuth.azimuth.getUnspawnedChildren(
405 possibleMissingInvites = possibleMissingInvites.concat(
406 outgoingPoints.filter(
407 (p: number) => isPlanet(p) && availablePoints.includes(p)
411 // Iterate over all spawned and controlled planets
412 // If the planet is not in available invites, generate the ticket and add it
414 console.log('POSSIBLE MISSING', possibleMissingInvites);
417 for (let i = 0; i < possibleMissingInvites.length; i++) {
418 const planet = possibleMissingInvites[i];
421 !availableInvites.find(hasPoint(planet)) &&
422 !(await azimuth.azimuth.isActive(_contracts, planet))
424 console.log('MISSING IN AVAILABLE', planet);
428 } = await wg.generateTemporaryDeterministicWallet(
433 availableInvites.push({
438 owner: owner.keys.address,
443 setStoredInvites(curPoint, {
444 available: availableInvites.filter(({ planet }) =>
445 possibleMissingInvites.includes(planet)
447 pending: stillPending,
449 setInvites(availableInvites);
452 console.warn('ERROR GETTING INVITES', error);
459 setPendingTransactions,
467 // On load, get initial config
474 }, [config, fetchConfig]);
477 const interval = setInterval(() => {
478 const nextRoll = getTimeToNextBatch(nextBatchTime, new Date().getTime());
479 setNextRoll(nextRoll);
481 if (nextBatchTime <= new Date().getTime()) {
482 api.getRollerConfig().then(response => {
483 setNextBatchTime(response.nextBatch);
486 getInvites(currentL2);
490 return () => clearInterval(interval);
491 }, [nextBatchTime, getPendingTransactions, currentL2]); // eslint-disable-line react-hooks/exhaustive-deps
498 getPendingTransactions,
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