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);
148 const rawDetails = await api.getPoint(pointNum);
149 const isL2 = isL2Spawn(rawDetails?.dominion);
151 const details = toL1Details(rawDetails);
155 const l2Quota = isL2 ? await api.getRemainingQuota(pointNum) : 0;
156 const l2Allowance = isL2 ? await api.getAllowance(pointNum) : 0;
161 address: _wallet.address,
167 const _contracts = need.contracts(contracts);
168 const l1Details = await azimuth.azimuth.getPoint(_contracts, point);
170 l1Details.sponsor = details.sponsor;
171 l1Details.escapeRequested = details.escapeRequested;
172 l1Details.escapeRequestedTo = details.escapeRequestedTo;
177 address: _wallet.address,
181 // Just return a placeholder Point
182 const details: L1Point = toL1Details();
186 address: _wallet.address,
191 [api, wallet, contracts]
194 const getAndUpdatePoint = useCallback(
195 async (point: number) => {
196 const updatedPoint = await initPoint(point);
197 updatePoint(updatedPoint);
199 [initPoint, updatePoint]
202 const checkForUpdates = useCallback(
209 intervalTime = TEN_SECONDS,
210 }: UpdateParams) => {
211 const pVal = typeof point === 'number' ? point : point.value;
212 const p = await initPoint(pVal);
214 if (l1Txn) storePendingL1Txn(l1Txn);
217 const check = async () => {
218 const changedField = !points[p.value]
220 : p.getChangedField(points[p.value], field);
223 console.log(`CHECKING FOR ${changedField} UPDATES:`, p.patp);
226 if (!p.isPlaceholder && changedField) {
229 clearInterval(interval);
232 if (l1Txn) deletePendingL1Txn(l1Txn);
235 `${message || getUpdatedPointMessage(p, changedField)}`
241 interval = setInterval(check, intervalTime);
244 [points, initPoint, updatePoint, storePendingL1Txn, deletePendingL1Txn]
247 const spawnPoint = useCallback(
248 async (pointToSpawn: number, destinationAddress: string) => {
249 if (quotaReached()) {
253 const _point = need.point(pointCursor);
254 const _contracts = contracts.getOrElse(null);
255 const _web3 = web3.getOrElse(null);
256 const _wallet = wallet.getOrElse(null);
257 const _authToken = authToken.getOrElse(null);
259 if (!_contracts || !_web3 || !_wallet || !_authToken) {
260 // not using need because we want a custom error
261 throw new Error('Internal Error: Missing Contracts/Web3/Wallet');
264 const proxy = point.getSpawnProxy();
266 if (proxy === undefined)
267 throw new Error("Error: Address doesn't match proxy");
269 const nonce = await api.getNonce({
274 await submitL2Transaction({
276 address: destinationAddress,
306 const getPoints = useCallback(
307 async (proxy: Proxy, address: EthAddress) => {
308 const points: Ship[] =
310 ? await api.getOwnedPoints(address)
312 ? await api.getManagerFor(address)
314 ? await api.getVotingFor(address)
315 : proxy === 'transfer'
316 ? await api.getTransferringFor(address)
318 ? await api.getSpawningFor(address)
326 const getPendingTransactions = useCallback(async () => {
328 if (!point.isDefault) {
329 const newPending = await api.getPendingByShip(point.value);
330 setPendingTransactions(newPending);
333 console.warn('ERROR GETTING PENDING', error);
335 }, [api, setPendingTransactions, point]);
337 interface ConfigureKeysParams {
339 customNetworkSeed?: string;
342 const configureNetworkKeys = useCallback(
343 async ({ breach, customNetworkSeed }: ConfigureKeysParams) => {
344 if (quotaReached()) {
347 const _wallet = wallet.getOrElse(null);
348 const _web3 = web3.getOrElse(null);
349 if (!_wallet || !_web3 || point.isDefault) {
350 // not using need because we want a custom error
351 throw new Error('Internal Error: Missing Wallet/Details');
354 const proxy = point.getManagerProxy();
356 if (proxy === undefined)
357 throw new Error("Error: Address doesn't match proxy");
359 const nonce = await api.getNonce({
364 const nextRevision = point.keyRevisionNumber + 1;
365 const networkSeed = customNetworkSeed
367 : await attemptNetworkSeedDerivation({
374 revision: nextRevision,
376 const txHash = await submitL2Transaction({
383 type: 'configureKeys',
392 return api.getPendingTx(txHash);
395 // eslint-disable-line
411 const performL2Reticket = async ({
418 }: ReticketParams) => {
419 const azimuthPoint = await api.getPoint(point);
421 const _web3 = web3.getOrElse(null);
422 let nonce = await api.getNonce({ ship: point, proxy });
423 const progress = onUpdate
424 ? (state: number) => onUpdate({ type: 'progress', state })
429 // 1. Update networking keys
430 progress(TRANSACTION_PROGRESS.GENERATING);
431 const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10);
432 const nextRevision = networkRevision + 1;
433 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
437 const configureKeysRequest = await submitL2Transaction({
445 type: 'configureKeys',
452 requests.push(configureKeysRequest);
454 // 2. Set Management Proxy
455 progress(TRANSACTION_PROGRESS.SIGNING);
456 const registerMgmtRequest = await registerProxyAddress(
471 requests.push(registerMgmtRequest);
473 // 3. Set Spawn Proxy
474 progress(TRANSACTION_PROGRESS.FUNDING);
475 if (!isPlanet(Number(point))) {
476 const registerSpawnRequest = await registerProxyAddress(
483 toWallet?.spawn?.keys?.address || to,
491 requests.push(registerSpawnRequest);
495 progress(TRANSACTION_PROGRESS.TRANSFERRING);
496 const transferTxRequest = await submitL2Transaction({
501 type: 'transferPoint',
510 requests.push(transferTxRequest);
512 progress(TRANSACTION_PROGRESS.CLEANING);
513 const hashes = await Promise.all(requests);
515 progress(TRANSACTION_PROGRESS.DONE);
519 const setProxyAddress = useCallback(
520 async (proxyType: Proxy, address: EthAddress) => {
521 if (quotaReached()) {
524 const _wallet = wallet.getOrElse(null);
525 const _web3 = web3.getOrElse(null);
528 // not using need because we want a custom error
529 throw new Error('Internal Error: Missing Wallet');
532 if (point.isDefault) {
533 // not using need because we want a custom error
534 throw new Error('Internal Error: Invalid point');
536 const proxy = point.getAddressProxy(proxyType);
538 if (proxy === undefined)
539 throw new Error("Error: Address doesn't match expected proxy");
541 const nonce = await api.getNonce({ ship: point.value, proxy });
542 const txHash = await registerProxyAddress(
556 return api.getPendingTx(txHash);
571 const changeSponsorship = useCallback(
574 type: 'adopt' | 'detach' | 'reject',
575 skipQuotaCheck = false
577 if (!skipQuotaCheck && quotaReached()) {
581 const _wallet = wallet.getOrElse(null);
582 const _web3 = web3.getOrElse(null);
583 const ship = point.value;
585 const proxy = point.getManagerProxy();
587 if (proxy === undefined)
588 throw new Error("Error: Address doesn't match expected proxy");
590 const nonce = await api.getNonce({ ship, proxy });
591 // const txHash = await adopt(api, _wallet, point.value, proxy, nonce, ship);
592 const txHash = await submitL2Transaction({
599 address: _wallet.address,
608 return api.getPendingTx(txHash);
623 const transferPoint = useCallback(
624 async (address: EthAddress, reset?: boolean) => {
625 if (quotaReached()) {
629 const _point = need.point(pointCursor);
630 const _wallet = wallet.getOrElse(null);
631 const _web3 = web3.getOrElse(null);
634 // not using need because we want a custom error
635 throw new Error('Internal Error: Missing Wallet');
638 const proxy = point.getTransferProxy();
640 if (proxy === undefined)
641 throw new Error("Error: Address doesn't match proxy");
643 const nonce = await api.getNonce({ ship: _point, proxy });
645 const txHash = await submitL2Transaction({
652 type: 'transferPoint',
661 return api.getPendingTx(txHash);
677 const sendL2Transaction = useCallback(
678 async (params: SendL2Params) => {
679 if (quotaReached()) {
683 const _wallet = wallet.getOrElse(null);
684 const _web3 = web3.getOrElse(null);
686 if (!_wallet || point.isDefault) {
687 // not using need because we want a custom error
688 throw new Error('Internal Error: Missing Wallet or Point Info');
691 return submitL2Transaction({
716 const changeSponsor = useCallback(
717 async (newSponsor: number) => {
718 if (point.isDefault) {
719 throw new Error('Internal Error: point is default');
722 const curPoint = point.value;
723 const _wallet = wallet.getOrElse(null);
726 // not using need because we want a custom error
727 throw new Error('Internal Error: Missing Wallet');
730 const proxy = point.getManagerProxy();
732 if (proxy === undefined)
733 throw new Error("Error: Address doesn't match proxy");
735 const nonce = await api.getNonce({ ship: curPoint, proxy });
737 return sendL2Transaction({
745 [api, point, sendL2Transaction, wallet]
748 const cancelEscape = useCallback(async () => {
749 if (point.isDefault) {
750 throw new Error('Internal Error: point is default');
753 const curPoint = point.value;
754 const _wallet = wallet.getOrElse(null);
757 // not using need because we want a custom error
758 throw new Error('Internal Error: Missing Wallet');
761 const proxy = point.getManagerProxy();
763 if (proxy === undefined)
764 throw new Error("Error: Address doesn't match proxy");
766 const nonce = await api.getNonce({ ship: curPoint, proxy });
768 return sendL2Transaction({
771 type: 'cancelEscape',
774 }, [api, point, sendL2Transaction, wallet]);
776 const getPointsDetails = useCallback(
778 ownedPoints: number[],
779 incomingPoints: number[],
780 managingPoints: number[],
781 votingPoints: number[],
782 spawningPoints: number[]
786 await Promise.all(ownedPoints.map(initPoint)),
787 await Promise.all(incomingPoints.map(initPoint)),
788 await Promise.all(managingPoints.map(initPoint)),
789 await Promise.all(votingPoints.map(initPoint)),
790 await Promise.all(spawningPoints.map(initPoint)),
794 allPoints.reduce((acc: Points, cur: (Point | undefined)[]) => {
807 [setPoints, initPoint]
810 // On load, get initial config
817 }, [config, fetchConfig]);
826 configureNetworkKeys,
830 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