From 6b2d3278504ee2c06420afbe1295d33dc1852e18 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B8r=E2=88=82=C2=A1?= <4456749@users.noreply.github.com>
Date: Wed, 13 Mar 2024 12:30:08 +0100
Subject: [PATCH] Adding cache support for ABI fetching
---
components/actions/action.tsx | 2 +-
components/input/function-call-form.tsx | 3 +-
hooks/useAbi.ts | 108 +++++++++++++-----------
hooks/useMetadata.ts | 6 +-
utils/types.ts | 8 ++
5 files changed, 72 insertions(+), 55 deletions(-)
diff --git a/components/actions/action.tsx b/components/actions/action.tsx
index ac4b8fdc..3165b26d 100644
--- a/components/actions/action.tsx
+++ b/components/actions/action.tsx
@@ -68,7 +68,7 @@ export const ActionCard = function ({ action, idx }: ActionCardProps) {
-
Contract call
+
Contract
{action.to}
diff --git a/components/input/function-call-form.tsx b/components/input/function-call-form.tsx
index 2c82d626..e0b96c43 100644
--- a/components/input/function-call-form.tsx
+++ b/components/input/function-call-form.tsx
@@ -23,6 +23,7 @@ export const FunctionCallForm: FC
= ({
value,
data,
});
+ setTargetContract("");
};
return (
@@ -47,7 +48,7 @@ export const FunctionCallForm: FC = ({
- Enter the address of the contract to interact with
+ Enter the address of the contract to call in a new action
{
const { addAlert } = useAlertContext();
const publicClient = usePublicClient({ chainId: PUB_CHAIN.id });
- const [abi, setAbi] = useState();
- const [isLoading, setIsLoading] = useState(false);
- useEffect(() => {
- if (!publicClient) return;
- else if (!isAddress(contractAddress)) return;
+ const {
+ data: abi,
+ isLoading,
+ error,
+ } = useQuery({
+ queryKey: [contractAddress || "", !!publicClient],
+ queryFn: () => {
+ if (!contractAddress || !isAddress(contractAddress) || !publicClient) {
+ return Promise.resolve([]);
+ }
- setIsLoading(true);
- const abiLoader = new whatsabi.loaders.EtherscanABILoader({
- apiKey: PUB_ETHERSCAN_API_KEY,
- });
+ const abiLoader = new whatsabi.loaders.EtherscanABILoader({
+ apiKey: PUB_ETHERSCAN_API_KEY,
+ });
- whatsabi
- .autoload(contractAddress!, {
- provider: publicClient,
- abiLoader,
- followProxies: true,
- enableExperimentalMetadata: true,
- })
- .then(({ abi }) => {
- const functionItems: AbiFunction[] = [];
- for (const item of abi) {
- if (item.type === "event") continue;
+ return whatsabi
+ .autoload(contractAddress!, {
+ provider: publicClient,
+ abiLoader,
+ followProxies: true,
+ enableExperimentalMetadata: true,
+ })
+ .then(({ abi }) => {
+ const functionItems: AbiFunction[] = [];
+ for (const item of abi) {
+ if (item.type === "event") continue;
- functionItems.push({
- name: (item as any).name ?? "(function)",
- inputs: item.inputs ?? [],
- outputs: item.outputs ?? [],
- stateMutability: item.stateMutability ?? "payable",
- type: item.type,
- });
- }
- functionItems.sort((a, b) => {
- if (
- ["pure", "view"].includes(a.stateMutability) &&
- ["pure", "view"].includes(b.stateMutability)
- ) {
+ functionItems.push({
+ name: (item as any).name ?? "(function)",
+ inputs: item.inputs ?? [],
+ outputs: item.outputs ?? [],
+ stateMutability: item.stateMutability ?? "payable",
+ type: item.type,
+ });
+ }
+ functionItems.sort((a, b) => {
+ if (
+ ["pure", "view"].includes(a.stateMutability) &&
+ ["pure", "view"].includes(b.stateMutability)
+ ) {
+ return 0;
+ } else if (["pure", "view"].includes(a.stateMutability)) return 1;
+ else if (["pure", "view"].includes(b.stateMutability)) return -1;
return 0;
- } else if (["pure", "view"].includes(a.stateMutability)) return 1;
- else if (["pure", "view"].includes(b.stateMutability)) return -1;
- return 0;
- });
- setAbi(functionItems);
- setIsLoading(false);
- })
- .catch((err) => {
- console.error(err);
- setIsLoading(false);
- addAlert("Cannot fetch", {
- description: "The details of the contract could not be fetched",
- type: "error",
+ });
+ return functionItems;
+ })
+ .catch((err) => {
+ console.error(err);
+ addAlert("Cannot fetch", {
+ description: "The details of the contract could not be fetched",
+ type: "error",
+ });
+ throw err;
});
- });
- }, [contractAddress]);
+ },
+ retry: 4,
+ refetchOnMount: false,
+ refetchOnReconnect: false,
+ retryOnMount: true,
+ staleTime: Infinity,
+ });
return {
abi: abi ?? [],
isLoading,
+ error,
};
};
diff --git a/hooks/useMetadata.ts b/hooks/useMetadata.ts
index dfb1e546..ee6351cb 100644
--- a/hooks/useMetadata.ts
+++ b/hooks/useMetadata.ts
@@ -1,11 +1,9 @@
import { fetchJsonFromIpfs } from "@/utils/ipfs";
+import { JsonValue } from "@/utils/types";
import { useQuery } from "@tanstack/react-query";
import { fromHex } from "viem";
-type JsonValue = string | number | boolean;
-type JsonObject = JsonValue | Record | Array;
-
-export function useMetadata(ipfsUri?: string) {
+export function useMetadata(ipfsUri?: string) {
const { data, isLoading, isSuccess, error } = useQuery({
queryKey: [ipfsUri || ""],
queryFn: () => {
diff --git a/utils/types.ts b/utils/types.ts
index c57bf243..848d4b9e 100644
--- a/utils/types.ts
+++ b/utils/types.ts
@@ -12,3 +12,11 @@ export interface IAlert {
explorerLink?: string;
dismissTimeout?: ReturnType;
}
+
+// General types
+
+type JsonLiteral = string | number | boolean;
+export type JsonValue =
+ | JsonLiteral
+ | Record
+ | Array;