Compare commits

..
Author SHA1 Message Date
asiaznik d3f5c2fc32 fix(proposals): protocol upgrade notification block querying 2023-08-31 13:48:25 +02:00
6 changed files with 91 additions and 99 deletions
@@ -26,22 +26,36 @@ export const ProtocolUpgradeInProgressNotification = () => {
const [nextUpgrade] = useLocalStorageSnapshot( const [nextUpgrade] = useLocalStorageSnapshot(
NEXT_PROTOCOL_UPGRADE_PROPOSAL_SNAPSHOT NEXT_PROTOCOL_UPGRADE_PROPOSAL_SNAPSHOT
); );
const { blocksRising, block } = useBlockRising();
const detailsLink = useProtocolUpgradeProposalLink(); const detailsLink = useProtocolUpgradeProposalLink();
let vegaReleaseTag: string | undefined; let vegaReleaseTag: string | undefined;
let upgradeBlockHeight: string | undefined; let upgradeBlockHeight: string | undefined;
if (error && !data && nextUpgrade && ALLOW_STORED_PROPOSAL_DATA) { const hasData = data && !error;
const hasStoredData = nextUpgrade && ALLOW_STORED_PROPOSAL_DATA;
if (hasData) {
// gets tag and height from the data api
vegaReleaseTag = data.vegaReleaseTag;
upgradeBlockHeight = data.upgradeBlockHeight;
} else if (hasStoredData) {
// gets tag and height from stored value if data api is unavailable
try { try {
const stored = JSON.parse(nextUpgrade) as StoredNextProtocolUpgradeData; const stored = JSON.parse(nextUpgrade) as StoredNextProtocolUpgradeData;
vegaReleaseTag = stored.vegaReleaseTag; vegaReleaseTag = stored.vegaReleaseTag;
upgradeBlockHeight = stored.upgradeBlockHeight; upgradeBlockHeight = stored.upgradeBlockHeight;
} catch { } catch {
// no op // NOOP - could not parse stored data
} }
} }
const hasUpgradeInfo = vegaReleaseTag && upgradeBlockHeight;
const { blocksRising, block } = useBlockRising(
// skips querying blocks if there's no upgrade information available
!hasUpgradeInfo
);
/** /**
* If upgrade is in progress then none of the nodes should produce blocks, * If upgrade is in progress then none of the nodes should produce blocks,
* same should be with the tendermint block info otherwise it's a network * same should be with the tendermint block info otherwise it's a network
@@ -50,10 +64,7 @@ export const ProtocolUpgradeInProgressNotification = () => {
* Once the networks is back then the notification disappears. * Once the networks is back then the notification disappears.
*/ */
const upgradeInProgress = const upgradeInProgress =
vegaReleaseTag && hasUpgradeInfo && !blocksRising && block <= Number(upgradeBlockHeight);
upgradeBlockHeight &&
!blocksRising &&
block <= Number(upgradeBlockHeight);
if (!upgradeInProgress) return null; if (!upgradeInProgress) return null;
@@ -15,31 +15,33 @@ const CHECK_INTERVAL = 5000; // ms
*/ */
const ALLOW_STALE = 2; // times -> MAX(this, 1) * CHECK_INTERVAL ~> min check time const ALLOW_STALE = 2; // times -> MAX(this, 1) * CHECK_INTERVAL ~> min check time
export const useBlockRising = () => { export const useBlockRising = (skip = false) => {
const [blocksRising, setBlocksRising] = useState(true); const [blocksRising, setBlocksRising] = useState(true);
const [block, setBlock] = useState(0); const [block, setBlock] = useState(0);
const nodes = useEnvironment((state) => state.nodes); const nodes = useEnvironment((state) => state.nodes);
const clients = useMemo(() => { const clients = useMemo(() => {
return nodes.map( return nodes.map((n) => {
(n) => if (n && n.length > 0) {
n && const client = createClient({
n.length > 0 &&
createClient({
url: n, url: n,
cacheConfig: undefined, cacheConfig: undefined,
retry: false, retry: false,
connectToDevTools: false, connectToDevTools: false,
connectToHeaderStore: true, connectToHeaderStore: true,
}) });
); return client;
}
return undefined;
});
}, [nodes]); }, [nodes]);
const { refetch: fetchBlockInfo } = useBlockInfo(); const { refetch: fetchBlockInfo } = useBlockInfo();
useEffect(() => { useEffect(() => {
if (skip) return;
let stale = 0; let stale = 0;
let prev = 0; let prev = 0;
const check = async () => { const check = async () => {
const queries = clients.map((client, index) => const queries = clients.map((client) =>
client client
? client ? client
.query<BlockStatisticsQuery>({ .query<BlockStatisticsQuery>({
@@ -47,18 +49,16 @@ export const useBlockRising = () => {
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
errorPolicy: 'ignore', errorPolicy: 'ignore',
}) })
.catch((err) => .catch(() => {
Promise.reject( // NOOP - could not retrieve statistics for that node (network error)
`could not retrieve statistics from ${nodes[index]}` })
)
)
: undefined : undefined
); );
const blockInfo = await fetchBlockInfo(); const blockInfo = await fetchBlockInfo();
const results = (await Promise.allSettled(compact(queries))).map( const results = (await Promise.allSettled(compact(queries))).map(
(res) => { (res) => {
if (res && res.status === 'fulfilled') { if (res && res.status === 'fulfilled' && res.value) {
return res.value.data.statistics; return res.value.data.statistics;
} else { } else {
return undefined; return undefined;
@@ -86,7 +86,7 @@ export const useBlockRising = () => {
return () => { return () => {
clearInterval(interval); clearInterval(interval);
}; };
}, [clients, fetchBlockInfo, blocksRising, nodes]); }, [clients, fetchBlockInfo, blocksRising, nodes, skip]);
return { blocksRising, block }; return { blocksRising, block };
}; };
@@ -1,7 +1,6 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { import {
Dialog, Dialog,
ExternalLink,
Intent, Intent,
Pill, Pill,
TradingButton, TradingButton,
@@ -40,7 +39,7 @@ import { useVegaWallet } from '../use-vega-wallet';
import { InjectedConnectorForm } from './injected-connector-form'; import { InjectedConnectorForm } from './injected-connector-form';
import { isBrowserWalletInstalled } from '../utils'; import { isBrowserWalletInstalled } from '../utils';
import { useIsWalletServiceRunning } from '../use-is-wallet-service-running'; import { useIsWalletServiceRunning } from '../use-is-wallet-service-running';
import { SnapStatus, useSnapStatus } from '../use-snap-status'; import { useIsSnapRunning } from '../use-is-snap-running';
import { useVegaWalletDialogStore } from './vega-wallet-dialog-store'; import { useVegaWalletDialogStore } from './vega-wallet-dialog-store';
export const CLOSE_DELAY = 1700; export const CLOSE_DELAY = 1700;
@@ -159,7 +158,7 @@ const ConnectDialogContainer = ({
appChainId appChainId
); );
const snapStatus = useSnapStatus( const isSnapRunning = useIsSnapRunning(
DEFAULT_SNAP_ID, DEFAULT_SNAP_ID,
Boolean(connectors['snap']) Boolean(connectors['snap'])
); );
@@ -184,7 +183,7 @@ const ConnectDialogContainer = ({
setWalletUrl={setWalletUrl} setWalletUrl={setWalletUrl}
onSelect={handleSelect} onSelect={handleSelect}
isDesktopWalletRunning={isDesktopWalletRunning} isDesktopWalletRunning={isDesktopWalletRunning}
snapStatus={snapStatus} isSnapRunning={isSnapRunning}
/> />
)} )}
</ConnectDialogContent> </ConnectDialogContent>
@@ -199,14 +198,14 @@ const ConnectorList = ({
walletUrl, walletUrl,
setWalletUrl, setWalletUrl,
isDesktopWalletRunning, isDesktopWalletRunning,
snapStatus, isSnapRunning,
}: { }: {
connectors: Connectors; connectors: Connectors;
onSelect: (type: WalletType) => void; onSelect: (type: WalletType) => void;
walletUrl: string; walletUrl: string;
setWalletUrl: (value: string) => void; setWalletUrl: (value: string) => void;
isDesktopWalletRunning: boolean | null; isDesktopWalletRunning: boolean | null;
snapStatus: SnapStatus; isSnapRunning: boolean | null;
}) => { }) => {
const { pubKey, links } = useVegaWallet(); const { pubKey, links } = useVegaWallet();
const title = isBrowserWalletInstalled() const title = isBrowserWalletInstalled()
@@ -250,7 +249,7 @@ const ConnectorList = ({
</div> </div>
{connectors['snap'] !== undefined ? ( {connectors['snap'] !== undefined ? (
<div> <div>
{snapStatus === SnapStatus.INSTALLED ? ( {isSnapRunning ? (
<ConnectionOption <ConnectionOption
type="snap" type="snap"
text={ text={
@@ -268,34 +267,22 @@ const ConnectorList = ({
}} }}
/> />
) : ( ) : (
<> <ConnectionOption
<ConnectionOption type="snap"
type="snap" text={
disabled={snapStatus === SnapStatus.NOT_SUPPORTED} <>
text={ <div className="flex items-center justify-center w-full h-full text-base gap-1">
<> {t('Install Vega MetaMask Snap')}
<div className="flex items-center justify-center w-full h-full text-base gap-1"> </div>
{t('Install Vega MetaMask Snap')} <div className="absolute top-0 flex items-center h-8 right-1">
</div> <VegaIcon name={VegaIconNames.METAMASK} size={24} />
<div className="absolute top-0 flex items-center h-8 right-1"> </div>
<VegaIcon name={VegaIconNames.METAMASK} size={24} /> </>
</div> }
</> onClick={() => {
} requestSnap(DEFAULT_SNAP_ID);
onClick={() => { }}
requestSnap(DEFAULT_SNAP_ID); />
}}
/>
{snapStatus === SnapStatus.NOT_SUPPORTED ? (
<p className="pt-2 text-sm text-default">
{t('No MetaMask version that supports snaps detected.')}{' '}
{t('Learn more about')}{' '}
<ExternalLink href="https://metamask.io/snaps/">
MetaMask Snaps
</ExternalLink>
</p>
) : null}
</>
)} )}
</div> </div>
) : null} ) : null}
+8 -4
View File
@@ -118,10 +118,14 @@ export const getSnap = async (
snapId: string, snapId: string,
version?: string version?: string
): Promise<Snap | undefined> => { ): Promise<Snap | undefined> => {
const snaps = await getSnaps(); try {
return Object.values(snaps).find( const snaps = await getSnaps();
(snap) => snap.id === snapId && (!version || snap.version === version) return Object.values(snaps).find(
); (snap) => snap.id === snapId && (!version || snap.version === version)
);
} catch (e) {
return undefined;
}
}; };
export const invokeSnap = async <T>( export const invokeSnap = async <T>(
+27
View File
@@ -0,0 +1,27 @@
import { useEffect, useState } from 'react';
import { getSnap } from './connectors';
const INTERVAL = 2_000;
export const useIsSnapRunning = (snapId: string, shouldCheck: boolean) => {
const [running, setRunning] = useState(false);
useEffect(() => {
if (!shouldCheck) return;
const checkState = async () => {
const snap = await getSnap(snapId);
setRunning(!!snap);
};
const i = setInterval(() => {
checkState();
}, INTERVAL);
checkState();
return () => {
clearInterval(i);
};
}, [snapId, shouldCheck]);
return running;
};
-37
View File
@@ -1,37 +0,0 @@
import { useEffect, useState } from 'react';
import { getSnap } from './connectors';
const INTERVAL = 2_000;
export enum SnapStatus {
NOT_SUPPORTED,
INSTALLED,
NOT_INSTALLED,
}
export const useSnapStatus = (snapId: string, shouldCheck: boolean) => {
const [status, setStatus] = useState<SnapStatus>(SnapStatus.NOT_INSTALLED);
useEffect(() => {
if (!shouldCheck) return;
const checkState = async () => {
try {
const snap = await getSnap(snapId);
setStatus(snap ? SnapStatus.INSTALLED : SnapStatus.NOT_INSTALLED);
} catch (err) {
setStatus(SnapStatus.NOT_SUPPORTED);
}
};
const i = setInterval(() => {
checkState();
}, INTERVAL);
checkState();
return () => {
clearInterval(i);
};
}, [snapId, shouldCheck]);
return status;
};