Skip to content

Commit

Permalink
feat: checks for indexer and keychain compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
pedrorezende committed Dec 26, 2024
1 parent 9af3ad3 commit 6382ce6
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 10 deletions.
56 changes: 56 additions & 0 deletions apps/namadillo/src/App/Common/FixedWarningBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { indexerHeartbeatAtom } from "atoms/settings";
import { useNamadaKeychain } from "hooks/useNamadaKeychain";
import { useAtomValue } from "jotai";
import { ReactNode, useEffect, useState } from "react";
import { IoWarning } from "react-icons/io5";
import {
checkIndexerCompatibilityErrors,
checkKeychainCompatibilityError,
} from "utils/compatibility";

export const FixedWarningBanner = (): JSX.Element => {
const [errorMessage, setErrorMessage] = useState<ReactNode | undefined>();
const indexerHealth = useAtomValue(indexerHeartbeatAtom);
const keychain = useNamadaKeychain();

const verifyKeychainVersion = async (): Promise<void> => {
const namadaKeychain = await keychain.namadaKeychain.get();
if (namadaKeychain) {
const version = namadaKeychain.version();
const versionErrorMessage = checkKeychainCompatibilityError(version);
if (versionErrorMessage) {
setErrorMessage(versionErrorMessage);
}
}
};

const verifyIndexerVersion = async (): Promise<void> => {
const versionErrorMessage = checkIndexerCompatibilityErrors(
indexerHealth.data?.version || ""
);
if (versionErrorMessage) {
setErrorMessage(versionErrorMessage);
}
};

useEffect(() => {
verifyKeychainVersion();
}, [keychain]);

useEffect(() => {
indexerHealth.isSuccess && verifyIndexerVersion();
}, [indexerHealth]);

if (!errorMessage) return <></>;

return (
<div className="fixed bottom-0 left-0 w-full bg-yellow z-[9999]">
<div className="flex flex-row justify-center items-center gap-1 px-12 py-3 text-sm [&_a]:underline">
<strong className="inline-flex items-center">
<IoWarning /> WARNING:{" "}
</strong>
<div>{errorMessage}</div>
</div>
</div>
);
};
2 changes: 2 additions & 0 deletions apps/namadillo/src/App/Layout/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FixedWarningBanner } from "App/Common/FixedWarningBanner";
import { ReactNode, useState } from "react";
import { twMerge } from "tailwind-merge";
import { AppHeader } from "./AppHeader";
Expand Down Expand Up @@ -42,6 +43,7 @@ export const AppLayout = ({
</aside>
<main className="min-h-full">{children}</main>
</div>
<FixedWarningBanner />
</div>
);
};
13 changes: 8 additions & 5 deletions apps/namadillo/src/atoms/settings/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { atomWithStorage } from "jotai/utils";
import { SettingsStorage } from "types";
import {
fetchDefaultTomlConfig,
isIndexerAlive,
getIndexerHealth,
isMaspIndexerAlive,
isRpcAlive,
} from "./services";
Expand Down Expand Up @@ -175,7 +175,10 @@ export const maspIndexerUrlAtom = atom((get) => {
export const updateIndexerUrlAtom = atomWithMutation(() => {
return {
mutationKey: ["update-indexer-url"],
mutationFn: changeSettingsUrl("indexerUrl", isIndexerAlive),
mutationFn: changeSettingsUrl(
"indexerUrl",
async (url: string): Promise<boolean> => !!(await getIndexerHealth(url))
),
};
});

Expand All @@ -200,9 +203,9 @@ export const indexerHeartbeatAtom = atomWithQuery((get) => {
refetchOnWindowFocus: true,
refetchInterval: 10_000,
queryFn: async () => {
const valid = await isIndexerAlive(indexerUrl);
if (!valid) throw "Unable to verify indexer heartbeat";
return true;
const indexerInfo = await getIndexerHealth(indexerUrl);
if (!indexerInfo) throw "Unable to verify indexer heartbeat";
return indexerInfo;
},
};
});
16 changes: 11 additions & 5 deletions apps/namadillo/src/atoms/settings/services.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { Configuration, DefaultApi } from "@namada/indexer-client";
import { isUrlValid } from "@namada/utils";
import toml from "toml";
import { SettingsTomlOptions } from "types";
import { SettingsTomlOptions, TempIndexerHealthType } from "types";

export const isIndexerAlive = async (url: string): Promise<boolean> => {
export const getIndexerHealth = async (
url: string
): Promise<TempIndexerHealthType | undefined> => {
if (!isUrlValid(url)) {
return false;
return;
}

try {
const configuration = new Configuration({ basePath: url });
const api = new DefaultApi(configuration);
const response = await api.healthGet();
return response.status === 200;

// TODO:update when indexer swagger is fixed
// @ts-expect-error Indexer swagger is out of date
return response.data as TempIndexerHealthType;
} catch {
return false;
return;
}
};

Expand Down
4 changes: 4 additions & 0 deletions apps/namadillo/src/compatibility.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"keychain": "0.3.x",
"indexer": "1.1.x"
}
6 changes: 6 additions & 0 deletions apps/namadillo/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,9 @@ export type LocalnetToml = {
chain_1_channel: string;
chain_2_channel: string;
};

// TODO: remove this after indexer swagger gets fixed
export type TempIndexerHealthType = {
version: string;
commit: string;
};
94 changes: 94 additions & 0 deletions apps/namadillo/src/utils/compatibility.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { routes } from "App/routes";
import compatibilty from "compatibility.json";
import { wallets } from "integrations";
import { Link } from "react-router-dom";
import semverSatisfies from "semver/functions/satisfies";
import semverLtr from "semver/ranges/ltr";

// Checks if the versions are compatible using semantic versioning (semver).
// Returns true if the versions are compatible, -1 if the version is outdated,
// or 1 if a required version is lower than the version provided.
const checkVersionsCompatible = (
currentVersion: string,
requiredVersion: string
): -1 | 1 | boolean => {
if (semverSatisfies(currentVersion, requiredVersion)) {
return true;
}
return semverLtr(currentVersion, requiredVersion) ? -1 : 1;
};

export const checkIndexerCompatibilityErrors = (
indexerVersion: string
): React.ReactNode => {
const requiredVersion = compatibilty.indexer;
const checkResult = checkVersionsCompatible(indexerVersion, requiredVersion);

if (checkResult === -1) {
return (
<>
You&apos;re using an outdated version of Namada Indexer. Please update
your indexer URL in the{" "}
<Link to={routes.settingsAdvanced}>Advanced Settings</Link> section.
</>
);
}

if (checkResult === 1) {
return (
<>
Your Namadillo version is not compatible with the current Namada
Indexer. Please upgrade your web interface or pick a different one from
the <a href="https://namada.net/apps#interfaces">Namada Apps</a> list.
</>
);
}

return "";
};

export const checkKeychainCompatibilityError = (
keychainVersion: string
): React.ReactNode => {
const targetKeychainVersion = compatibilty.keychain;
const checkResult = checkVersionsCompatible(
keychainVersion,
targetKeychainVersion
);

if (checkResult === -1) {
return (
<>
Your Namada Keychain version is outdated. Please upgrade it using{" "}
<a
href={wallets.namada.downloadUrl.chrome}
target="_blank"
rel="nofollow noreferrer"
>
Chrome store
</a>{" "}
or{" "}
<a
href={wallets.namada.downloadUrl.firefox}
target="_blank"
rel="nofollow noreferrer"
>
Firefox addons
</a>{" "}
websites.
</>
);
}

if (checkResult === 1) {
return (
<>
Your Namadillo version is not compatible with the keychain installed.
Please upgrade your web interface or pick a different one from the{" "}
<a href="https://namada.net/apps#interfaces">Namada Apps</a> list.
</>
);
}

return "";
};

0 comments on commit 6382ce6

Please sign in to comment.