diff --git a/package.json b/package.json index 36974c7..4e0aa9e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "start": "next start" }, "dependencies": { - "@consenlabs/imaccount-sdk": "^0.1.8", + "@consenlabs/imaccount-sdk": "^0.1.28", "next": "^13.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", diff --git a/pages/_app.tsx b/pages/_app.tsx index 4e91473..6f85034 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,8 +1,8 @@ -import '../styles.css'; // Import the global CSS here -import { AppProps } from 'next/app'; +import "../styles.css"; // Import the global CSS here +import { AppProps } from "next/app"; function MyApp({ Component, pageProps }: AppProps) { return ; } -export default MyApp; \ No newline at end of file +export default MyApp; diff --git a/pages/debugger.tsx b/pages/debugger.tsx new file mode 100644 index 0000000..a4c46bf --- /dev/null +++ b/pages/debugger.tsx @@ -0,0 +1,317 @@ +import React, { useState } from "react"; +import { + simulate, + BundlerUserOperationData, + PackedUserOperation, + unwrap, + wrap, + SimulateType, + getUserOpHash, + INFRA_ADDRESS, +} from "@consenlabs/imaccount-sdk"; +import { Address } from "viem"; +import Header from "./header"; + +const Debugger = () => { + // Tenderly Config + const [TENDERLY_ACCESS_KEY, setTENDERLY_ACCESS_KEY] = useState(process.env.NEXT_PUBLIC_TENDERLY_ACCESS_KEY || ""); + const [projectOwner, setProjectOwner] = useState("ChiHaoLu"); + const [projectName, setProjectName] = useState("aatest"); + const [richBundlerEOA, setRichBundlerEOA] = useState
( + "0x4d7f573039fddc84fdb28515ba20d75ef6b987ff" + ); + const [network, setNetwork] = useState("11155111"); + const [blockNumber, setBlockNumber] = useState("latest"); + const [simulateType, setSimulateType] = useState( + SimulateType.Send + ); + + // userOp input + const [input, setInput] = useState(`{ + sender: "0x8b8aF275602891ddDf4d60783A80A01bf56f8F64", + nonce: "0x31", + callData: "0xe9ae5c53010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001842b67b570000000000000000000000000f7728cf90ad4cb9a14342827641056fd2e88a82e0000000000000000000000001c7d4b196cb0c7b01d743fbc6116a902379c72380000000000000000000000000000000000000000000000000000000000e4e1c00000000000000000000000000000000000000000000000000000000066f0e1be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc450000000000000000000000000000000000000000000000000000000066f0e1be000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000557a61f7aa49341fb0ef67b1376f84b72bf1063a678417b73b0a2bc06100a7b3d880f51c8837b46a14fa235ac1441ad436681fb0d73e39e869f79f52f46212fc9575f2bd6cb9c522f32be0e1ba49f0e2a3987c018e1c000000000000000000000000000000000000000000000000000000000000000000000000000000", + callGasLimit: "0x0", + verificationGasLimit: "0x0", + preVerificationGas: "0x0", + maxFeePerGas: "0x1", + maxPriorityFeePerGas: "0x1", + paymasterVerificationGasLimit: "0x0", + paymasterPostOpGasLimit: "0x0", + signature:"0x7a61f7aa49341fb0ef67b1376f84b72bf1063a67fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" + }`); + const [parsedInput, setParsedInput] = useState<{ + unwrapped: BundlerUserOperationData; + wrapped: PackedUserOperation; + serialized: string; + } | null>(null); + const [parseError, setParseError] = useState(null); + + // Result + const [loading, setLoading] = useState(false); + const [result, setResult] = useState(""); + + const isSimulateDisabled = + !TENDERLY_ACCESS_KEY || !projectOwner || !projectName || !richBundlerEOA; + + const handleSimulate = async () => { + if ( + !TENDERLY_ACCESS_KEY || + !projectOwner || + !projectName || + !richBundlerEOA || + !parsedInput + ) { + alert("Please fill in all required fields."); + return; + } + + setLoading(true); + try { + const simulationResult = await simulate( + simulateType, + TENDERLY_ACCESS_KEY, + richBundlerEOA, + parsedInput.wrapped, + projectOwner, + projectName, + network, + blockNumber + ); + + setResult(JSON.stringify(simulationResult, null, 2)); + } catch (error) { + console.error(error); + alert(`Simulation failed.\n${error}`); + setResult(""); + } finally { + setLoading(false); + } + }; + + function serialize(parsed: any): Record { + return Object.entries(parsed).reduce( + (acc, [key, value]) => { + if ( + typeof value === "string" && + /^0x[a-fA-F0-9]+$/.test(value) && + (key.includes("gas") || key.includes("Gas")) && + key !== "accountGasLimits" && + key !== "gasFees" + ) { + acc[key] = String(BigInt(value)); + } else if (typeof value === "bigint") { + acc[key] = String(value); + } else { + acc[key] = value; + } + return acc; + }, + {} as Record + ); + } + + function parse(parsed: { + unwrapped: BundlerUserOperationData; + wrapped: PackedUserOperation; + }): string { + return JSON.stringify( + { + wrapped: serialize(parsed.wrapped), + unwrapped: serialize(parsed.unwrapped), + }, + null, + 2 + ); + } + + const handleInputChange = (e: React.ChangeEvent) => { + setResult(""); + const text = e.target.value; + setInput(text); + try { + const parsed = JSON.parse( + text + .replace(/([a-zA-Z0-9]+):/g, '"$1":') // Add quotes around keys + .replace(/'/g, '"') // Replace single quotes with double quotes + ); + + // Check and transform BigInt values where necessary + if (parsed.accountGasLimits !== undefined) { + const serialized = parse({ + wrapped: parsed as PackedUserOperation, + unwrapped: unwrap(parsed as PackedUserOperation), + }); + setParsedInput({ + wrapped: parsed as PackedUserOperation, + unwrapped: unwrap(parsed as PackedUserOperation), + serialized: serialized, + }); + } else { + const serialized = parse({ + wrapped: wrap(parsed as BundlerUserOperationData), + unwrapped: parsed as BundlerUserOperationData, + }); + setParsedInput({ + wrapped: wrap(parsed as BundlerUserOperationData), + unwrapped: parsed as BundlerUserOperationData, + serialized: serialized, + }); + } + setParseError(null); + } catch (error) { + setParseError(`Invalid JSON format.\n${error}`); + setParsedInput(null); + } + }; + + const handleResultClick = () => { + const url = result.startsWith('"') ? JSON.parse(result) : result; + window.open(url, "_blank"); + }; + + return ( +
+
+ +

AA Simulator

+

1. Fill Up the Tenderly Config

+
+

+ This default configuration currently only supports the Sepolia + Testnet. +
If you wish to use a different network, please create a + Tenderly project on your own. +

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+ +

2. Give your userOp

+

Simulate Usage

+
+ +
+

UserOp Input

+
+