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';
45interface UpdateParams {
51 intervalTime?: number;
54export default function useRoller() {
62 const { connector } = useWalletConnect();
63 const { pointCursor }: any = usePointCursor();
64 const { web3, contracts }: any = useNetwork();
65 const allPoints: any = usePointCache();
66 const getDetails = allPoints?.getDetails;
74 setPendingTransactions,
81 const [config, setConfig] = useState<Config | null>(null);
83 const { options } = useRollerOptions();
85 const api = useMemo(() => {
86 return new RollerRPCAPI(options);
93 encryptionSecret: authToken.getOrElse('default'),
98 const getNextQuotaTime = useCallback(
99 (nextSlice: number) => {
104 setNextQuotaTime(next);
105 getNextQuotaTime(next);
107 .catch(err => console.warn(err));
108 }, nextSlice - new Date().getTime());
110 [api, setNextQuotaTime]
113 const fetchConfig = useCallback(async () => {
114 getNextQuotaTime(Date.now() + 1000);
119 setNextBatchTime(config.nextBatch);
122 // TODO: more elegant error handling
124 '[fetchConfig:failed] check the roller connection?\n',
128 }, [api, getNextQuotaTime, setNextBatchTime]);
130 const quotaReached = useCallback(() => {
131 if (point.l2Quota <= 0) {
133 `You have reached your weekly L2 transaction limit. You will get another
134 ${point.l2Allowance} transactions on ${ddmmmYYYY(nextQuotaTime)}.`
138 return point.l2Quota <= 0;
139 }, [nextQuotaTime, point, setModalText]);
141 const initPoint = useCallback(
142 async (point: string | number) => {
143 const _wallet = wallet.getOrElse(null);
144 const _contracts = contracts.getOrElse(null);
146 if (!_wallet || !_contracts) {
150 const pointNum = Number(point);
152 const rawDetails = await api.getPoint(pointNum);
153 const isL2 = isL2Spawn(rawDetails?.dominion);
154 const l2Quota = isL2 ? await api.getRemainingQuota(pointNum) : 0;
155 const l2Allowance = isL2 ? await api.getAllowance(pointNum) : 0;
157 const details = isL2Spawn(rawDetails?.dominion)
158 ? toL1Details(rawDetails)
159 : await azimuth.azimuth.getPoint(_contracts, point);
164 address: _wallet.address,
171 // Try getting the details from L1
173 const _contracts = need.contracts(contracts);
174 const details = await azimuth.azimuth.getPoint(_contracts, point);
178 address: _wallet.address,
182 // Just return a placeholder Point
183 const details: L1Point = toL1Details();
187 address: _wallet.address,
193 [api, wallet, contracts]
196 const getAndUpdatePoint = useCallback(
197 async (point: number) => {
198 const updatedPoint = await initPoint(point);
199 updatePoint(updatedPoint);
201 [initPoint, updatePoint]
204 const checkForUpdates = useCallback(
211 intervalTime = TEN_SECONDS,
212 }: UpdateParams) => {
213 if (l1Txn) storePendingL1Txn(l1Txn);
216 const check = async () => {
217 const updatedPoint = await initPoint(point);
218 const changedField = !points[point]
220 : updatedPoint.getChangedField(points[point], field);
223 console.log(`CHECKING FOR ${changedField} UPDATES:`, updatedPoint);
226 if (!updatedPoint.isPlaceholder && changedField) {
227 updatePoint(updatedPoint);
229 clearInterval(interval);
232 if (l1Txn) deletePendingL1Txn(l1Txn);
235 `${message || getUpdatedPointMessage(updatedPoint, 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,
302 const getPoints = useCallback(
303 async (proxy: Proxy, address: EthAddress) => {
304 const points: Ship[] =
306 ? await api.getOwnedPoints(address)
308 ? await api.getManagerFor(address)
310 ? await api.getVotingFor(address)
311 : proxy === 'transfer'
312 ? await api.getTransferringFor(address)
314 ? await api.getSpawningFor(address)
322 const getPendingTransactions = useCallback(async () => {
324 if (!point.isDefault) {
325 const newPending = await api.getPendingByShip(point.value);
326 setPendingTransactions(newPending);
329 console.warn('ERROR GETTING PENDING', error);
331 }, [api, setPendingTransactions, point]);
333 interface ConfigureKeysParams {
335 customNetworkSeed?: string;
338 const configureNetworkKeys = useCallback(
339 async ({ breach, customNetworkSeed }: ConfigureKeysParams) => {
340 if (quotaReached()) {
343 const _wallet = wallet.getOrElse(null);
344 const _web3 = web3.getOrElse(null);
345 if (!_wallet || !_web3 || point.isDefault) {
346 // not using need because we want a custom error
347 throw new Error('Internal Error: Missing Wallet/Details');
350 const proxy = point.getManagerProxy();
352 if (proxy === undefined)
353 throw new Error("Error: Address doesn't match proxy");
355 const nonce = await api.getNonce({
360 const nextRevision = point.keyRevisionNumber + 1;
361 const networkSeed = customNetworkSeed
363 : await attemptNetworkSeedDerivation({
370 revision: nextRevision,
372 const txHash = await submitL2Transaction({
379 type: 'configureKeys',
386 return api.getPendingTx(txHash);
389 // eslint-disable-line
403 const performL2Reticket = async ({
410 }: ReticketParams) => {
411 const azimuthPoint = await api.getPoint(point);
413 const _web3 = web3.getOrElse(null);
414 let nonce = await api.getNonce({ ship: point, proxy });
415 const progress = onUpdate
416 ? (state: number) => onUpdate({ type: 'progress', state })
421 // 1. Update networking keys
422 progress(TRANSACTION_PROGRESS.GENERATING);
423 const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10);
424 const nextRevision = networkRevision + 1;
425 const networkSeed = await deriveNetworkSeedFromUrbitWallet(
429 const configureKeysRequest = await submitL2Transaction({
437 type: 'configureKeys',
442 requests.push(configureKeysRequest);
444 // 2. Set Management Proxy
445 progress(TRANSACTION_PROGRESS.SIGNING);
446 const registerMgmtRequest = await registerProxyAddress(
459 requests.push(registerMgmtRequest);
461 // 3. Set Spawn Proxy
462 progress(TRANSACTION_PROGRESS.FUNDING);
463 if (!isPlanet(Number(point))) {
464 const registerSpawnRequest = await registerProxyAddress(
471 toWallet?.spawn?.keys?.address || to,
477 requests.push(registerSpawnRequest);
481 progress(TRANSACTION_PROGRESS.TRANSFERRING);
482 const transferTxRequest = await submitL2Transaction({
487 type: 'transferPoint',
494 requests.push(transferTxRequest);
496 progress(TRANSACTION_PROGRESS.CLEANING);
497 const hashes = await Promise.all(requests);
499 progress(TRANSACTION_PROGRESS.DONE);
503 const setProxyAddress = useCallback(
504 async (proxyType: Proxy, address: EthAddress) => {
505 if (quotaReached()) {
509 const _wallet = wallet.getOrElse(null);
510 const _web3 = web3.getOrElse(null);
513 // not using need because we want a custom error
514 throw new Error('Internal Error: Missing Wallet');
517 if (point.isDefault) {
518 // not using need because we want a custom error
519 throw new Error('Internal Error: Invalid point');
522 const proxy = point.getAddressProxy(proxyType);
524 if (proxy === undefined)
525 throw new Error("Error: Address doesn't match expected proxy");
527 const nonce = await api.getNonce({ ship: point.value, proxy });
529 const txHash = await registerProxyAddress(
542 return api.getPendingTx(txHash);
544 [api, connector, point, wallet, web3, walletType, quotaReached]
547 const changeSponsorship = useCallback(
550 type: 'adopt' | 'detach' | 'reject',
551 skipQuotaCheck = false
553 if (!skipQuotaCheck && quotaReached()) {
557 const _wallet = wallet.getOrElse(null);
558 const _web3 = web3.getOrElse(null);
559 const ship = point.value;
561 const proxy = point.getManagerProxy();
563 if (proxy === undefined)
564 throw new Error("Error: Address doesn't match expected proxy");
566 const nonce = await api.getNonce({ ship, proxy });
567 // const txHash = await adopt(api, _wallet, point.value, proxy, nonce, ship);
568 const txHash = await submitL2Transaction({
575 address: _wallet.address,
582 return api.getPendingTx(txHash);
584 [api, point, wallet, walletType, web3, connector, quotaReached]
587 const transferPoint = useCallback(
588 async (address: EthAddress, reset?: boolean) => {
589 if (quotaReached()) {
593 const _point = need.point(pointCursor);
594 const _wallet = wallet.getOrElse(null);
595 const _web3 = web3.getOrElse(null);
598 // not using need because we want a custom error
599 throw new Error('Internal Error: Missing Wallet');
602 const proxy = point.getTransferProxy();
604 if (proxy === undefined)
605 throw new Error("Error: Address doesn't match proxy");
607 const nonce = await api.getNonce({ ship: _point, proxy });
609 const txHash = await submitL2Transaction({
616 type: 'transferPoint',
623 return api.getPendingTx(txHash);
625 [api, connector, point, pointCursor, wallet, web3, walletType, quotaReached]
628 const sendL2Transaction = useCallback(
629 async (params: SendL2Params) => {
630 if (quotaReached()) {
634 const _wallet = wallet.getOrElse(null);
635 const _web3 = web3.getOrElse(null);
637 if (!_wallet || point.isDefault) {
638 // not using need because we want a custom error
639 throw new Error('Internal Error: Missing Wallet or Point Info');
642 return submitL2Transaction({
652 [api, connector, point, wallet, web3, walletType, quotaReached]
655 const changeSponsor = useCallback(
656 async (newSponsor: number) => {
657 if (point.isDefault) {
658 throw new Error('Internal Error: point is default');
661 const curPoint = point.value;
662 const _wallet = wallet.getOrElse(null);
665 // not using need because we want a custom error
666 throw new Error('Internal Error: Missing Wallet');
669 const proxy = point.getManagerProxy();
671 if (proxy === undefined)
672 throw new Error("Error: Address doesn't match proxy");
674 const nonce = await api.getNonce({ ship: curPoint, proxy });
676 return sendL2Transaction({
684 [api, point, sendL2Transaction, wallet]
687 const cancelEscape = useCallback(async () => {
688 if (point.isDefault) {
689 throw new Error('Internal Error: point is default');
692 const curPoint = point.value;
693 const _wallet = wallet.getOrElse(null);
696 // not using need because we want a custom error
697 throw new Error('Internal Error: Missing Wallet');
700 const proxy = point.getManagerProxy();
702 if (proxy === undefined)
703 throw new Error("Error: Address doesn't match proxy");
705 const nonce = await api.getNonce({ ship: curPoint, proxy });
707 return sendL2Transaction({
710 type: 'cancelEscape',
713 }, [api, point, sendL2Transaction, wallet]);
715 const getPointsDetails = useCallback(
717 ownedPoints: number[],
718 incomingPoints: number[],
719 managingPoints: number[],
720 votingPoints: number[],
721 spawningPoints: number[]
725 await Promise.all(ownedPoints.map(initPoint)),
726 await Promise.all(incomingPoints.map(initPoint)),
727 await Promise.all(managingPoints.map(initPoint)),
728 await Promise.all(votingPoints.map(initPoint)),
729 await Promise.all(spawningPoints.map(initPoint)),
733 allPoints.reduce((acc: Points, cur: (Point | undefined)[]) => {
746 [setPoints, initPoint]
749 // On load, get initial config
756 }, [config, fetchConfig]);
765 configureNetworkKeys,
769 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