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';
16} from '@urbit/roller-api';
18import { isDevelopment, isRopsten } from './flags';
19import { ROLLER_HOSTS } from './constants';
20import { useRollerStore } from 'store/roller';
21import { getTimeToNextBatch } from './utils/roller';
22import { useWallet } from 'store/wallet';
23import { usePointCursor } from 'store/pointCursor';
24import { useNetwork } from 'store/network';
25import { signTransactionHash } from './authToken';
26import { Invite } from 'types/Invite';
31} from 'store/storage/roller';
32import { usePointCache } from 'store/pointCache';
33import { getOutgoingPoints, maybeGetResult } from 'views/Points';
35const hasPoint = (point: number) => (invite: Invite) => invite.planet === point;
37const getProxyAndNonce = (
40): { proxy?: string; nonce?: number } =>
41 point.ownership?.managementProxy?.address === address
42 ? { proxy: 'manage', nonce: point.ownership?.managementProxy.nonce }
43 : point.ownership?.owner?.address === address
44 ? { proxy: 'own', nonce: point.ownership?.owner.nonce }
45 : point.ownership?.spawnProxy?.address === address
46 ? { proxy: 'spawn', nonce: point.ownership?.spawn?.owner.nonce }
47 : point.ownership?.votingProxy?.address === address
48 ? { proxy: 'vote', nonce: point.ownership?.votingProxy?.owner.nonce }
49 : point.ownership?.transferProxy?.address === address
52 nonce: point.transferProxy?.votingProxy?.owner.nonce,
54 : { proxy: undefined, nonce: undefined };
56export default function useRoller() {
57 const { wallet, authToken }: any = useWallet();
58 const { pointCursor }: any = usePointCursor();
59 const { web3, contracts }: any = useNetwork();
60 const allPoints: any = usePointCache();
61 const controlledPoints = allPoints?.controlledPoints;
62 const getDetails = allPoints?.getDetails;
68 setPendingTransactions,
72 const [config, setConfig] = useState<Config | null>(null);
74 const options: Options = useMemo(() => {
75 const type = isRopsten || !isDevelopment ? 'https' : 'http';
76 const host = isRopsten
77 ? ROLLER_HOSTS.ROPSTEN
80 : ROLLER_HOSTS.MAINNET;
81 const port = isDevelopment ? 8080 : 80;
82 const path = '/v1/roller';
94 const api = useMemo(() => {
95 return new RollerRPCAPI(options);
98 const fetchConfig = useCallback(async () => {
103 setNextBatchTime(response.nextBatch);
106 // TODO: more elegant error handling
108 '[fetchConfig:failed] is roller running on localhost?\n',
112 }, [api]); // eslint-disable-line react-hooks/exhaustive-deps
114 const generateInviteCodes = useCallback(
115 async (numInvites: number) => {
116 const _point = need.point(pointCursor);
117 const _contracts = contracts.getOrElse(null);
118 const _web3 = web3.getOrElse(null);
119 const _wallet = wallet.getOrElse(null);
120 const _authToken = authToken.getOrElse(null);
121 if (!_contracts || !_web3 || !_wallet || !_authToken) {
122 // not using need because we want a custom error
123 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
126 const planets: UnspawnedPoints = await api.getUnspawned(_point);
127 const starInfo = await api.getPoint(_point);
129 const tickets: { ticket: string; planet: number; owner: string }[] = [];
130 const requests: Promise<string>[] = [];
132 const { proxy, nonce } = getProxyAndNonce(starInfo, _wallet.address);
134 if (!(proxy === 'own' || proxy === 'spawn') || nonce === undefined)
135 throw new Error("Error: Address doesn't match proxy");
137 for (let i = 0; i < numInvites && planets[i]; i++) {
138 const planet = planets[i];
140 const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
146 ship: _point, //ship that is spawning the planet
151 address: owner.keys.address, // the new owner of the star (invite wallet)
152 ship: planet, // ship to spawn
155 const txHash = await api.hashTransaction(
162 const signature = signTransactionHash(txHash, _wallet.privateKey);
163 requests.push(api.spawn(signature, from, _wallet.address, data));
167 owner: owner.keys.address,
171 const hashes = await Promise.all(requests);
172 const pendingInvites = hashes.map(
173 (hash, i): Invite => ({
179 setPendingInvites(_point, pendingInvites);
193 const getPoints = useCallback(
194 async (proxy: Proxy, address: EthAddress) => {
195 const points: Ship[] =
197 ? await api.getOwnedPoints(address)
199 ? await api.getManagerFor(address)
201 ? await api.getVotingFor(address)
202 : proxy === 'transfer'
203 ? await api.getTransferringFor(address)
205 ? await api.getSpawningFor(address)
213 const getPendingTransactions = useCallback(async () => {
215 const curPoint = Number(need.point(pointCursor));
216 const newPending = await api.getPendingByShip(curPoint);
217 setPendingTransactions(newPending);
219 // const allTransactions = await api.getHistory()
221 console.warn('ERROR GETTING PENDING', error);
223 }, [api, setPendingTransactions, pointCursor]);
225 const getInvites = useCallback(
226 async (isL2: boolean) => {
228 const curPoint: number = Number(need.point(pointCursor));
229 const invites = getStoredInvites(curPoint);
230 const availableInvites = invites.available;
232 const pendingTransactions = await api.getPendingByShip(curPoint);
234 console.log('PENDING', pendingTransactions);
236 setPendingTransactions(pendingTransactions);
238 const stillPending = invites.pending.filter(invite => {
239 const completed = !pendingTransactions.find(
240 p => `~${p?.rawTx?.tx?.tx?.data?.ship}` === ob.patp(invite.planet)
243 availableInvites.push({ ...invite, status: 'available' });
248 setStoredInvites(curPoint, {
249 available: availableInvites,
250 pending: stillPending,
252 setInvites(availableInvites);
254 const _authToken = authToken.getOrElse(null);
255 const _contracts = contracts.getOrElse(null);
257 if (_authToken && _contracts) {
258 let possibleMissingInvites: number[] = [];
260 const allSpawned = await api.getSpawned(curPoint);
261 const ownedPoints = maybeGetResult(
266 possibleMissingInvites = allSpawned.filter(
268 ownedPoints.includes(p) &&
269 azimuth.azimuth.getPointSize(p) ===
270 azimuth.azimuth.PointSize.Planet
274 const outgoingPoints = getOutgoingPoints(
279 const availablePoints = await azimuth.azimuth.getUnspawnedChildren(
284 possibleMissingInvites = possibleMissingInvites.concat(
285 outgoingPoints.filter(
287 azimuth.azimuth.getPointSize(p) ===
288 azimuth.azimuth.PointSize.Planet &&
289 availablePoints.includes(p)
293 // Iterate over all spawned and controlled planets
294 // If the planet is not in available invites, generate the ticket and add it
296 console.log('POSSIBLE MISSING', possibleMissingInvites);
299 for (let i = 0; i < possibleMissingInvites.length; i++) {
300 const planet = possibleMissingInvites[i];
303 !availableInvites.find(hasPoint(planet)) &&
304 !(await azimuth.azimuth.isActive(_contracts, planet))
306 console.log('MISSING IN AVAILABLE', planet);
310 } = await wg.generateTemporaryDeterministicWallet(
315 availableInvites.push({
320 owner: owner.keys.address,
325 setStoredInvites(curPoint, {
326 available: availableInvites.filter(({ planet }) =>
327 possibleMissingInvites.includes(planet)
329 pending: stillPending,
331 setInvites(availableInvites);
334 console.warn('ERROR GETTING INVITES', error);
341 setPendingTransactions,
349 // On load, get initial config
356 }, [config, fetchConfig]);
359 const interval = setInterval(() => {
360 const nextRoll = getTimeToNextBatch(nextBatchTime, new Date().getTime());
361 setNextRoll(nextRoll);
363 if (nextBatchTime <= new Date().getTime()) {
364 api.getRollerConfig().then(response => {
365 setNextBatchTime(response.nextBatch);
368 getInvites(currentL2);
372 return () => clearInterval(interval);
373 }, [nextBatchTime, getPendingTransactions, currentL2]); // eslint-disable-line react-hooks/exhaustive-deps
380 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