From 442568b260915738aa2c8a13b1384b33540164f0 Mon Sep 17 00:00:00 2001 From: Richard Watts <108257153+rrw-zilliqa@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:03:30 +0000 Subject: [PATCH] Misc bug fixes and improvements (#108) * (feat) Can now copy state and init params to clipboard (fix) Remove spurious debug * (fix) Fix syntax * (fix) Fix compilation and syntax * (fix) Fix the bug induced by the typechecker's last attempt to avoid bugs --- src/components/HighlightedCode.tsx | 36 +++++++++++++++++++ src/execution/address/ScillaContract.tsx | 11 ++---- src/execution/address/ScillaInitParams.tsx | 26 +++++++++++++- src/execution/address/ScillaState.tsx | 22 +++++++++++- .../decoder/DecodedScillaParamsTable.tsx | 22 +++++++++--- src/search/search.ts | 17 +++++++-- src/useERC1967.ts | 11 +++--- src/useResolvedAddresses.ts | 28 ++++++++++++--- src/useZilliqaHooks.ts | 4 +-- 9 files changed, 149 insertions(+), 28 deletions(-) create mode 100644 src/components/HighlightedCode.tsx diff --git a/src/components/HighlightedCode.tsx b/src/components/HighlightedCode.tsx new file mode 100644 index 00000000..13f39f49 --- /dev/null +++ b/src/components/HighlightedCode.tsx @@ -0,0 +1,36 @@ +import { faCopy } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { FC, PropsWithChildren } from "react"; +import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco"; +import { SyntaxHighlighter } from "../highlight-init.ts"; + +type HighlightedCodeProps = { + language: string; + content: string; +}; + +export const HighlightedCode: FC> = ({ + language, + content, +}) => { + const handleCopy = () => { + navigator.clipboard.writeText(content ?? ""); + }; + return ( +
+ + {content ?? ""} + + +
+ ); +}; + +export default HighlightedCode; diff --git a/src/execution/address/ScillaContract.tsx b/src/execution/address/ScillaContract.tsx index 6426d275..cd345b7b 100644 --- a/src/execution/address/ScillaContract.tsx +++ b/src/execution/address/ScillaContract.tsx @@ -1,7 +1,7 @@ import { Tab } from "@headlessui/react"; import React from "react"; +import { HighlightedCode } from "../../components/HighlightedCode"; import SwitchTab from "../../components/SwitchTab"; -import { SyntaxHighlighter, docco } from "../../highlight-init"; import { ScillaInitParams } from "./ScillaInitParams"; import { ScillaState } from "./ScillaState"; @@ -23,14 +23,7 @@ const ScillaContract: React.FC = ({ address, content }) => { - - {content ?? ""} - + {" "} diff --git a/src/execution/address/ScillaInitParams.tsx b/src/execution/address/ScillaInitParams.tsx index 4bee0843..08f260cf 100644 --- a/src/execution/address/ScillaInitParams.tsx +++ b/src/execution/address/ScillaInitParams.tsx @@ -1,3 +1,5 @@ +import { faCopy } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FC, useContext } from "react"; import { RuntimeContext } from "../../useRuntime"; import { useSmartContractInit } from "../../useZilliqaHooks"; @@ -12,6 +14,14 @@ type ScillaInitParamRowProps = { value: string; }; +const formatJsonValue = (value: any): string => { + if (typeof value == "object") { + return JSON.stringify(value, null, 2); + } else { + return value; + } +}; + const ScillaInitParamRow: FC = ({ name, valueType, @@ -24,7 +34,7 @@ const ScillaInitParamRow: FC = ({ {name} {valueType} - {value} + {formatJsonValue(value)} ); @@ -33,6 +43,10 @@ const ScillaInitParamRow: FC = ({ export const ScillaInitParams: FC = ({ address }) => { const { zilliqa } = useContext(RuntimeContext); const { data, isLoading } = useSmartContractInit(zilliqa, address); + const handleCopy = () => { + navigator.clipboard.writeText(JSON.stringify(data) ?? ""); + }; + if (isLoading) { return (
@@ -42,6 +56,16 @@ export const ScillaInitParams: FC = ({ address }) => { } else { return (
+ +
+ diff --git a/src/execution/address/ScillaState.tsx b/src/execution/address/ScillaState.tsx index e5997295..271d4801 100644 --- a/src/execution/address/ScillaState.tsx +++ b/src/execution/address/ScillaState.tsx @@ -1,3 +1,5 @@ +import { faCopy } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FC, useContext, useState } from "react"; import { RuntimeContext } from "../../useRuntime"; import { ContractState, useSmartContractState } from "../../useZilliqaHooks"; @@ -27,7 +29,11 @@ const ScillaStateParamRow: FC = ({ name, value }) => { }; const formatJsonValue = (value: any): string => { - return JSON.stringify(value, null, 2); + if (typeof value == "object") { + return JSON.stringify(value, null, 2); + } else { + return value; + } }; export const ScillaState: FC = ({ @@ -65,6 +71,10 @@ export const ScillaState: FC = ({ return
Loading contract state
; } + const handleCopy = () => { + navigator.clipboard.writeText(JSON.stringify(contractState) ?? ""); + }; + return (
+
+ +
diff --git a/src/execution/transaction/decoder/DecodedScillaParamsTable.tsx b/src/execution/transaction/decoder/DecodedScillaParamsTable.tsx index 29e86b4b..709869fb 100644 --- a/src/execution/transaction/decoder/DecodedScillaParamsTable.tsx +++ b/src/execution/transaction/decoder/DecodedScillaParamsTable.tsx @@ -18,16 +18,30 @@ const DecodedScillaParamRow: FC = ({ return ( <> - - - + + ); }; +function valueOf(val: any): string { + if (val instanceof Object) { + return JSON.stringify(val); + } else { + if (val === null) { + return "null"; + } + if (val === undefined) { + return "undefined"; + } + return val.toString(); + } +} + const DecodedScillaParamsTable: FC = ({ params, }) => { @@ -47,7 +61,7 @@ const DecodedScillaParamsTable: FC = ({ key={val["vname"]} name={val["vname"]} valueType={val["type"]} - value={val["value"]} + value={valueOf(val["value"])} /> ))} diff --git a/src/search/search.ts b/src/search/search.ts index b4be67ab..291a1fec 100644 --- a/src/search/search.ts +++ b/src/search/search.ts @@ -251,6 +251,7 @@ export const parseSearch = (q: string): string | undefined => { // Cleanup q = q.trim(); + console.log(`parseSearch q = ${q}`); let maybeAddress = q; let maybeIndex = ""; const sepIndex = q.lastIndexOf(":"); @@ -260,8 +261,11 @@ export const parseSearch = (q: string): string | undefined => { maybeIndex = !isNaN(parseInt(afterAddress)) ? parseInt(afterAddress).toString() : ""; + console.log(`2 = ${maybeAddress}`); } + console.log(`3 = ${maybeAddress}`); + // Parse URLs for other block explorers try { const url = new URL(q); @@ -275,6 +279,7 @@ export const parseSearch = (q: string): string | undefined => { const validatorMatch = pathname.match(/^\/validator\/(.*)$/); if (addressMatch) { maybeAddress = addressMatch[1]; + console.log(`maybeAddress from ${pathname} -> ${maybeAddress}`); // The URL might use a different port number maybeIndex = ""; } else if (txMatch) { @@ -309,18 +314,26 @@ export const parseSearch = (q: string): string | undefined => { } catch (e) { console.log(`search: Not a bech32 address`); } + console.log(`4 = ${maybeAddress}`); // The type checker is convinced that ethers:isAddress() will never say that a string > 40 characters // long is not an address. I'm not sure why... if (!isAddress(maybeAddress)) { let typeCheckerIsWrong = maybeAddress as string; - if (typeCheckerIsWrong.length > 40) { + // remove any leading 0x + console.log(`typeCheckerIsWrong ${typeCheckerIsWrong}`); + if (typeCheckerIsWrong.startsWith("0x")) { + typeCheckerIsWrong = typeCheckerIsWrong.substr(2); + } + console.log(`rem ${typeCheckerIsWrong} ${typeCheckerIsWrong.length}`); + if (typeCheckerIsWrong.length >= 40) { try { maybeAddress = "0x" + typeCheckerIsWrong .substr(typeCheckerIsWrong.length - 40) .toLowerCase(); + console.log(`rem2 ${maybeAddress}`); } catch (e) { // Obviously not. } @@ -329,7 +342,7 @@ export const parseSearch = (q: string): string | undefined => { // Plain address? if (isAddress(maybeAddress)) { - console.log(`search: maybeAddress ${maybeAddress} is an address ..`); + console.log(`search: maybeAddress ${maybeAddress} is an address XXX ..`); return `/address/${maybeAddress}${maybeIndex !== "" ? `?nonce=${maybeIndex}` : ""}`; } diff --git a/src/useERC1967.ts b/src/useERC1967.ts index 4077c3b9..5674755d 100644 --- a/src/useERC1967.ts +++ b/src/useERC1967.ts @@ -18,13 +18,17 @@ export type ERC1967ProxyAttributes = { export const GetStorageQuery = ( provider: JsonRpcApiProvider, - address: Address, + address: Address | undefined, slot: string, ) => { return { queryKey: ["getStorageAt", address, slot], queryFn: () => { - return provider.getStorage(address, slot); + if (address === undefined) { + return Promise.resolve(undefined); + } else { + return provider.getStorage(address, slot); + } }, }; }; @@ -41,9 +45,6 @@ export const useERC1967ProxyAttributes = ( return addr; } - if (address === undefined) { - return undefined; - } const { data: delegateData } = useQuery( GetStorageQuery(provider, address, DELEGATE_STORAGE_LOCATION), ); diff --git a/src/useResolvedAddresses.ts b/src/useResolvedAddresses.ts index 802ff110..889b56e5 100644 --- a/src/useResolvedAddresses.ts +++ b/src/useResolvedAddresses.ts @@ -59,11 +59,31 @@ export const useAddressOrENS = ( setChecksummedAddress(undefined); } }; - resolveName(); } else { - setENS(false); - setError(true); - setChecksummedAddress(undefined); + // Would it be an address if we lowercased it and removed anything other than the leading 0x and the hex chars? + try { + console.log(`trying with ${addressOrName}`); + // typescript thinks that addressOrName is never here, but it isn't .. + const _unsummedAddress = getAddress( + (addressOrName as string).toLowerCase(), + ); + if (isAddress(_unsummedAddress)) { + setENS(false); + setError(false); + setChecksummedAddress(_unsummedAddress); + console.log( + `_unsummed = ${_unsummedAddress} is ${isAddress(_unsummedAddress)}`, + ); + } else { + setENS(false); + setError(true); + setChecksummedAddress(undefined); + } + } catch (e) { + setENS(false); + setError(true); + setChecksummedAddress(undefined); + } } }, [provider, addressOrName, urlFixer]); diff --git a/src/useZilliqaHooks.ts b/src/useZilliqaHooks.ts index 04886efd..8d87fdb5 100644 --- a/src/useZilliqaHooks.ts +++ b/src/useZilliqaHooks.ts @@ -145,8 +145,8 @@ export const smartContractStateFetcher: Fetcher< [Zilliqa, string, string] > = async ([zilliqa, methodName, address]) => { const contract = zilliqa.contracts.at(address); - const initParams = await contract.getState(); - return initParams as any; + const state = await contract.getState(); + return state as any; }; export const useSmartContractState = (
+ {name} {valueType}{value}{valueType}{value}