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';
27import { getStoredInvites, setPendingInvites, setStoredInvites } from 'store/storage/roller';
28import { usePointCache } from 'store/pointCache';
29import { maybeGetResult } from 'views/Points';
31const hasPoint = (point: number) => (invite: Invite) => invite.planet === point;
33const getProxyAndNonce = (
36): { proxy?: string; nonce?: number } =>
37 point.ownership?.managementProxy?.address === address
38 ? { proxy: 'manage', nonce: point.ownership?.managementProxy.nonce }
39 : point.ownership?.owner?.address === address
40 ? { proxy: 'own', nonce: point.ownership?.owner.nonce }
41 : point.ownership?.spawnProxy?.address === address
42 ? { proxy: 'spawn', nonce: point.ownership?.spawn?.owner.nonce }
43 : point.ownership?.votingProxy?.address === address
44 ? { proxy: 'vote', nonce: point.ownership?.votingProxy?.owner.nonce }
45 : point.ownership?.transferProxy?.address === address
48 nonce: point.transferProxy?.votingProxy?.owner.nonce,
50 : { proxy: undefined, nonce: undefined };
52export default function useRoller() {
53 const { wallet, walletType, walletHdPath, authToken }: any = useWallet();
54 const { pointCursor }: any = usePointCursor();
55 const { web3, contracts }: any = useNetwork();
56 const allPoints: any = usePointCache();
57 const controlledPoints = allPoints?.controlledPoints;
63 setPendingTransactions,
66 const [config, setConfig] = useState<Config | null>(null);
68 const options: Options = useMemo(() => {
69 const type = isRopsten || !isDevelopment ? 'https' : 'http';
70 const host = isRopsten
71 ? ROLLER_HOSTS.ROPSTEN
74 : ROLLER_HOSTS.MAINNET;
75 const port = isDevelopment ? 8080 : 80;
76 const path = '/v1/roller';
88 const api = useMemo(() => {
89 return new RollerRPCAPI(options);
92 const fetchConfig = useCallback(async () => {
97 setNextBatchTime(response.nextBatch);
100 // TODO: more elegant error handling
102 '[fetchConfig:failed] is roller running on localhost?\n',
106 }, [api]); // eslint-disable-line react-hooks/exhaustive-deps
108 const generateInviteCodes = useCallback(
109 async (numInvites: number) => {
110 const _point = need.point(pointCursor);
111 const _contracts = contracts.getOrElse(null);
112 const _web3 = web3.getOrElse(null);
113 const _wallet = wallet.getOrElse(null);
114 const _authToken = authToken.getOrElse(null);
115 if (!_contracts || !_web3 || !_wallet || !_authToken) {
116 // not using need because we want a custom error
117 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
120 const planets: UnspawnedPoints = await api.getUnspawned(_point);
121 const starInfo = await api.getPoint(_point);
123 const tickets: { ticket: string; planet: number; owner: string }[] = [];
124 const requests: Promise<string>[] = [];
126 const { proxy, nonce } = getProxyAndNonce(starInfo, _wallet.address);
128 if (proxy === undefined || nonce === undefined)
129 throw new Error("Error: Address doesn't match proxy");
131 for (let i = 0; i < numInvites && planets[i]; i++) {
132 const planet = planets[i];
134 const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
140 ship: _point, //ship that is spawning the planet
141 proxy, // TODO: check that this is either "own" or "spawn"
145 address: owner.keys.address, // the new owner of the star (invite wallet)
146 ship: planet, // ship to spawn
149 const txHash = await api.hashTransaction(
156 const signature = signTransactionHash(txHash, _wallet.privateKey);
157 requests.push(api.spawn(signature, from, _wallet.address, data));
161 owner: owner.keys.address,
165 const hashes = await Promise.all(requests);
166 const pendingInvites = hashes.map(
167 (hash, i): Invite => ({
173 setPendingInvites(_point, pendingInvites);
187 const getPoints = useCallback(
188 async (proxy: Proxy, address: EthAddress) => {
189 const points: Ship[] =
191 ? await api.getOwnedPoints(address)
193 ? await api.getManagerFor(address)
195 ? await api.getVotingFor(address)
196 : proxy === 'transfer'
197 ? await api.getTransferringFor(address)
199 ? await api.getSpawningFor(address)
207 const getPendingTransactions = useCallback(async () => {
209 const curPoint = need.point(pointCursor);
210 const newPending = await api.getPendingByShip(Number(curPoint));
211 console.log('PENDING', newPending);
212 setPendingTransactions(newPending);
214 // const allTransactions = await api.getHistory()
216 console.warn('ERROR GETTING PENDING', error);
218 }, [api, setPendingTransactions, pointCursor]);
220 const getInvites = useCallback(async () => {
222 const curPoint: string = need.point(pointCursor);
223 const invites = getStoredInvites(curPoint);
224 const availableInvites = invites.available;
226 const pendingTransactions = await api.getPendingByShip(curPoint);
227 console.log('PENDING', pendingTransactions);
228 setPendingTransactions(pendingTransactions);
230 const stillPending = invites.pending.filter(invite => {
231 const completed = !pendingTransactions.find(
232 ({ tx }) => tx.tx?.data?.ship === invite.planet
236 availableInvites.push({ ...invite, status: 'available' });
242 setStoredInvites(curPoint, {
243 available: availableInvites,
244 pending: stillPending,
245 claimed: invites.claimed,
247 setInvites(availableInvites);
249 const allSpawned = await api.getSpawned(Number(curPoint));
250 const ownedPoints = maybeGetResult(controlledPoints, 'ownedPoints', []);
251 const spawnedAndOwned = ownedPoints.filter(
252 (p: number) => allSpawned.includes(p) && p > 2000
254 console.log('SPAWNED AND OWNED', spawnedAndOwned);
256 // Iterate over all spawned and controlled planets
257 // If the planet is not in available invites, generate the ticket and add it
258 const _authToken = authToken.getOrElse(null);
259 const _contracts = contracts.getOrElse(null);
261 if (_authToken && _contracts) {
262 for (let i = 0; i < spawnedAndOwned.length; i++) {
263 const point = spawnedAndOwned[i];
265 if (!availableInvites.find(hasPoint(point))) {
266 console.log('MISSING IN AVAILABLE', point);
270 } = await wg.generateTemporaryDeterministicWallet(
275 availableInvites.push({
280 owner: owner.keys.address,
285 setStoredInvites(curPoint, {
286 available: availableInvites,
287 pending: stillPending,
288 claimed: invites.claimed,
290 setInvites(availableInvites);
293 console.warn('ERROR GETTING INVITES', error);
299 setPendingTransactions,
305 // On load, get initial config
312 }, [config, fetchConfig]);
315 const interval = setInterval(() => {
316 const nextRoll = getTimeToNextBatch(nextBatchTime, new Date().getTime());
317 setNextRoll(nextRoll);
319 if (nextBatchTime <= new Date().getTime()) {
320 api.getRollerConfig().then(response => {
321 setNextBatchTime(response.nextBatch);
328 return () => clearInterval(interval);
329 }, [nextBatchTime, getPendingTransactions]); // eslint-disable-line react-hooks/exhaustive-deps
336 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