Skip to content

Commit

Permalink
feat: store shielded context by chain id
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszjasiuk committed Dec 24, 2024
1 parent 06ffe4e commit 65abd93
Show file tree
Hide file tree
Showing 22 changed files with 268 additions and 99 deletions.
2 changes: 2 additions & 0 deletions apps/namadillo/src/App/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { routes } from "./routes";
import { Advanced } from "./Settings/Advanced";
import { EnableFeatures } from "./Settings/EnableFeatures";
import { SettingsMain } from "./Settings/SettingsMain";
import { SettingsMASP } from "./Settings/SettingsMASP";
import { SettingsPanel } from "./Settings/SettingsPanel";
import { SettingsSignArbitrary } from "./Settings/SettingsSignArbitrary";
import { SignMessages } from "./SignMessages/SignMessages";
Expand Down Expand Up @@ -144,6 +145,7 @@ export const MainRoutes = (): JSX.Element => {
path={routes.settingsSignArbitrary}
element={<SettingsSignArbitrary />}
/>
<Route path={routes.settingsMASP} element={<SettingsMASP />} />
<Route
path={routes.settingsFeatures}
element={<EnableFeatures />}
Expand Down
43 changes: 23 additions & 20 deletions apps/namadillo/src/App/Masp/ShieldedBalanceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export const ShieldedBalanceChart = (): JSX.Element => {
const shieldedTokensQuery = useAtomValue(shieldedTokensAtom);
const [{ data: shieldedSyncProgress, refetch: shieledSync }] =
useAtom(shieldedSyncAtom);

const [showSyncProgress, setShowSyncProgress] = useState(false);
const [progress, setProgress] = useState({
[ProgressBarNames.Scanned]: 0,
Expand All @@ -27,26 +26,37 @@ export const ShieldedBalanceChart = (): JSX.Element => {

useEffect(() => {
if (!shieldedSyncProgress) return;

const onProgressBarIncremented = ({
name,
current,
total,
}: ProgressBarIncremented): void => {
const perc =
total === 0 ? 0 : Math.min(Math.floor((current / total) * 100), 100);
if (name === ProgressBarNames.Fetched) {
// TODO: this maybe can be improved by passing total in ProgressBarStarted event
// If total is more than one batch of 100, show progress
if (total > 100) {
setShowSyncProgress(true);
}

setProgress((prev) => ({
...prev,
[name]: perc,
}));
const perc =
total === 0 ? 0 : Math.min(Math.floor((current / total) * 100), 100);

setProgress((prev) => ({
...prev,
[name]: perc,
}));
}
};

const onProgressBarFinished = ({ name }: ProgressBarFinished): void => {
setProgress((prev) => ({
...prev,
[name]: 100,
}));
if (name === ProgressBarNames.Fetched) {
setProgress((prev) => ({
...prev,
[name]: 100,
}));

setShowSyncProgress(false);
}
};

shieldedSyncProgress.on(
Expand All @@ -73,13 +83,6 @@ export const ShieldedBalanceChart = (): JSX.Element => {

useEffect(() => {
shieledSync();

const timeoutId = setTimeout(() => {
setShowSyncProgress(true);
}, 3000);
return () => {
clearTimeout(timeoutId);
};
}, []);

const shieldedDollars = getTotalDollar(shieldedTokensQuery.data);
Expand All @@ -91,7 +94,7 @@ export const ShieldedBalanceChart = (): JSX.Element => {
result={shieldedTokensQuery}
niceError="Unable to load balance"
>
{shieldedTokensQuery.isPending ?
{shieldedTokensQuery.isPending || showSyncProgress ?
<SkeletonLoading
height="100%"
width="100%"
Expand Down
33 changes: 33 additions & 0 deletions apps/namadillo/src/App/Settings/SettingsMASP.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ActionButton, Stack } from "@namada/components";
import { routes } from "App/routes";
import { shieldedSyncAtom } from "atoms/balance";
import { clearShieldedContextAtom } from "atoms/settings";
import { useAtom } from "jotai";
import { useNavigate } from "react-router-dom";

export const SettingsMASP = (): JSX.Element => {
const navigate = useNavigate();
const [clearShieldedContext] = useAtom(clearShieldedContextAtom);
const [{ refetch: shieldedSync }] = useAtom(shieldedSyncAtom);

const onInvalidateShieldedContext = async (): Promise<void> => {
await clearShieldedContext.mutateAsync();
shieldedSync();
navigate(routes.masp);
};

return (
<Stack as="footer" className="px-5" gap={3}>
<h2 className="text-base">Invalidate Shielded Context</h2>
<p className="text-sm">
In case your shielded balance is not updating correctly, you can
invalidate the shielded context to force a rescan. This might take a few
minutes to complete.
</p>

<ActionButton onClick={onInvalidateShieldedContext} className="shrink-0">
Invalidate Shielded Context
</ActionButton>
</Stack>
);
};
1 change: 1 addition & 0 deletions apps/namadillo/src/App/Settings/SettingsMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const SettingsMain = (): JSX.Element => {
url={routes.settingsSignArbitrary}
text="Sign Arbitrary"
/>
<SettingsPanelMenuItem url={routes.settingsMASP} text="MASP" />
</ul>
<div className="text-xs">
<div>Namadillo Version: {version}</div>
Expand Down
1 change: 1 addition & 0 deletions apps/namadillo/src/App/WorkerTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export function WorkerTest(): JSX.Element {
type: "sync",
payload: {
vks: [{ key: vk, birthday: 0 }],
chainId: chain!.id,
},
});

Expand Down
1 change: 1 addition & 0 deletions apps/namadillo/src/App/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const routes = {
settings: "/settings",
settingsAdvanced: "/settings/advanced",
settingsSignArbitrary: "/settings/sign-arbitrary",
settingsMASP: "/settings/masp",
settingsFeatures: "/settings/features",

// Other
Expand Down
23 changes: 17 additions & 6 deletions apps/namadillo/src/atoms/balance/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export const shieldedSyncAtom = atomWithQuery<ShieldedSyncEmitter | null>(
const namTokenAddressQuery = get(nativeTokenAddressAtom);
const rpcUrl = get(rpcUrlAtom);
const maspIndexerUrl = get(maspIndexerUrlAtom);
const parametersQuery = get(chainParametersAtom);

return {
queryKey: [
Expand All @@ -122,17 +123,19 @@ export const shieldedSyncAtom = atomWithQuery<ShieldedSyncEmitter | null>(
...queryDependentFn(async () => {
const viewingKeys = viewingKeysQuery.data;
const namTokenAddress = namTokenAddressQuery.data;
if (!namTokenAddress || !viewingKeys) {
const parameters = parametersQuery.data;
if (!namTokenAddress || !viewingKeys || !parameters) {
return null;
}
const [_, allViewingKeys] = viewingKeys;
return shieldedSync(
rpcUrl,
maspIndexerUrl,
namTokenAddress,
allViewingKeys
allViewingKeys,
parameters.chainId
);
}, [viewingKeysQuery, namTokenAddressQuery]),
}, [viewingKeysQuery, namTokenAddressQuery, parametersQuery]),
};
}
);
Expand All @@ -147,6 +150,7 @@ export const shieldedBalanceAtom = atomWithQuery<
const rpcUrl = get(rpcUrlAtom);
const maspIndexerUrl = get(maspIndexerUrlAtom);
const shieldedSync = get(shieldedSyncAtom);
const chainParametersQuery = get(chainParametersAtom);

return {
refetchInterval: enablePolling ? 1000 : false,
Expand All @@ -163,7 +167,8 @@ export const shieldedBalanceAtom = atomWithQuery<
const viewingKeys = viewingKeysQuery.data;
const chainTokens = chainTokensQuery.data;
const syncEmitter = shieldedSync.data;
if (!viewingKeys || !chainTokens || !syncEmitter) {
const chain = chainParametersQuery.data;
if (!viewingKeys || !chainTokens || !syncEmitter || !chain) {
return [];
}
const [viewingKey] = viewingKeys;
Expand All @@ -174,14 +179,20 @@ export const shieldedBalanceAtom = atomWithQuery<

const response = await fetchShieldedBalance(
viewingKey,
chainTokens.map((t) => t.address)
chainTokens.map((t) => t.address),
chain.chainId
);
const shieldedBalance = response.map(([address, amount]) => ({
address,
minDenomAmount: BigNumber(amount),
}));
return shieldedBalance;
}, [viewingKeysQuery, chainTokensQuery, namTokenAddressQuery]),
}, [
viewingKeysQuery,
chainTokensQuery,
namTokenAddressQuery,
chainParametersQuery,
]),
};
});

Expand Down
23 changes: 6 additions & 17 deletions apps/namadillo/src/atoms/balance/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export function shieldedSync(
rpcUrl: string,
maspIndexerUrl: string,
token: string,
viewingKeys: DatedViewingKey[]
viewingKeys: DatedViewingKey[],
chainId: string
): EventEmitter<ShieldedSyncEventMap> {
// Only one sync process at a time
if (shieldedSyncEmitter) {
Expand Down Expand Up @@ -63,7 +64,7 @@ export function shieldedSync(
});
await shieldedSyncWorker.sync({
type: "sync",
payload: { vks: viewingKeys },
payload: { vks: viewingKeys, chainId },
});
} finally {
worker.terminate();
Expand All @@ -76,13 +77,14 @@ export function shieldedSync(

export const fetchShieldedBalance = async (
viewingKey: DatedViewingKey,
addresses: string[]
addresses: string[],
chainId: string
): Promise<Balance> => {
// TODO mock shielded balance
// return await mockShieldedBalance(viewingKey);

const sdk = await getSdkInstance();
return await sdk.rpc.queryBalance(viewingKey.key, addresses);
return await sdk.rpc.queryBalance(viewingKey.key, addresses, chainId);
};

export const fetchBlockHeightByTimestamp = async (
Expand All @@ -93,16 +95,3 @@ export const fetchBlockHeightByTimestamp = async (

return Number(response.data.height);
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const mockShieldedBalance = async (
viewingKey: DatedViewingKey
): Promise<Balance> => {
await new Promise((r) => setTimeout(() => r(0), 500));
getSdkInstance().then((sdk) => sdk.rpc.shieldedSync([viewingKey]));
return [
["tnam1qy440ynh9fwrx8aewjvvmu38zxqgukgc259fzp6h", "37"], // nam
["tnam1p5nnjnasjtfwen2kzg78fumwfs0eycqpecuc2jwz", "1"], // uatom
["tnam1p4rm6gy30xzeehj29qr8v0t33xmwdlsn5ye0ezf0", "2"], // uosmo
];
};
6 changes: 6 additions & 0 deletions apps/namadillo/src/atoms/chain/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
NativeToken,
Parameters,
} from "@namada/indexer-client";
import { getSdkInstance } from "utils/sdk";

export const fetchRpcUrlFromIndexer = async (
api: DefaultApi
Expand All @@ -24,3 +25,8 @@ export const fetchChainTokens = async (
): Promise<(NativeToken | IbcToken)[]> => {
return (await api.apiV1ChainTokenGet()).data;
};

export const clearShieldedContext = async (chainId: string): Promise<void> => {
const sdk = await getSdkInstance();
await sdk.getMasp().clearShieldedContext(chainId);
};
15 changes: 14 additions & 1 deletion apps/namadillo/src/atoms/settings/atoms.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { isUrlValid, sanitizeUrl } from "@namada/utils";
import { indexerRpcUrlAtom } from "atoms/chain";
import { chainParametersAtom, indexerRpcUrlAtom } from "atoms/chain";
import { Getter, Setter, atom, getDefaultStore } from "jotai";
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
import { atomWithStorage } from "jotai/utils";
import { SettingsStorage } from "types";
import {
clearShieldedContext,
fetchDefaultTomlConfig,
isIndexerAlive,
isMaspIndexerAlive,
Expand Down Expand Up @@ -206,3 +207,15 @@ export const indexerHeartbeatAtom = atomWithQuery((get) => {
},
};
});

export const clearShieldedContextAtom = atomWithMutation((get) => {
const parameters = get(chainParametersAtom);
if (!parameters.data) {
throw new Error("Chain parameters not loaded");
}

return {
mutationKey: ["clear-shielded-context"],
mutationFn: () => clearShieldedContext(parameters.data.chainId),
};
});
6 changes: 6 additions & 0 deletions apps/namadillo/src/atoms/settings/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Configuration, DefaultApi } from "@namada/indexer-client";
import { isUrlValid } from "@namada/utils";
import toml from "toml";
import { SettingsTomlOptions } from "types";
import { getSdkInstance } from "utils/sdk";

export const isIndexerAlive = async (url: string): Promise<boolean> => {
if (!isUrlValid(url)) {
Expand Down Expand Up @@ -46,3 +47,8 @@ export const fetchDefaultTomlConfig =
const response = await fetch("/config.toml");
return toml.parse(await response.text()) as SettingsTomlOptions;
};

export const clearShieldedContext = async (chainId: string): Promise<void> => {
const sdk = await getSdkInstance();
await sdk.getMasp().clearShieldedContext(chainId);
};
12 changes: 7 additions & 5 deletions apps/namadillo/src/hooks/useSdk.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Sdk } from "@namada/sdk/web";
import { QueryStatus, useQuery } from "@tanstack/react-query";
import { nativeTokenAddressAtom } from "atoms/chain";
import { chainParametersAtom, nativeTokenAddressAtom } from "atoms/chain";
import { useAtomValue } from "jotai";
import {
createContext,
Expand Down Expand Up @@ -29,20 +29,21 @@ export const SdkProvider: FunctionComponent<PropsWithChildren> = ({
}) => {
const [sdk, setSdk] = useState<Sdk>();
const nativeToken = useAtomValue(nativeTokenAddressAtom);
const parameters = useAtomValue(chainParametersAtom);

// fetchAndStoreMaspParams() returns nothing,
// so we return boolean on success for the query to succeed:
const fetchMaspParams = async (): Promise<boolean | void> => {
const fetchMaspParams = async (chainId: string): Promise<boolean | void> => {
const { masp } = sdk!;

return masp.hasMaspParams().then(async (hasMaspParams) => {
if (hasMaspParams) {
await masp.loadMaspParams("").catch((e) => Promise.reject(e));
await masp.loadMaspParams("", chainId).catch((e) => Promise.reject(e));
return true;
}
return masp
.fetchAndStoreMaspParams(paramsUrl)
.then(() => masp.loadMaspParams("").then(() => true))
.then(() => masp.loadMaspParams("", chainId).then(() => true))
.catch((e) => {
throw new Error(e);
});
Expand All @@ -51,7 +52,8 @@ export const SdkProvider: FunctionComponent<PropsWithChildren> = ({

const { status: maspParamsStatus } = useQuery({
queryKey: ["sdk"],
queryFn: fetchMaspParams,
enabled: Boolean(parameters.data),
queryFn: async () => await fetchMaspParams(parameters.data!.chainId),
retry: 3,
retryDelay: 3000,
});
Expand Down
Loading

0 comments on commit 65abd93

Please sign in to comment.