Skip to content

Commit 6326da1

Browse files
committed
feat: masp indexer url UI and rpc backup
1 parent 8acb9d0 commit 6326da1

File tree

11 files changed

+194
-59
lines changed

11 files changed

+194
-59
lines changed

apps/namadillo/src/App/Settings/Advanced.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { ActionButton, Input, Stack } from "@namada/components";
22
import { chainParametersAtom } from "atoms/chain";
33
import {
44
indexerUrlAtom,
5+
maspIndexerUrlAtom,
56
rpcUrlAtom,
67
updateIndexerUrlAtom,
8+
updateMaspIndexerUrlAtom,
79
updateRpcUrlAtom,
810
} from "atoms/settings";
911
import { useAtom, useAtomValue } from "jotai";
@@ -16,25 +18,32 @@ export const Advanced = (): JSX.Element => {
1618
const [currentRpc] = useAtom(rpcUrlAtom);
1719
const [rpcMutation] = useAtom(updateRpcUrlAtom);
1820
const [currentIndexer] = useAtom(indexerUrlAtom);
21+
const [currentMaspIndexer] = useAtom(maspIndexerUrlAtom);
1922
const [indexerMutation] = useAtom(updateIndexerUrlAtom);
23+
const [maspIndexerMutation] = useAtom(updateMaspIndexerUrlAtom);
2024
const { data: chainParameters } = useAtomValue(chainParametersAtom);
2125

2226
const [rpc, setRpc] = useState(currentRpc);
2327
const [indexer, setIndexer] = useState(currentIndexer);
28+
const [maspIndexer, setMaspIndexer] = useState(currentMaspIndexer);
2429

2530
const onSubmit = async (e: React.FormEvent): Promise<void> => {
2631
e.preventDefault();
2732
try {
2833
await Promise.all([
2934
rpcMutation.mutateAsync(rpc),
3035
indexerMutation.mutateAsync(indexer),
36+
maspIndexerMutation.mutateAsync(maspIndexer),
3137
]);
3238
document.location.href =
3339
location.state.backgroundLocation.pathname ?? location.pathname;
3440
} catch {}
3541
};
3642

37-
const isPending = rpcMutation.isPending || indexerMutation.isPending;
43+
const isPending =
44+
rpcMutation.isPending ||
45+
indexerMutation.isPending ||
46+
maspIndexerMutation.isPending;
3847

3948
return (
4049
<form
@@ -71,6 +80,21 @@ export const Advanced = (): JSX.Element => {
7180
}}
7281
required
7382
/>
83+
<Input
84+
type="text"
85+
placeholder="Optional"
86+
value={maspIndexer}
87+
error={
88+
maspIndexerMutation.error instanceof Error &&
89+
maspIndexerMutation.error.message
90+
}
91+
label="MASP Indexer URL"
92+
className="[&_input]:border-neutral-300"
93+
onChange={(e) => {
94+
setMaspIndexer(e.currentTarget.value);
95+
maspIndexerMutation.reset();
96+
}}
97+
/>
7498
<Input
7599
type="text"
76100
variant="ReadOnlyCopy"

apps/namadillo/src/atoms/settings/atoms.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { Getter, Setter, atom, getDefaultStore } from "jotai";
44
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
55
import { atomWithStorage } from "jotai/utils";
66
import { SettingsStorage } from "types";
7-
import { fetchDefaultTomlConfig, isIndexerAlive, isRpcAlive } from "./services";
7+
import {
8+
fetchDefaultTomlConfig,
9+
isIndexerAlive,
10+
isMaspIndexerAlive,
11+
isRpcAlive,
12+
} from "./services";
813

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

@@ -65,18 +70,21 @@ const changeSettings =
6570
const changeSettingsUrl =
6671
(
6772
key: keyof SettingsStorage,
68-
healthCheck: (url: string) => Promise<boolean>
73+
healthCheck: (url: string) => Promise<boolean>,
74+
allowEmpty = false
6975
) =>
70-
async (url: string) => {
71-
const sanitizedUrl = sanitizeUrl(url);
72-
if (!isUrlValid(sanitizedUrl)) {
76+
async (inputUrl: string) => {
77+
const allowedEmpty = allowEmpty && inputUrl.length === 0;
78+
const url = allowedEmpty ? "" : sanitizeUrl(inputUrl);
79+
80+
if (!allowedEmpty && !isUrlValid(url)) {
7381
throw new Error(
7482
"Invalid URL. The URL should be valid starting with 'http', 'https', 'ws', or 'wss'."
7583
);
7684
}
77-
if (await healthCheck(sanitizedUrl)) {
85+
if (allowedEmpty || (await healthCheck(url))) {
7886
const { get, set } = getDefaultStore();
79-
changeSettings(key)(get, set, sanitizedUrl);
87+
changeSettings(key)(get, set, url);
8088
} else {
8189
throw new Error(
8290
"Couldn't reach the URL. Please provide a valid Namada URL service."
@@ -143,13 +151,30 @@ export const indexerUrlAtom = atom((get) => {
143151
return "";
144152
});
145153

154+
export const maspIndexerUrlAtom = atom((get) => {
155+
const customIndexerUrl = get(settingsAtom).maspIndexerUrl;
156+
if (customIndexerUrl) return customIndexerUrl;
157+
158+
const tomlIndexerUrl = get(defaultServerConfigAtom).data?.masp_indexer_url;
159+
if (tomlIndexerUrl) return tomlIndexerUrl;
160+
161+
return "";
162+
});
163+
146164
export const updateIndexerUrlAtom = atomWithMutation(() => {
147165
return {
148166
mutationKey: ["update-indexer-url"],
149167
mutationFn: changeSettingsUrl("indexerUrl", isIndexerAlive),
150168
};
151169
});
152170

171+
export const updateMaspIndexerUrlAtom = atomWithMutation(() => {
172+
return {
173+
mutationKey: ["update-masp-indexer-url"],
174+
mutationFn: changeSettingsUrl("maspIndexerUrl", isMaspIndexerAlive, true),
175+
};
176+
});
177+
153178
export const signArbitraryEnabledAtom = atom(
154179
(get) => get(settingsAtom).signArbitraryEnabled,
155180
changeSettings<boolean>("signArbitraryEnabled")

apps/namadillo/src/atoms/settings/services.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ export const isIndexerAlive = async (url: string): Promise<boolean> => {
1717
}
1818
};
1919

20+
export const isMaspIndexerAlive = async (url: string): Promise<boolean> => {
21+
if (!isUrlValid(url)) {
22+
return false;
23+
}
24+
try {
25+
const response = await fetch(`${url}/health`);
26+
return response.ok && response.status === 200;
27+
} catch {
28+
return false;
29+
}
30+
};
31+
2032
export const isRpcAlive = async (url: string): Promise<boolean> => {
2133
if (!isUrlValid(url)) {
2234
return false;

apps/namadillo/src/hooks/useSdk.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import initSdk from "@heliax/namada-sdk/inline-init";
22
import { getSdk, Sdk } from "@heliax/namada-sdk/web";
33
import { nativeTokenAddressAtom } from "atoms/chain";
4-
import { rpcUrlAtom } from "atoms/settings";
4+
import { maspIndexerUrlAtom, rpcUrlAtom } from "atoms/settings";
55
import { getDefaultStore, useAtomValue } from "jotai";
66
import {
77
createContext,
@@ -18,13 +18,20 @@ const initializeSdk = async (): Promise<Sdk> => {
1818
const { cryptoMemory } = await initSdk();
1919
const store = getDefaultStore();
2020
const rpcUrl = store.get(rpcUrlAtom);
21+
const maspIndexerUrl = store.get(maspIndexerUrlAtom);
2122
const nativeToken = store.get(nativeTokenAddressAtom);
2223

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

27-
const sdk = getSdk(cryptoMemory, rpcUrl, "", nativeToken.data);
28+
const sdk = getSdk(
29+
cryptoMemory,
30+
rpcUrl,
31+
maspIndexerUrl,
32+
"",
33+
nativeToken.data
34+
);
2835
return sdk;
2936
};
3037

apps/namadillo/src/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export type ChainSettings = {
4545

4646
export type SettingsTomlOptions = {
4747
indexer_url?: string;
48+
masp_indexer_url?: string;
4849
rpc_url?: string;
4950
};
5051

@@ -61,6 +62,7 @@ export type SettingsStorage = {
6162
fiat: CurrencyType;
6263
rpcUrl?: string;
6364
indexerUrl: string;
65+
maspIndexerUrl?: string;
6466
signArbitraryEnabled: boolean;
6567
};
6668

packages/sdk/src/indexWeb.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export * from "./utils";
88
* @async
99
* @param cryptoMemory - WebAssembly.Memory of crypto package
1010
* @param url - URL of the node
11+
* @param maspIndexerUrl - optional URL of the MASP indexer
1112
* @param dbName - Name of the database for the serialized wallet
1213
* @param [token] - Native token of the chain
1314
* @throws {Error} - Unable to Query native token
@@ -16,11 +17,15 @@ export * from "./utils";
1617
export function getSdk(
1718
cryptoMemory: WebAssembly.Memory,
1819
url: string,
20+
maspIndexerUrl: string,
1921
dbName: string,
2022
token: string
2123
): Sdk {
24+
// We change empty string to undefined so it "maps" to the Option<String> in Rust
25+
const maspIndexerUrlOpt =
26+
maspIndexerUrl.length === 0 ? undefined : maspIndexerUrl;
2227
// Instantiate QueryWasm
23-
const query = new QueryWasm(url);
28+
const query = new QueryWasm(url, maspIndexerUrlOpt);
2429

2530
// Instantiate SdkWasm
2631
const sdk = new SdkWasm(url, token, dbName);

0 commit comments

Comments
 (0)