on bd2bfea help sign in
bridge/src/lib/useRoller.ts
201 lines 5.5 kB history raw
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';
7import {
8 Config,
9 Ship,
10 Proxy,
11 RollerRPCAPI,
12 Options,
13 EthAddress,
14} from '@urbit/roller-api';
15import { isDevelopment, isRopsten } from './flags';
16import { ROLLER_HOSTS } from './constants';
17import { useRollerStore } from 'store/roller';
18import { getTimeToNextBatch } from './utils/roller';
19import { useWallet } from 'store/wallet';
20import { usePointCursor } from 'store/pointCursor';
21import { useNetwork } from 'store/network';
23export default function useRoller() {
24 const { wallet, walletType, walletHdPath, authToken }: any = useWallet();
25 const { pointCursor }: any = usePointCursor();
26 const { web3, contracts }: any = useNetwork();
28 const { nextBatchTime, setNextBatchTime, setNextRoll } = useRollerStore();
29 const [config, setConfig] = useState<Config | null>(null);
31 const options: Options = useMemo(() => {
32 const type = isRopsten || !isDevelopment ? 'https' : 'http';
33 const host = isRopsten
34 ? ROLLER_HOSTS.ROPSTEN
35 : isDevelopment
36 ? ROLLER_HOSTS.LOCAL
37 : ROLLER_HOSTS.MAINNET;
38 const port = isDevelopment ? 8080 : 80;
39 const path = '/v1/roller';
41 return {
42 transport: {
43 type,
44 host,
45 port,
46 path,
47 },
48 };
49 }, []);
51 const api = useMemo(() => {
52 return new RollerRPCAPI(options);
53 }, [options]);
55 const fetchConfig = useCallback(async () => {
56 api
57 .getRollerConfig()
58 .then(response => {
59 setConfig(response);
60 setNextBatchTime(response.nextBatch);
61 })
62 .catch(err => {
63 // TODO: more elegant error handling
64 console.warn(
65 '[fetchConfig:failed] is roller running on localhost?\n',
66 err
67 );
68 });
69 }, [api]); // eslint-disable-line react-hooks/exhaustive-deps
71 const generateInviteCodes = useCallback(
72 async (numInvites: number) => {
73 const _point = need.point(pointCursor);
74 const _contracts = contracts.getOrElse(null);
75 const _web3 = web3.getOrElse(null);
76 const _wallet = wallet.getOrElse(null);
77 const _authToken = authToken.getOrElse(null);
78 if (!_contracts || !_web3 || !_wallet || !_authToken) {
79 // not using need because we want a custom error
80 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
81 }
83 const planets: number[] = await azimuth.azimuth.getUnspawnedChildren(
84 _contracts,
85 _point
86 );
88 // const planets: any = await azimuth.delegatedSending.getPlanetsToSend(
89 // _contracts,
90 // _point,
91 // numInvites,
92 // );
94 const starInfo = await api.getPoint(_point);
96 const tickets: { ticket: string; planet: string }[] = [];
97 const requests: Promise<string>[] = [];
99 const nonce = starInfo?.ownership?.owner?.nonce!;
100 const address = starInfo?.ownership?.owner?.address!;
102 for (let i = 0; i < numInvites && planets[i]; i++) {
103 const planet = planets[i];
105 const { ticket, owner } = await wg.generateTemporaryDeterministicWallet(
106 planet,
107 _authToken
108 );
110 const from = {
111 ship: _point, //ship that is spawning the planet
112 proxy: 'own', // this should be either "own" or "proxy"
113 };
115 const data = {
116 address: owner.keys.address, // the new owner of the star (invite wallet)
117 ship: planet, // ship to spawn
118 };
120 const txHash = await api.hashTransaction(
121 nonce + i,
122 from,
123 'spawn',
124 data
125 );
126 const { signature } = _web3.eth.accounts.sign(
127 txHash,
128 _wallet.privateKey.toString('hex')
129 );
131 requests.push(api.spawn(signature, from, address, data));
132 tickets.push({ ticket, planet: ob.patp(planet) });
133 }
135 const hashes = await Promise.all(requests);
137 return hashes.map((hash, i) => ({ hash, ...tickets[i] }));
138 },
139 [
140 api,
141 authToken,
142 contracts,
143 pointCursor,
144 wallet,
145 walletHdPath,
146 walletType,
147 web3
148 ]
149 );
151 const getPoints = useCallback(
152 async (proxy: Proxy, address: EthAddress) => {
153 const points: Ship[] =
154 proxy === 'own'
155 ? await api.getOwnedPoints(address)
156 : proxy === 'mange'
157 ? await api.getManagerFor(address)
158 : proxy === 'vote'
159 ? await api.getVotingFor(address)
160 : proxy === 'transfer'
161 ? await api.getTransferringFor(address)
162 : proxy === 'spawn'
163 ? await api.getSpawningFor(address)
164 : [];
166 return points;
167 },
168 [api]
169 );
171 // On load, get initial config
172 useEffect(() => {
173 if (config) {
174 return;
175 }
177 fetchConfig();
178 }, [config, fetchConfig]);
180 useEffect(() => {
181 const interval = setInterval(() => {
182 const nextRoll = getTimeToNextBatch(nextBatchTime, new Date().getTime());
183 setNextRoll(nextRoll);
185 if (nextBatchTime <= new Date().getTime()) {
186 api.getRollerConfig().then(response => {
187 setNextBatchTime(response.nextBatch);
188 });
189 }
190 }, 1000);
192 return () => clearInterval(interval);
193 }, [nextBatchTime]); // eslint-disable-line react-hooks/exhaustive-deps
195 return {
196 api,
197 config,
198 getPoints,
199 generateInviteCodes,
200 };