diff --git a/components/input/calldata-form.tsx b/components/input/calldata-form.tsx index 2a60ec5..6c1b256 100644 --- a/components/input/calldata-form.tsx +++ b/components/input/calldata-form.tsx @@ -1,12 +1,23 @@ import { type RawAction } from "@/utils/types"; import { type FC, useEffect, useState } from "react"; -import { InputText, InputNumber, TextArea } from "@aragon/ods"; -import { type Address, parseEther, isHex } from "viem"; +import { InputText, InputNumber, TextArea, AlertInline } from "@aragon/ods"; +import { + type Address, + parseEther, + isHex, + decodeFunctionData, + Hex, + toFunctionSelector, + AbiFunction, + toFunctionSignature, +} from "viem"; import { isAddress } from "@/utils/evm"; -import { If } from "../if"; +import { If, Then } from "../if"; import { PUB_CHAIN } from "@/constants"; import { useIsContract } from "@/hooks/useIsContract"; import { PleaseWaitSpinner } from "../please-wait"; +import { useAbi } from "@/hooks/useAbi"; +import { CallFunctionSignatureField, CallParamField } from "../proposalActions/callParamField"; interface ICalldataFormProps { onChange: (actions: RawAction) => any; @@ -19,6 +30,7 @@ export const CalldataForm: FC = ({ onChange, onSubmit }) => const [calldata, setCalldata] = useState(""); const [value, setValue] = useState(""); const { isContract, isLoading, error: isContractError } = useIsContract(to); + const { abi, isLoading: isLoadingAbi } = useAbi((to || "") as Address); useEffect(() => { if (!isAddress(to)) return; @@ -31,6 +43,24 @@ export const CalldataForm: FC = ({ onChange, onSubmit }) => setTo(event?.target?.value as Address); }; + let decodedParams: ReturnType | null = null; + let matchingAbiFunction: AbiFunction | null = null; + try { + decodedParams = decodeFunctionData({ + abi: abi, + data: calldata as Hex, + }); + + for (const item of abi) { + const selector = toFunctionSelector(item); + if (calldata.startsWith(selector)) { + matchingAbiFunction = item; + } + } + } catch (_) { + // + } + return (
@@ -65,11 +95,7 @@ export const CalldataForm: FC = ({ onChange, onSubmit }) => label="Calldata" placeholder="0x..." value={calldata} - alert={ - !calldata || (isHex(calldata) && calldata.trim().length % 2 === 0) - ? undefined - : { message: "The given calldata is not valid", variant: "critical" } - } + alert={resolveCalldataAlert(calldata, abi, decodedParams)} onChange={(e) => setCalldata(e.target.value)} />
@@ -82,7 +108,42 @@ export const CalldataForm: FC = ({ onChange, onSubmit }) => onKeyDown={(e) => (e.key === "Enter" ? onSubmit?.() : null)} />
+ {/* Try to decode */} + + +
+

Decoded parameters

+
+ +
+ + {decodedParams?.args?.map((arg, i) => ( +
+ +
+ ))} +
+
+
); }; + +function resolveCalldataAlert( + calldata: string, + abi: AbiFunction[] | null, + decodedParams: { + args: readonly unknown[]; + functionName: string; + } | null +): { message: string; variant: "critical" | "warning" } | undefined { + if (!calldata) return undefined; + else if (!isHex(calldata) || calldata.trim().length % 2 !== 0) { + return { message: "The given calldata is not a valid hex string", variant: "critical" }; + } else if (!abi?.length) return undefined; + else if (!decodedParams) { + return { message: "The given calldata cannot be decoded using the available ABI", variant: "warning" }; + } + return undefined; +} diff --git a/pages/globals.css b/pages/globals.css index c949a88..84a8118 100644 --- a/pages/globals.css +++ b/pages/globals.css @@ -119,6 +119,16 @@ body { font-family: "Public Sans"; } +body, +span, +p, +ul, +ol, +div { + /* Custom */ + font-family: "Public Sans"; +} + h1, h2, h3,