1import { useCallback, useEffect, useMemo, useState } from 'react';
2import * as azimuth from 'azimuth-js';
3import * as need from 'lib/need';
4import SecureLS from 'secure-ls';
7 deriveNetworkSeedFromUrbitWallet,
8 attemptNetworkSeedDerivation,
11import { useNetwork } from 'store/network';
12import { usePointCursor } from 'store/pointCursor';
13import { EMPTY_POINT, useRollerStore } from 'store/rollerStore';
14import { useWallet } from 'store/wallet';
16import { getUpdatedPointMessage, isPlanet, toL1Details } from './utils/point';
17import { convertToInt } from './convertToInt';
18import { isDevelopment } from './flags';
23} from './utils/roller';
24import { TEN_SECONDS } from './constants';
32} from '@urbit/roller-api';
34import Point, { PointField, Points } from './types/Point';
35import { ReticketParams, SendL2Params } from './types/L2Transaction';
36import { L1Point } from './types/L1Point';
37import { ddmmmYYYY } from './utils/date';
38import { showNotification } from './utils/notifications';
39import { useWalletConnect } from './useWalletConnect';
40import { PendingL1Txn } from './types/PendingL1Transaction';
41import { TRANSACTION_PROGRESS } from './reticket';
42import { useRollerOptions } from './useRollerOptions';
43import { useTimerStore } from 'store/timerStore';
45interface UpdateParams {
46 point?: Point | number;
51 intervalTime?: number;
54export default function useRoller() {
62 const { connector, isConnected, signPersonalMessage } = useWalletConnect();
63 const { pointCursor }: any = usePointCursor();
64 const { web3, contracts }: any = useNetwork();
71 setPendingTransactions,
78 const { setNextBatchTime } = useTimerStore();
79 const [config, setConfig] = useState<Config | null>(null);
81 const { options } = useRollerOptions();
83 const api = useMemo(() => {
84 return new RollerRPCAPI(options);
91 encryptionSecret: authToken.getOrElse('default'),
96 const getNextQuotaTime = useCallback(
97 (nextSlice: number) => {
102 setNextQuotaTime(next);
103 getNextQuotaTime(next);
105 .catch(err => console.warn(err));
106 }, nextSlice - new Date().getTime());
108 [api, setNextQuotaTime]
111 const fetchConfig = useCallback(async () => {
112 getNextQuotaTime(Date.now() + 1000);
117 setNextBatchTime(config.nextBatch);
120 // TODO: more elegant error handling
122 '[fetchConfig:failed] check the roller connection?\n',
126 }, [api, getNextQuotaTime, setNextBatchTime]);
128 const quotaReached = useCallback(() => {
129 if (point.l2Quota <= 0) {
131 `You have reached your weekly L2 transaction limit. You will get another
132 ${point.l2Allowance} transactions on ${ddmmmYYYY(nextQuotaTime)}.`
136 return point.l2Quota <= 0;
137 }, [nextQuotaTime, point, setModalText]);
139 const initPoint = useCallback(
140 async (point: string | number): Promise<Point> => {
141 const _wallet = wallet.getOrElse(null);
147 const pointNum = Number(point);
149 const rawDetails = await api.getPoint(pointNum);
150 const isL2 = isL2Spawn(rawDetails?.dominion);
151 const l2Quota = isL2 ? await api.getRemainingQuota(pointNum) : 0;
152 const l2Allowance = isL2 ? await api.getAllowance(pointNum) : 0;
154 const details = toL1Details(rawDetails);
159 address: _wallet.address,
166 // Try getting the details from L1
168 const _contracts = need.contracts(contracts);
169 const details = await azimuth.azimuth.getPoint(_contracts, point);
173 address: _wallet.address,
177 // Just return a placeholder Point
178 const details: L1Point = toL1Details();
182 address: _wallet.address,
188 [api, wallet, contracts]
191 const getAndUpdatePoint = useCallback(
192 async (point: number) => {
193 const updatedPoint = await initPoint(point);
194 updatePoint(updatedPoint);
196 [initPoint, updatePoint]
199 const checkForUpdates = useCallback(
206 intervalTime = TEN_SECONDS,
207 }: UpdateParams) => {
208 const pVal = typeof point === 'number' ? point : point.value;
209 const p = await initPoint(pVal);
211 if (l1Txn) storePendingL1Txn(l1Txn);
214 const check = async () => {
215 const changedField = !points[p.value]
217 : p.getChangedField(points[p.value], field);
220 console.log(`CHECKING FOR ${changedField} UPDATES:`, p.patp);
223 if (!p.isPlaceholder && changedField) {
226 clearInterval(interval);
229 if (l1Txn) deletePendingL1Txn(l1Txn);
232 `${message || getUpdatedPointMessage(p, changedField)}`
238 interval = setInterval(check, intervalTime);
241 [points, initPoint, updatePoint, storePendingL1Txn, deletePendingL1Txn]
244 const spawnPoint = useCallback(
245 async (pointToSpawn: number, destinationAddress: string) => {
246 if (quotaReached()) {
250 const _point = need.point(pointCursor);
251 const _contracts = contracts.getOrElse(null);
252 const _web3 = web3.getOrElse(null);
253 const _wallet = wallet.getOrElse(null);
254 const _authToken = authToken.getOrElse(null);
256 if (!_contracts || !_web3 || !_wallet || !_authToken) {
257 // not using need because we want a custom error
258 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
261 const proxy = point.getSpawnProxy();
263 if (proxy === undefined)
264 throw new Error("Error: Address doesn't match proxy");
266 const nonce = await api.getNonce({
271 await submitL2Transaction({
273 address: destinationAddress,
303 const getPoints = useCallback(
304 async (proxy: Proxy, address: EthAddress) => {
305 const points: Ship[] =
307 ? await api.getOwnedPoints(address)
309 ? await api.getManagerFor(address)
311 ? await api.getVotingFor(address)
312 : proxy === 'transfer'
313 ? await api.getTransferringFor(address)
315 ? await api.getSpawningFor(address)
323 const getPendingTransactions = useCallback(async () => {
325 if (!point.isDefault) {
326 const newPending = await api.getPendingByShip(point.value);
327 setPendingTransactions(newPending);
330 console.warn('ERROR GETTING PENDING', error);
332 }, [api, setPendingTransactions, point]);
334 interface ConfigureKeysParams {
336 customNetworkSeed?: string;
339 const configureNetworkKeys = useCallback(
340 async ({ breach, customNetworkSeed }: ConfigureKeysParams) => {
341 if (quotaReached()) {
344 const _wallet = wallet.getOrElse(null);
345 const _web3 = web3.getOrElse(null);
346 if (!_wallet || !_web3 || point.isDefault) {
347 // not using need because we want a custom error
348 throw new Error('Internal Error: Missing Wallet/Details');
351 const proxy = point.getManagerProxy();
353 if (proxy === undefined)
354 throw new Error("Error: Address doesn't match proxy");
356 const nonce = await api.getNonce({
361 const nextRevision = point.keyRevisionNumber + 1;
362 const networkSeed = customNetworkSeed
364 : await attemptNetworkSeedDerivation({
371 revision: nextRevision,
373 const txHash = await submitL2Transaction({
380 type: 'configureKeys',
389 return api.getPendingTx(txHash);
392 // eslint-disable-line
408 const performL2Reticket = async ({
415 }: ReticketParams) => {
416 const azimuthPoint = await api.getPoint(point);
418 const _web3 = web3.getOrElse(null);
419 let nonce = await api.getNonce({ ship: point, proxy });
420 const progress = onUpdate
421 ? (state: number) => onUpdate({ type: 'progress', state })
426 // 1. Update networking keys
427 progress(TRANSACTION_PROGRESS.GENERATING);
428 const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10);
429 const nextRevision = networkRevision + 1;
430 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
434 const configureKeysRequest = await submitL2Transaction({
442 type: 'configureKeys',
449 requests.push(configureKeysRequest);
451 // 2. Set Management Proxy
452 progress(TRANSACTION_PROGRESS.SIGNING);
453 const registerMgmtRequest = await registerProxyAddress(
468 requests.push(registerMgmtRequest);
470 // 3. Set Spawn Proxy
471 progress(TRANSACTION_PROGRESS.FUNDING);
472 if (!isPlanet(Number(point))) {
473 const registerSpawnRequest = await registerProxyAddress(
480 toWallet?.spawn?.keys?.address || to,
488 requests.push(registerSpawnRequest);
492 progress(TRANSACTION_PROGRESS.TRANSFERRING);
493 const transferTxRequest = await submitL2Transaction({
498 type: 'transferPoint',
507 requests.push(transferTxRequest);
509 progress(TRANSACTION_PROGRESS.CLEANING);
510 const hashes = await Promise.all(requests);
512 progress(TRANSACTION_PROGRESS.DONE);
516 const setProxyAddress = useCallback(
517 async (proxyType: Proxy, address: EthAddress) => {
518 if (quotaReached()) {
521 const _wallet = wallet.getOrElse(null);
522 const _web3 = web3.getOrElse(null);
525 // not using need because we want a custom error
526 throw new Error('Internal Error: Missing Wallet');
529 if (point.isDefault) {
530 // not using need because we want a custom error
531 throw new Error('Internal Error: Invalid point');
533 const proxy = point.getAddressProxy(proxyType);
535 if (proxy === undefined)
536 throw new Error("Error: Address doesn't match expected proxy");
538 const nonce = await api.getNonce({ ship: point.value, proxy });
539 const txHash = await registerProxyAddress(
553 return api.getPendingTx(txHash);
568 const changeSponsorship = useCallback(
571 type: 'adopt' | 'detach' | 'reject',
572 skipQuotaCheck = false
574 if (!skipQuotaCheck && quotaReached()) {
578 const _wallet = wallet.getOrElse(null);
579 const _web3 = web3.getOrElse(null);
580 const ship = point.value;
582 const proxy = point.getManagerProxy();
584 if (proxy === undefined)
585 throw new Error("Error: Address doesn't match expected proxy");
587 const nonce = await api.getNonce({ ship, proxy });
588 // const txHash = await adopt(api, _wallet, point.value, proxy, nonce, ship);
589 const txHash = await submitL2Transaction({
596 address: _wallet.address,
605 return api.getPendingTx(txHash);
620 const transferPoint = useCallback(
621 async (address: EthAddress, reset?: boolean) => {
622 if (quotaReached()) {
626 const _point = need.point(pointCursor);
627 const _wallet = wallet.getOrElse(null);
628 const _web3 = web3.getOrElse(null);
631 // not using need because we want a custom error
632 throw new Error('Internal Error: Missing Wallet');
635 const proxy = point.getTransferProxy();
637 if (proxy === undefined)
638 throw new Error("Error: Address doesn't match proxy");
640 const nonce = await api.getNonce({ ship: _point, proxy });
642 const txHash = await submitL2Transaction({
649 type: 'transferPoint',
658 return api.getPendingTx(txHash);
674 const sendL2Transaction = useCallback(
675 async (params: SendL2Params) => {
676 if (quotaReached()) {
680 const _wallet = wallet.getOrElse(null);
681 const _web3 = web3.getOrElse(null);
683 if (!_wallet || point.isDefault) {
684 // not using need because we want a custom error
685 throw new Error('Internal Error: Missing Wallet or Point Info');
688 return submitL2Transaction({
713 const changeSponsor = useCallback(
714 async (newSponsor: number) => {
715 if (point.isDefault) {
716 throw new Error('Internal Error: point is default');
719 const curPoint = point.value;
720 const _wallet = wallet.getOrElse(null);
723 // not using need because we want a custom error
724 throw new Error('Internal Error: Missing Wallet');
727 const proxy = point.getManagerProxy();
729 if (proxy === undefined)
730 throw new Error("Error: Address doesn't match proxy");
732 const nonce = await api.getNonce({ ship: curPoint, proxy });
734 return sendL2Transaction({
742 [api, point, sendL2Transaction, wallet]
745 const cancelEscape = useCallback(async () => {
746 if (point.isDefault) {
747 throw new Error('Internal Error: point is default');
750 const curPoint = point.value;
751 const _wallet = wallet.getOrElse(null);
754 // not using need because we want a custom error
755 throw new Error('Internal Error: Missing Wallet');
758 const proxy = point.getManagerProxy();
760 if (proxy === undefined)
761 throw new Error("Error: Address doesn't match proxy");
763 const nonce = await api.getNonce({ ship: curPoint, proxy });
765 return sendL2Transaction({
768 type: 'cancelEscape',
771 }, [api, point, sendL2Transaction, wallet]);
773 const getPointsDetails = useCallback(
775 ownedPoints: number[],
776 incomingPoints: number[],
777 managingPoints: number[],
778 votingPoints: number[],
779 spawningPoints: number[]
783 await Promise.all(ownedPoints.map(initPoint)),
784 await Promise.all(incomingPoints.map(initPoint)),
785 await Promise.all(managingPoints.map(initPoint)),
786 await Promise.all(votingPoints.map(initPoint)),
787 await Promise.all(spawningPoints.map(initPoint)),
791 allPoints.reduce((acc: Points, cur: (Point | undefined)[]) => {
804 [setPoints, initPoint]
807 // On load, get initial config
814 }, [config, fetchConfig]);
823 configureNetworkKeys,
827 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