Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { withThemeByClassName } from "@storybook/addon-themes";
import type { Preview } from "@storybook/react-vite";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import React from "react";
import "../tailwind.css";
import { ClipboardProvider } from "../components/providers/clipboard-provider";

const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
refetchOnWindowFocus: false,
},
mutations: {
retry: false,
},
},
});

const preview: Preview = {
parameters: {
controls: {
Expand All @@ -22,11 +36,13 @@ const preview: Preview = {
defaultTheme: "light",
}),
(Story) => (
<div className="p-4 dark:bg-background">
<ClipboardProvider>
<Story />
</ClipboardProvider>
</div>
<QueryClientProvider client={queryClient}>
<div className="p-4 dark:bg-background">
<ClipboardProvider>
<Story />
</ClipboardProvider>
</div>
</QueryClientProvider>
),
],
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
import { memo, useCallback, useState } from "react";
import { FormProvider } from "react-hook-form";
import { memo } from "react";
import type { AbiFunction } from "viem";
import { AbiItemFormWithPreview } from "../../abi-form/abi-item-form-with-preview.js";
import {
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../../shadcn/accordion.js";
import {
ActionButtons,
ConnectWalletAlert,
DefaultResultDisplay,
MsgSenderInput,
} from "../shared/components.js";
import { useMsgSenderForm } from "../shared/form-utils.js";
import type { BaseExecutionProps, ExecutionParams } from "../shared/types.js";
import { useFunctionExecution } from "../shared/use-function-execution.js";
import { isWriteFunction } from "../shared/utils.js";
import { ExecutionForm } from "../shared/components/execution-form.js";
import type { BaseExecutionProps, ExecutionParams } from "../types.js";

interface FunctionItemProps extends BaseExecutionProps {
func: AbiFunction;
Expand All @@ -42,42 +32,6 @@ export const FunctionItem = memo(
addressRenderer,
onHashClick,
}: FunctionItemProps) => {
const [callData, setCallData] = useState<string>("");
const { result, isSimulating, isExecuting, simulate, execute } =
useFunctionExecution();
const { form, msgSender } = useMsgSenderForm(sender);

const isWrite = isWriteFunction(func);

const handleCallDataChange = useCallback(
(newCallData: string | undefined) => {
setCallData(newCallData || "");
},
[],
);

const handleSimulate = () => {
simulate({
abiFunction: func,
callData,
msgSender,
onQuery,
onWrite,
onSimulate,
});
};

const handleExecute = () => {
execute({
abiFunction: func,
callData,
msgSender,
onQuery,
onWrite,
onSimulate,
});
};

const functionKey = `${func.name}-${index}`;

return (
Expand All @@ -91,55 +45,23 @@ export const FunctionItem = memo(
</span>
</AccordionTrigger>
<AccordionContent className="px-3 pb-3">
<FormProvider {...form}>
<div className="mt-4 space-y-6">
{isWrite && <MsgSenderInput />}

<AbiItemFormWithPreview
addresses={addresses}
key={func.name}
onChange={(data) => {
const callData = data.data?.toString() ?? undefined;
handleCallDataChange(callData);
}}
abiFunction={func}
address={address}
sender={sender || address}
chainId={chainId}
defaultCalldata={callData as `0x${string}` | undefined}
ArgProps={
addressRenderer
? {
addressRenderer,
}
: undefined
}
/>

{isWrite && requiresConnection && !isConnected && (
<ConnectWalletAlert />
)}

<ActionButtons
isWrite={isWrite}
callData={callData}
isSimulating={isSimulating}
isExecuting={isExecuting}
isConnected={isConnected}
hasSimulate={!!onSimulate}
simulate={handleSimulate}
execute={handleExecute}
/>

{result && (
<DefaultResultDisplay
key={`${result.type}-${result.data}`}
result={result}
onHashClick={onHashClick}
/>
)}
</div>
</FormProvider>
<ExecutionForm
abiFunction={func}
address={address}
chainId={chainId}
sender={sender}
addresses={addresses}
requiresConnection={requiresConnection}
isConnected={isConnected}
addressRenderer={addressRenderer}
onHashClick={onHashClick}
executionParams={{
onQuery,
onWrite,
onSimulate,
}}
className="space-y-4"
/>
</AccordionContent>
</AccordionItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { cn } from "../../../lib/utils.js";
import { Accordion } from "../../shadcn/accordion.js";
import { Input } from "../../shadcn/input.js";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../shadcn/tabs.js";
import type { ContractExecutionTabsProps } from "../shared/types.js";
import type { ContractExecutionTabsProps } from "../types.js";
import { FunctionItem } from "./function-item.js";
import { RawOperations } from "./raw-tab.js";
import { SignatureOperations } from "./signature-tab.js";
Expand Down Expand Up @@ -213,6 +213,7 @@ export function ContractExecutionTabs({
isConnected={isConnected}
onQuery={onQuery}
onWrite={onWrite}
onSimulate={onSimulate}
addressRenderer={addressRenderer}
onHashClick={onHashClick}
/>
Expand Down
119 changes: 30 additions & 89 deletions components/contract-execution/contract-execution-tabs/raw-tab.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
import { useCallback, useState } from "react";
import { FormProvider } from "react-hook-form";
import type { Hex } from "viem";
import { AbiItemFormWithPreview } from "../../abi-form/abi-item-form-with-preview.js";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "../../shadcn/accordion.js";
import { Button } from "../../shadcn/button.js";
import {
ConnectWalletAlert,
DefaultResultDisplay,
MsgSenderInput,
} from "../shared/components.js";
import { useMsgSenderForm } from "../shared/form-utils.js";
import type { BaseExecutionProps, ExecutionParams } from "../shared/types.js";
import { useRawExecution } from "../shared/use-raw-execution.js";
import { ExecutionForm } from "../shared/components/execution-form.js";
import type { BaseExecutionProps, ExecutionParams } from "../types.js";

interface RawOperationsProps extends BaseExecutionProps {
onQuery: (params: ExecutionParams) => Promise<`0x${string}`>;
onWrite: (params: ExecutionParams) => Promise<`0x${string}`>;
onSimulate?: (params: ExecutionParams) => Promise<`0x${string}`>;
}

export function RawOperations({
Expand All @@ -32,6 +22,7 @@ export function RawOperations({
isConnected,
onQuery,
onWrite,
onSimulate,
addressRenderer,
onHashClick,
}: RawOperationsProps) {
Expand All @@ -46,7 +37,7 @@ export function RawOperations({
addresses={addresses}
requiresConnection={requiresConnection}
isConnected={isConnected}
onExecute={onQuery}
onQuery={onQuery}
addressRenderer={addressRenderer}
onHashClick={onHashClick}
/>
Expand All @@ -58,7 +49,8 @@ export function RawOperations({
addresses={addresses}
requiresConnection={requiresConnection}
isConnected={isConnected}
onExecute={onWrite}
onWrite={onWrite}
onSimulate={onSimulate}
addressRenderer={addressRenderer}
onHashClick={onHashClick}
/>
Expand All @@ -69,7 +61,9 @@ export function RawOperations({

interface RawOperationItemProps extends BaseExecutionProps {
type: "call" | "transaction";
onExecute: (params: ExecutionParams) => Promise<`0x${string}`>;
onQuery?: (params: ExecutionParams) => Promise<`0x${string}`>;
onWrite?: (params: ExecutionParams) => Promise<`0x${string}`>;
onSimulate?: (params: ExecutionParams) => Promise<`0x${string}`>;
}

function RawOperationItem({
Expand All @@ -80,41 +74,18 @@ function RawOperationItem({
addresses,
requiresConnection,
isConnected,
onExecute,
onQuery,
onWrite,
onSimulate,
addressRenderer,
onHashClick,
}: RawOperationItemProps) {
const [callData, setCallData] = useState<string>("");
const [value, setValue] = useState<bigint | undefined>();
const { form, msgSender } = useMsgSenderForm(sender);

const isWrite = type === "transaction";
const {
result,
isExecuting,
execute: executeRaw,
} = useRawExecution({
isWrite,
onExecute,
});
const title = type === "call" ? "Raw Call" : "Raw Transaction";
const description =
type === "call"
? "Execute eth_call with arbitrary calldata"
: "Send transaction with arbitrary calldata";

const handleCallDataChange = useCallback(
({ data, value: newValue }: { data?: Hex; value?: bigint }) => {
setCallData(data || "");
setValue(newValue);
},
[],
);

const handleExecute = () => {
executeRaw({ callData, value, msgSender });
};

return (
<AccordionItem
value={type}
Expand All @@ -127,53 +98,23 @@ function RawOperationItem({
</div>
</AccordionTrigger>
<AccordionContent className="px-3 pb-3">
<FormProvider {...form}>
<div className="mt-4 space-y-6">
{isWrite && <MsgSenderInput />}

<AbiItemFormWithPreview
addresses={addresses}
onChange={handleCallDataChange}
abiFunction={type === "call" ? "rawCall" : "raw"}
address={address}
sender={sender || address}
chainId={chainId}
ArgProps={
addressRenderer
? {
addressRenderer,
}
: undefined
}
/>

{isWrite && requiresConnection && !isConnected && (
<ConnectWalletAlert />
)}

<div className="flex flex-row items-center justify-center gap-2">
<Button
onClick={handleExecute}
disabled={!callData || isExecuting || (isWrite && !isConnected)}
className="w-fit"
>
{isExecuting
? "Executing..."
: type === "call"
? "Call"
: "Send Transaction"}
</Button>
</div>

{result && (
<DefaultResultDisplay
key={`${result.type}-${result.data}`}
result={result}
onHashClick={onHashClick}
/>
)}
</div>
</FormProvider>
<ExecutionForm
abiFunction={type === "call" ? "rawCall" : "raw"}
address={address}
chainId={chainId}
sender={sender}
addresses={addresses}
requiresConnection={requiresConnection}
isConnected={isConnected}
addressRenderer={addressRenderer}
onHashClick={onHashClick}
executionParams={{
onQuery,
onWrite,
onSimulate,
}}
className="mt-4 space-y-4"
/>
</AccordionContent>
</AccordionItem>
);
Expand Down
Loading