From f369ae98a934e2f871078938d9a724002515d3ec Mon Sep 17 00:00:00 2001 From: Iveta Date: Thu, 6 Mar 2025 09:00:50 -0500 Subject: [PATCH 1/4] Add Download Wasm button --- .../components/ContractInfo.tsx | 1 + .../components/ContractSpec.tsx | 89 ++++++++++++++++++- src/query/external/useSEContractInfo.ts | 8 +- .../external/useSEContractVersionHistory.ts | 8 +- src/query/external/useSEContractWasmBinary.ts | 47 ++++++++++ 5 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 src/query/external/useSEContractWasmBinary.ts diff --git a/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractInfo.tsx b/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractInfo.tsx index e99b16f0..601ade7c 100644 --- a/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractInfo.tsx +++ b/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractInfo.tsx @@ -244,6 +244,7 @@ export const ContractInfo = ({ { const isXdrInit = useIsXdrInit(); + const queryClient = useQueryClient(); const { data: wasmData, @@ -37,6 +47,47 @@ export const ContractSpec = ({ isActive: Boolean(isActive && rpcUrl && wasmHash), }); + const { + data: wasmBinary, + error: wasmBinaryError, + isLoading: isWasmBinaryLoading, + isFetching: isWasmBinaryFetching, + refetch: fetchWasmBinary, + } = useSEContractWasmBinary({ wasmHash, networkId }); + + const resetWasmBlob = useCallback(() => { + queryClient.resetQueries({ + queryKey: ["useSEContractWasmBinary", networkId, wasmHash], + }); + }, [networkId, queryClient, wasmHash]); + + useEffect(() => { + if (wasmBinary) { + // Create blob + const blob = new Blob([wasmBinary], { + type: "application/octet-stream", + }); + + // Create a link element and trigger a download + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.setAttribute("download", `${wasmHash}.wasm`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + }, [wasmBinary, wasmHash]); + + useEffect(() => { + if (wasmBinaryError) { + // Automatically clear error message after 5 sec + delayedAction({ + action: resetWasmBlob, + delay: 5000, + }); + } + }, [resetWasmBlob, wasmBinaryError]); + if (!wasmHash) { return ( @@ -90,6 +141,38 @@ export const ContractSpec = ({ }; return ( - + + + + + + + <> + {wasmBinaryError ? ( + + ) : null} + + + ); }; diff --git a/src/query/external/useSEContractInfo.ts b/src/query/external/useSEContractInfo.ts index 16bf5324..ec0ee102 100644 --- a/src/query/external/useSEContractInfo.ts +++ b/src/query/external/useSEContractInfo.ts @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { STELLAR_EXPERT_API } from "@/constants/settings"; +import { getStellarExpertNetwork } from "@/helpers/getStellarExpertNetwork"; import { ContractInfoApiResponse, NetworkType } from "@/types/types"; /** @@ -15,13 +16,12 @@ export const useSEContractInfo = ({ const query = useQuery({ queryKey: ["useSEContractInfo", networkId, contractId], queryFn: async () => { - // Not supported networks - if (["futurenet", "custom"].includes(networkId)) { + const network = getStellarExpertNetwork(networkId); + + if (!network) { return null; } - const network = networkId === "mainnet" ? "public" : "testnet"; - try { const response = await fetch( `${STELLAR_EXPERT_API}/${network}/contract/${contractId}`, diff --git a/src/query/external/useSEContractVersionHistory.ts b/src/query/external/useSEContractVersionHistory.ts index 095eea7e..ebbedb17 100644 --- a/src/query/external/useSEContractVersionHistory.ts +++ b/src/query/external/useSEContractVersionHistory.ts @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { STELLAR_EXPERT_API } from "@/constants/settings"; +import { getStellarExpertNetwork } from "@/helpers/getStellarExpertNetwork"; import { ContractVersionHistoryResponseItem, NetworkType } from "@/types/types"; /** @@ -17,13 +18,12 @@ export const useSEContractVersionHistory = ({ const query = useQuery({ queryKey: ["useSEContractVersionHistory", networkId, contractId], queryFn: async () => { - // Not supported networks - if (["futurenet", "custom"].includes(networkId)) { + const network = getStellarExpertNetwork(networkId); + + if (!network) { return null; } - const network = networkId === "mainnet" ? "public" : "testnet"; - try { const response = await fetch( `${STELLAR_EXPERT_API}/${network}/contract/${contractId}/version`, diff --git a/src/query/external/useSEContractWasmBinary.ts b/src/query/external/useSEContractWasmBinary.ts new file mode 100644 index 00000000..ba4c886a --- /dev/null +++ b/src/query/external/useSEContractWasmBinary.ts @@ -0,0 +1,47 @@ +import { useQuery } from "@tanstack/react-query"; +import { STELLAR_EXPERT_API } from "@/constants/settings"; +import { getStellarExpertNetwork } from "@/helpers/getStellarExpertNetwork"; +import { NetworkType } from "@/types/types"; + +/** + * StellarExpert API to get smart contract wasm binary + */ +export const useSEContractWasmBinary = ({ + networkId, + wasmHash, +}: { + networkId: NetworkType; + wasmHash: string; +}) => { + const query = useQuery({ + queryKey: ["useSEContractWasmBinary", networkId, wasmHash], + queryFn: async () => { + const network = getStellarExpertNetwork(networkId); + + if (!network) { + return null; + } + + try { + const response = await fetch( + `${STELLAR_EXPERT_API}/${network}/wasm/${wasmHash}`, + ); + + if (response.status === 200) { + const responseArrayBuffer = await response.arrayBuffer(); + + return responseArrayBuffer; + } else { + const responseJson = await response.json(); + + throw responseJson.error || "WASM binary error"; + } + } catch (e: any) { + throw `Error downloading WASM. ${e}`; + } + }, + enabled: false, + }); + + return query; +}; From 3d713365383804b7a9836f245689b224256f591d Mon Sep 17 00:00:00 2001 From: Iveta Date: Thu, 6 Mar 2025 09:36:22 -0500 Subject: [PATCH 2/4] Show warning if network error --- .../components/ContractSpec.tsx | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx b/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx index 333a1247..0dc769e1 100644 --- a/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx +++ b/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx @@ -114,7 +114,26 @@ export const ContractSpec = ({ } if (wasmError) { - return ; + const errorString = wasmError.toString(); + let networkMessage = null; + + if (errorString.toLowerCase().includes("network error")) { + networkMessage = ( + + There may be an issue with the RPC server. You can change it in the + network settings in the upper right corner. + + ); + } + + return ( + + <> + + {networkMessage} + + + ); } const formatSpec = () => { From 5773279751b163e7a08f5357998924508de8fa8f Mon Sep 17 00:00:00 2001 From: Iveta Date: Thu, 6 Mar 2025 15:00:39 -0500 Subject: [PATCH 3/4] Download file helper + download json --- .../components/ContractSpec.tsx | 24 ++++++------ src/components/CodeEditor/index.tsx | 37 ++++++++++++++++++- src/components/DataTable/index.tsx | 2 +- src/helpers/downloadFile.ts | 24 ++++++++++++ src/helpers/exportJsonToCsvFile.ts | 17 ++++----- 5 files changed, 79 insertions(+), 25 deletions(-) create mode 100644 src/helpers/downloadFile.ts diff --git a/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx b/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx index 0dc769e1..ba938d66 100644 --- a/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx +++ b/src/app/(sidebar)/smart-contracts/contract-explorer/components/ContractSpec.tsx @@ -13,6 +13,7 @@ import { useIsXdrInit } from "@/hooks/useIsXdrInit"; import * as StellarXdr from "@/helpers/StellarXdr"; import { prettifyJsonString } from "@/helpers/prettifyJsonString"; import { delayedAction } from "@/helpers/delayedAction"; +import { downloadFile } from "@/helpers/downloadFile"; import { NetworkType } from "@/types/types"; @@ -63,18 +64,12 @@ export const ContractSpec = ({ useEffect(() => { if (wasmBinary) { - // Create blob - const blob = new Blob([wasmBinary], { - type: "application/octet-stream", + downloadFile({ + value: wasmBinary, + fileType: "application/octet-stream", + fileName: wasmHash, + fileExtension: "wasm", }); - - // Create a link element and trigger a download - const link = document.createElement("a"); - link.href = URL.createObjectURL(blob); - link.setAttribute("download", `${wasmHash}.wasm`); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); } }, [wasmBinary, wasmHash]); @@ -161,7 +156,12 @@ export const ContractSpec = ({ return ( - + + ) : null} + +