Skip to content

Commit

Permalink
MASP indexer integration (#1120)
Browse files Browse the repository at this point in the history
* feat: code compiles

* feat: poc shielded sync

* feat: masp indexer url UI and rpc backup

* fix: shielded context for nodejs feature

* chore: cleanup test code

* fix: use namada_sdk version with masp indexer fix

* fix: make sync job blocking
  • Loading branch information
mateuszjasiuk authored Oct 3, 2024
1 parent cbdc8f7 commit e72d0f1
Show file tree
Hide file tree
Showing 25 changed files with 1,045 additions and 560 deletions.
11 changes: 10 additions & 1 deletion apps/extension/src/background/sdk/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const {
defaultTokenAddress = "tnam1qxgfw7myv4dh0qna4hq0xdg6lx77fzl7dcem8h7e",
} = process.env;

// Extension does not care about the MASP indexer - this will map to None in Rust
const MASP_INDEXER_URL = "";

export class SdkService {
private constructor(
private rpc: string,
Expand All @@ -27,6 +30,12 @@ export class SdkService {
}

getSdk(): Sdk {
return getSdk(this.cryptoMemory, this.rpc, "NOT USED DB NAME", this.token);
return getSdk(
this.cryptoMemory,
this.rpc,
MASP_INDEXER_URL,
"NOT USED DB NAME",
this.token
);
}
}
26 changes: 25 additions & 1 deletion apps/namadillo/src/App/Settings/Advanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { ActionButton, Input, Stack } from "@namada/components";
import { chainParametersAtom } from "atoms/chain";
import {
indexerUrlAtom,
maspIndexerUrlAtom,
rpcUrlAtom,
updateIndexerUrlAtom,
updateMaspIndexerUrlAtom,
updateRpcUrlAtom,
} from "atoms/settings";
import { useAtom, useAtomValue } from "jotai";
Expand All @@ -16,25 +18,32 @@ export const Advanced = (): JSX.Element => {
const [currentRpc] = useAtom(rpcUrlAtom);
const [rpcMutation] = useAtom(updateRpcUrlAtom);
const [currentIndexer] = useAtom(indexerUrlAtom);
const [currentMaspIndexer] = useAtom(maspIndexerUrlAtom);
const [indexerMutation] = useAtom(updateIndexerUrlAtom);
const [maspIndexerMutation] = useAtom(updateMaspIndexerUrlAtom);
const { data: chainParameters } = useAtomValue(chainParametersAtom);

const [rpc, setRpc] = useState(currentRpc);
const [indexer, setIndexer] = useState(currentIndexer);
const [maspIndexer, setMaspIndexer] = useState(currentMaspIndexer);

const onSubmit = async (e: React.FormEvent): Promise<void> => {
e.preventDefault();
try {
await Promise.all([
rpcMutation.mutateAsync(rpc),
indexerMutation.mutateAsync(indexer),
maspIndexerMutation.mutateAsync(maspIndexer),
]);
document.location.href =
location.state.backgroundLocation.pathname ?? location.pathname;
} catch {}
};

const isPending = rpcMutation.isPending || indexerMutation.isPending;
const isPending =
rpcMutation.isPending ||
indexerMutation.isPending ||
maspIndexerMutation.isPending;

return (
<form
Expand Down Expand Up @@ -71,6 +80,21 @@ export const Advanced = (): JSX.Element => {
}}
required
/>
<Input
type="text"
placeholder="Optional"
value={maspIndexer}
error={
maspIndexerMutation.error instanceof Error &&
maspIndexerMutation.error.message
}
label="MASP Indexer URL"
className="[&_input]:border-neutral-300"
onChange={(e) => {
setMaspIndexer(e.currentTarget.value);
maspIndexerMutation.reset();
}}
/>
<Input
type="text"
variant="ReadOnlyCopy"
Expand Down
39 changes: 32 additions & 7 deletions apps/namadillo/src/atoms/settings/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { Getter, Setter, atom, getDefaultStore } from "jotai";
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
import { atomWithStorage } from "jotai/utils";
import { SettingsStorage } from "types";
import { fetchDefaultTomlConfig, isIndexerAlive, isRpcAlive } from "./services";
import {
fetchDefaultTomlConfig,
isIndexerAlive,
isMaspIndexerAlive,
isRpcAlive,
} from "./services";

export type ConnectStatus = "idle" | "connecting" | "connected" | "error";

Expand Down Expand Up @@ -65,18 +70,21 @@ const changeSettings =
const changeSettingsUrl =
(
key: keyof SettingsStorage,
healthCheck: (url: string) => Promise<boolean>
healthCheck: (url: string) => Promise<boolean>,
allowEmpty = false
) =>
async (url: string) => {
const sanitizedUrl = sanitizeUrl(url);
if (!isUrlValid(sanitizedUrl)) {
async (inputUrl: string) => {
const allowedEmpty = allowEmpty && inputUrl.length === 0;
const url = allowedEmpty ? "" : sanitizeUrl(inputUrl);

if (!allowedEmpty && !isUrlValid(url)) {
throw new Error(
"Invalid URL. The URL should be valid starting with 'http', 'https', 'ws', or 'wss'."
);
}
if (await healthCheck(sanitizedUrl)) {
if (allowedEmpty || (await healthCheck(url))) {
const { get, set } = getDefaultStore();
changeSettings(key)(get, set, sanitizedUrl);
changeSettings(key)(get, set, url);
} else {
throw new Error(
"Couldn't reach the URL. Please provide a valid Namada URL service."
Expand Down Expand Up @@ -138,13 +146,30 @@ export const indexerUrlAtom = atom((get) => {
return "";
});

export const maspIndexerUrlAtom = atom((get) => {
const customIndexerUrl = get(settingsAtom).maspIndexerUrl;
if (customIndexerUrl) return customIndexerUrl;

const tomlIndexerUrl = get(defaultServerConfigAtom).data?.masp_indexer_url;
if (tomlIndexerUrl) return tomlIndexerUrl;

return "";
});

export const updateIndexerUrlAtom = atomWithMutation(() => {
return {
mutationKey: ["update-indexer-url"],
mutationFn: changeSettingsUrl("indexerUrl", isIndexerAlive),
};
});

export const updateMaspIndexerUrlAtom = atomWithMutation(() => {
return {
mutationKey: ["update-masp-indexer-url"],
mutationFn: changeSettingsUrl("maspIndexerUrl", isMaspIndexerAlive, true),
};
});

export const signArbitraryEnabledAtom = atom(
(get) => get(settingsAtom).signArbitraryEnabled,
changeSettings<boolean>("signArbitraryEnabled")
Expand Down
12 changes: 12 additions & 0 deletions apps/namadillo/src/atoms/settings/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ export const isIndexerAlive = async (url: string): Promise<boolean> => {
}
};

export const isMaspIndexerAlive = async (url: string): Promise<boolean> => {
if (!isUrlValid(url)) {
return false;
}
try {
const response = await fetch(`${url}/health`);
return response.ok && response.status === 200;
} catch {
return false;
}
};

export const isRpcAlive = async (url: string): Promise<boolean> => {
if (!isUrlValid(url)) {
return false;
Expand Down
11 changes: 9 additions & 2 deletions apps/namadillo/src/hooks/useSdk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import initSdk from "@heliax/namada-sdk/inline-init";
import { getSdk, Sdk } from "@heliax/namada-sdk/web";
import { QueryStatus, useQuery } from "@tanstack/react-query";
import { nativeTokenAddressAtom } from "atoms/chain";
import { rpcUrlAtom } from "atoms/settings";
import { maspIndexerUrlAtom, rpcUrlAtom } from "atoms/settings";
import { getDefaultStore, useAtomValue } from "jotai";
import {
createContext,
Expand Down Expand Up @@ -33,13 +33,20 @@ const initializeSdk = async (): Promise<Sdk> => {
const { cryptoMemory } = await initSdk();
const store = getDefaultStore();
const rpcUrl = store.get(rpcUrlAtom);
const maspIndexerUrl = store.get(maspIndexerUrlAtom);
const nativeToken = store.get(nativeTokenAddressAtom);

if (!nativeToken.isSuccess) {
throw "Native token not loaded";
}

const sdk = getSdk(cryptoMemory, rpcUrl, "", nativeToken.data);
const sdk = getSdk(
cryptoMemory,
rpcUrl,
maspIndexerUrl,
"",
nativeToken.data
);
return sdk;
};

Expand Down
2 changes: 2 additions & 0 deletions apps/namadillo/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type ChainSettings = {

export type SettingsTomlOptions = {
indexer_url?: string;
masp_indexer_url?: string;
rpc_url?: string;
};

Expand All @@ -61,6 +62,7 @@ export type SettingsStorage = {
fiat: CurrencyType;
rpcUrl?: string;
indexerUrl: string;
maspIndexerUrl?: string;
signArbitraryEnabled: boolean;
};

Expand Down
Loading

1 comment on commit e72d0f1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.