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';
15import { usePointCache } from 'store/pointCache';
17import { getUpdatedPointMessage, isPlanet, toL1Details } from './utils/point';
18import { convertToInt } from './convertToInt';
19import { isDevelopment } from './flags';
24} from './utils/roller';
25import { TEN_SECONDS } from './constants';
33} from '@urbit/roller-api';
35import Point, { PointField, Points } from './types/Point';
36import { ReticketParams, SendL2Params } from './types/L2Transaction';
37import { L1Point } from './types/L1Point';
38import { ddmmmYYYY } from './utils/date';
39import { showNotification } from './utils/notifications';
40import { useWalletConnect } from './useWalletConnect';
41import { PendingL1Txn } from './types/PendingL1Transaction';
42import { TRANSACTION_PROGRESS } from './reticket';
43import { useRollerOptions } from './useRollerOptions';
44import { useTimerStore } from 'store/timerStore';
46interface UpdateParams {
47 point?: Point | number;
52 intervalTime?: number;
55export default function useRoller() {
63 const { connector } = useWalletConnect();
64 const { pointCursor }: any = usePointCursor();
65 const { web3, contracts }: any = useNetwork();
66 const allPoints: any = usePointCache();
67 const getDetails = allPoints?.getDetails;
74 setPendingTransactions,
81 const { setNextBatchTime } = useTimerStore();
82 const [config, setConfig] = useState<Config | null>(null);
84 const { options } = useRollerOptions();
86 const api = useMemo(() => {
87 return new RollerRPCAPI(options);
94 encryptionSecret: authToken.getOrElse('default'),
99 const getNextQuotaTime = useCallback(
100 (nextSlice: number) => {
105 setNextQuotaTime(next);
106 getNextQuotaTime(next);
108 .catch(err => console.warn(err));
109 }, nextSlice - new Date().getTime());
111 [api, setNextQuotaTime]
114 const fetchConfig = useCallback(async () => {
115 getNextQuotaTime(Date.now() + 1000);
120 setNextBatchTime(config.nextBatch);
123 // TODO: more elegant error handling
125 '[fetchConfig:failed] check the roller connection?\n',
129 }, [api, getNextQuotaTime, setNextBatchTime]);
131 const quotaReached = useCallback(() => {
132 if (point.l2Quota <= 0) {
134 `You have reached your weekly L2 transaction limit. You will get another
135 ${point.l2Allowance} transactions on ${ddmmmYYYY(nextQuotaTime)}.`
139 return point.l2Quota <= 0;
140 }, [nextQuotaTime, point, setModalText]);
142 const initPoint = useCallback(
143 async (point: string | number): Promise<Point> => {
144 const _wallet = wallet.getOrElse(null);
145 const _contracts = contracts.getOrElse(null);
147 if (!_wallet || !_contracts) {
151 const pointNum = Number(point);
153 const rawDetails = await api.getPoint(pointNum);
154 const isL2 = isL2Spawn(rawDetails?.dominion);
155 const l2Quota = isL2 ? await api.getRemainingQuota(pointNum) : 0;
156 const l2Allowance = isL2 ? await api.getAllowance(pointNum) : 0;
158 const details = isL2Spawn(rawDetails?.dominion)
159 ? toL1Details(rawDetails)
160 : await azimuth.azimuth.getPoint(_contracts, point);
165 address: _wallet.address,
172 // Try getting the details from L1
174 const _contracts = need.contracts(contracts);
175 const details = await azimuth.azimuth.getPoint(_contracts, point);
179 address: _wallet.address,
183 // Just return a placeholder Point
184 const details: L1Point = toL1Details();
188 address: _wallet.address,
194 [api, wallet, contracts]
197 const getAndUpdatePoint = useCallback(
198 async (point: number) => {
199 const updatedPoint = await initPoint(point);
200 updatePoint(updatedPoint);
202 [initPoint, updatePoint]
205 const checkForUpdates = useCallback(
212 intervalTime = TEN_SECONDS,
213 }: UpdateParams) => {
216 const pVal = (typeof point === 'number' ? point : point.value);
217 const p = await initPoint(pVal);
219 if (l1Txn) storePendingL1Txn(l1Txn);
222 const check = async () => {
223 const changedField = !points[p.value]
225 : p.getChangedField(points[p.value], field);
228 console.log(`CHECKING FOR ${changedField} UPDATES:`, p.patp);
231 if (!p.isPlaceholder && changedField) {
234 clearInterval(interval);
237 if (l1Txn) deletePendingL1Txn(l1Txn);
240 `${message || getUpdatedPointMessage(p, changedField)}`
246 interval = setInterval(check, intervalTime);
249 [points, initPoint, updatePoint, storePendingL1Txn, deletePendingL1Txn]
252 const spawnPoint = useCallback(
253 async (pointToSpawn: number, destinationAddress: string) => {
254 if (quotaReached()) {
258 const _point = need.point(pointCursor);
259 const _contracts = contracts.getOrElse(null);
260 const _web3 = web3.getOrElse(null);
261 const _wallet = wallet.getOrElse(null);
262 const _authToken = authToken.getOrElse(null);
264 if (!_contracts || !_web3 || !_wallet || !_authToken) {
265 // not using need because we want a custom error
266 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
269 const proxy = point.getSpawnProxy();
271 if (proxy === undefined)
272 throw new Error("Error: Address doesn't match proxy");
274 const nonce = await api.getNonce({
279 await submitL2Transaction({
281 address: destinationAddress,
307 const getPoints = useCallback(
308 async (proxy: Proxy, address: EthAddress) => {
309 const points: Ship[] =
311 ? await api.getOwnedPoints(address)
313 ? await api.getManagerFor(address)
315 ? await api.getVotingFor(address)
316 : proxy === 'transfer'
317 ? await api.getTransferringFor(address)
319 ? await api.getSpawningFor(address)
327 const getPendingTransactions = useCallback(async () => {
329 if (!point.isDefault) {
330 const newPending = await api.getPendingByShip(point.value);
331 setPendingTransactions(newPending);
334 console.warn('ERROR GETTING PENDING', error);
336 }, [api, setPendingTransactions, point]);
338 interface ConfigureKeysParams {
340 customNetworkSeed?: string;
343 const configureNetworkKeys = useCallback(
344 async ({ breach, customNetworkSeed }: ConfigureKeysParams) => {
345 if (quotaReached()) {
348 const _wallet = wallet.getOrElse(null);
349 const _web3 = web3.getOrElse(null);
350 if (!_wallet || !_web3 || point.isDefault) {
351 // not using need because we want a custom error
352 throw new Error('Internal Error: Missing Wallet/Details');
355 const proxy = point.getManagerProxy();
357 if (proxy === undefined)
358 throw new Error("Error: Address doesn't match proxy");
360 const nonce = await api.getNonce({
365 const nextRevision = point.keyRevisionNumber + 1;
366 const networkSeed = customNetworkSeed
368 : await attemptNetworkSeedDerivation({
375 revision: nextRevision,
377 const txHash = await submitL2Transaction({
384 type: 'configureKeys',
391 return api.getPendingTx(txHash);
394 // 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',
447 requests.push(configureKeysRequest);
449 // 2. Set Management Proxy
450 progress(TRANSACTION_PROGRESS.SIGNING);
451 const registerMgmtRequest = await registerProxyAddress(
464 requests.push(registerMgmtRequest);
466 // 3. Set Spawn Proxy
467 progress(TRANSACTION_PROGRESS.FUNDING);
468 if (!isPlanet(Number(point))) {
469 const registerSpawnRequest = await registerProxyAddress(
476 toWallet?.spawn?.keys?.address || to,
482 requests.push(registerSpawnRequest);
486 progress(TRANSACTION_PROGRESS.TRANSFERRING);
487 const transferTxRequest = await submitL2Transaction({
492 type: 'transferPoint',
499 requests.push(transferTxRequest);
501 progress(TRANSACTION_PROGRESS.CLEANING);
502 const hashes = await Promise.all(requests);
504 progress(TRANSACTION_PROGRESS.DONE);
508 const setProxyAddress = useCallback(
509 async (proxyType: Proxy, address: EthAddress) => {
510 if (quotaReached()) {
514 const _wallet = wallet.getOrElse(null);
515 const _web3 = web3.getOrElse(null);
518 // not using need because we want a custom error
519 throw new Error('Internal Error: Missing Wallet');
522 if (point.isDefault) {
523 // not using need because we want a custom error
524 throw new Error('Internal Error: Invalid point');
527 const proxy = point.getAddressProxy(proxyType);
529 if (proxy === undefined)
530 throw new Error("Error: Address doesn't match expected proxy");
532 const nonce = await api.getNonce({ ship: point.value, proxy });
534 const txHash = await registerProxyAddress(
547 return api.getPendingTx(txHash);
549 [api, connector, point, wallet, web3, walletType, quotaReached]
552 const changeSponsorship = useCallback(
555 type: 'adopt' | 'detach' | 'reject',
556 skipQuotaCheck = false
558 if (!skipQuotaCheck && quotaReached()) {
562 const _wallet = wallet.getOrElse(null);
563 const _web3 = web3.getOrElse(null);
564 const ship = point.value;
566 const proxy = point.getManagerProxy();
568 if (proxy === undefined)
569 throw new Error("Error: Address doesn't match expected proxy");
571 const nonce = await api.getNonce({ ship, proxy });
572 // const txHash = await adopt(api, _wallet, point.value, proxy, nonce, ship);
573 const txHash = await submitL2Transaction({
580 address: _wallet.address,
587 return api.getPendingTx(txHash);
589 [api, point, wallet, walletType, web3, connector, quotaReached]
592 const transferPoint = useCallback(
593 async (address: EthAddress, reset?: boolean) => {
594 if (quotaReached()) {
598 const _point = need.point(pointCursor);
599 const _wallet = wallet.getOrElse(null);
600 const _web3 = web3.getOrElse(null);
603 // not using need because we want a custom error
604 throw new Error('Internal Error: Missing Wallet');
607 const proxy = point.getTransferProxy();
609 if (proxy === undefined)
610 throw new Error("Error: Address doesn't match proxy");
612 const nonce = await api.getNonce({ ship: _point, proxy });
614 const txHash = await submitL2Transaction({
621 type: 'transferPoint',
628 return api.getPendingTx(txHash);
630 [api, connector, point, pointCursor, wallet, web3, walletType, quotaReached]
633 const sendL2Transaction = useCallback(
634 async (params: SendL2Params) => {
635 if (quotaReached()) {
639 const _wallet = wallet.getOrElse(null);
640 const _web3 = web3.getOrElse(null);
642 if (!_wallet || point.isDefault) {
643 // not using need because we want a custom error
644 throw new Error('Internal Error: Missing Wallet or Point Info');
647 return submitL2Transaction({
657 [api, connector, point, wallet, web3, walletType, quotaReached]
660 const changeSponsor = useCallback(
661 async (newSponsor: number) => {
662 if (point.isDefault) {
663 throw new Error('Internal Error: point is default');
666 const curPoint = point.value;
667 const _wallet = wallet.getOrElse(null);
670 // not using need because we want a custom error
671 throw new Error('Internal Error: Missing Wallet');
674 const proxy = point.getManagerProxy();
676 if (proxy === undefined)
677 throw new Error("Error: Address doesn't match proxy");
679 const nonce = await api.getNonce({ ship: curPoint, proxy });
681 return sendL2Transaction({
689 [api, point, sendL2Transaction, wallet]
692 const cancelEscape = useCallback(async () => {
693 if (point.isDefault) {
694 throw new Error('Internal Error: point is default');
697 const curPoint = point.value;
698 const _wallet = wallet.getOrElse(null);
701 // not using need because we want a custom error
702 throw new Error('Internal Error: Missing Wallet');
705 const proxy = point.getManagerProxy();
707 if (proxy === undefined)
708 throw new Error("Error: Address doesn't match proxy");
710 const nonce = await api.getNonce({ ship: curPoint, proxy });
712 return sendL2Transaction({
715 type: 'cancelEscape',
718 }, [api, point, sendL2Transaction, wallet]);
720 const getPointsDetails = useCallback(
722 ownedPoints: number[],
723 incomingPoints: number[],
724 managingPoints: number[],
725 votingPoints: number[],
726 spawningPoints: number[]
730 await Promise.all(ownedPoints.map(initPoint)),
731 await Promise.all(incomingPoints.map(initPoint)),
732 await Promise.all(managingPoints.map(initPoint)),
733 await Promise.all(votingPoints.map(initPoint)),
734 await Promise.all(spawningPoints.map(initPoint)),
738 allPoints.reduce((acc: Points, cur: (Point | undefined)[]) => {
751 [setPoints, initPoint]
754 // On load, get initial config
761 }, [config, fetchConfig]);
770 configureNetworkKeys,
774 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