From 001a2432b3bd2e694601c9f7fc870a928dd05890 Mon Sep 17 00:00:00 2001 From: karooolis Date: Thu, 24 Oct 2024 20:17:14 +0300 Subject: [PATCH] display logs, function names, args --- .../observe/TransactionTableRow.tsx | 28 +- .../observe/TransactionsWatcher.tsx | 279 ++++++------------ .../observe/useObservedTransactions.ts | 8 + packages/explorer/src/observer/store.ts | 4 + 4 files changed, 133 insertions(+), 186 deletions(-) diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionTableRow.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionTableRow.tsx index d34d1f640d..ec812eb79d 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionTableRow.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionTableRow.tsx @@ -81,7 +81,29 @@ export function TransactionTableRow({ row }: { row: Row })

Inputs

- {Array.isArray(data.functionData?.args) && data.functionData?.args.length > 0 ? ( + + {data.calls && data.calls.length > 0 ? ( +
+ {data.calls.map((call, idx) => { + return ( +
+ {call.args?.map((arg, idx) => ( +
+ arg {idx + 1}: + + {typeof arg === "object" && arg !== null ? JSON.stringify(arg, null, 2) : String(arg)} + +
+ ))} +
+ ); + })} +
+ ) : ( +

No inputs

+ )} + + {/* {Array.isArray(data.functionData?.args) && data.functionData?.args.length > 0 ? (
{data.functionData?.args?.map((arg, idx) => (
@@ -94,7 +116,7 @@ export function TransactionTableRow({ row }: { row: Row })
) : (

No inputs

- )} + )} */}
{data.error ? ( @@ -128,7 +150,7 @@ export function TransactionTableRow({ row }: { row: Row }) {Object.entries(args).map(([key, value]) => (
  • {key}: - {value as never} + {String(value)}
  • ))} diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx index 858b37b181..fa6280d886 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx @@ -1,6 +1,16 @@ import { useParams } from "next/navigation"; -import { Address, BaseError, Hex, TransactionReceipt, decodeFunctionData, parseAbi, parseEventLogs } from "viem"; +import { + Address, + BaseError, + Hex, + TransactionReceipt, + decodeFunctionData, + getAbiItem, + parseAbi, + parseEventLogs, +} from "viem"; import { PackedUserOperation, entryPoint07Abi, entryPoint07Address } from "viem/account-abstraction"; +import { formatAbiItem } from "viem/utils"; import { useConfig, useWatchBlocks } from "wagmi"; import { getTransaction, simulateContract, waitForTransactionReceipt } from "wagmi/actions"; import { useStore } from "zustand"; @@ -719,94 +729,6 @@ export const doomWorldAbi = [ ], stateMutability: "nonpayable", }, - { - type: "function", - name: "registerNamespace", - inputs: [ - { - name: "namespaceId", - type: "bytes32", - internalType: "ResourceId", - }, - ], - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - name: "registerNamespaceDelegation", - inputs: [ - { - name: "namespaceId", - type: "bytes32", - internalType: "ResourceId", - }, - { - name: "delegationControlId", - type: "bytes32", - internalType: "ResourceId", - }, - { - name: "initCallData", - type: "bytes", - internalType: "bytes", - }, - ], - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - name: "registerRootFunctionSelector", - inputs: [ - { - name: "systemId", - type: "bytes32", - internalType: "ResourceId", - }, - { - name: "worldFunctionSignature", - type: "string", - internalType: "string", - }, - { - name: "systemFunctionSignature", - type: "string", - internalType: "string", - }, - ], - outputs: [ - { - name: "worldFunctionSelector", - type: "bytes4", - internalType: "bytes4", - }, - ], - stateMutability: "nonpayable", - }, - { - type: "function", - name: "registerStoreHook", - inputs: [ - { - name: "tableId", - type: "bytes32", - internalType: "ResourceId", - }, - { - name: "hookAddress", - type: "address", - internalType: "contract IStoreHook", - }, - { - name: "enabledHooksBitmap", - type: "uint8", - internalType: "uint8", - }, - ], - outputs: [], - stateMutability: "nonpayable", - }, { type: "function", name: "registerSystem", @@ -2138,96 +2060,85 @@ export function TransactionsWatcher() { const transaction = await getTransaction(wagmiConfig, { hash }); if (transaction.to?.toLowerCase() === entryPoint07Address.toLowerCase()) { + console.log("entry point call", transaction); + const decodedEntryPointCall = decodeFunctionData({ abi: entryPoint07Abi, data: transaction.input, }); - const userOps = decodedEntryPointCall.args[0] as PackedUserOperation[]; - const decodedSmartAccountCall = decodeFunctionData({ - abi: parseAbi(["function execute(address target, uint256 value, bytes calldata data)"]), - data: userOps[0].callData, - }); + console.log("decodedEntryPointCall", decodedEntryPointCall); - const data = decodedSmartAccountCall.args[2]; - let functionName: string | undefined; - let args: readonly unknown[] | undefined; - let transactionError: BaseError | undefined; + const userOps = decodedEntryPointCall.args[0] as PackedUserOperation[]; + const worldTo = decodedEntryPointCall.args[1] as Address; - try { - const functionData = decodeFunctionData({ abi, data }); + for (const userOp of userOps) { + const decodedSmartAccountCall = decodeFunctionData({ + abi: parseAbi([ + "function execute(address target, uint256 value, bytes calldata data)", + "function executeBatch((address target,uint256 value,bytes data)[])", + ]), + data: userOp.callData, + }); - functionName = functionData.functionName; - args = functionData.args; - } catch (error) { - transactionError = error as BaseError; - functionName = transaction.input.length > 10 ? transaction.input.slice(0, 10) : "unknown"; - } + console.log("decodedSmartAccountCall (userOp) within loop:", decodedSmartAccountCall); - const write = Object.values(observerWrites).find((write) => write.hash === hash); - setTransaction({ - hash, - writeId: write?.writeId ?? hash, - from: transaction.from, - timestamp, - transaction, - status: "pending", - functionData: { - functionName, - args, - }, - value: transaction.value, - }); + const calls = decodedSmartAccountCall.args[0].map((worldFunction) => { + let functionName: string | undefined; + let args: readonly unknown[] | undefined; + let transactionError: BaseError | undefined; // TODO: what to do with this? + try { + const functionData = decodeFunctionData({ abi: doomWorldAbi, data: worldFunction.data }); + functionName = functionData.functionName; + args = functionData.args; + } catch (error) { + transactionError = error as BaseError; + functionName = transaction.input.length > 10 ? transaction.input.slice(0, 10) : "unknown"; + } - let receipt: TransactionReceipt | undefined; - try { - receipt = await waitForTransactionReceipt(wagmiConfig, { hash }); - } catch { - console.error(`Failed to fetch transaction receipt. Transaction hash: ${hash}`); - } + const functionAbiItem = getAbiItem({ + abi: doomWorldAbi, + name: functionName, + args, + } as never); - if (receipt && receipt.status === "reverted" && functionName) { - try { - // Simulate the failed transaction to retrieve the revert reason - // Note, it only works for functions that are declared in the ABI - // See: https://github.com/wevm/viem/discussions/462 - await simulateContract(wagmiConfig, { - account: transaction.from, - address: worldAddress, - abi, - value: transaction.value, - blockNumber: receipt.blockNumber, + return { + to: worldFunction.target, + functionSignature: functionAbiItem ? formatAbiItem(functionAbiItem) : "unknown", functionName, args, - }); - } catch (error) { - transactionError = error as BaseError; - } - } + }; + }); - const status = receipt ? receipt.status : "unknown"; + console.log("calls", calls); - // TODO: - // event UserOperationEvent( - // bytes32 indexed userOpHash, - // address indexed sender, - // address indexed paymaster, - // uint256 nonce, - // bool success, - // uint256 actualGasCost, - // uint256 actualGasUsed - // ); - const logs = parseEventLogs({ - abi: [...abi, userOperationEventAbi], - logs: receipt?.logs || [], - }); + const write = undefined; // TODO: add back + setTransaction({ + hash, + writeId: write?.writeId ?? hash, + from: transaction.from, + timestamp, + transaction, + status: "pending", + calls, + value: transaction.value, + }); - updateTransaction(hash, { - receipt, - logs, - status, - error: transactionError as BaseError, - }); + const receipt = await waitForTransactionReceipt(wagmiConfig, { hash }); + const logs = parseEventLogs({ + abi: [...abi, userOperationEventAbi], + logs: receipt?.logs || [], + }); + + console.log("logs:", logs); + + updateTransaction(hash, { + receipt, + logs, + status: "success", + error: undefined, // TODO: transactionError as BaseError, + }); + } } else if (transaction.to === worldAddress) { let functionName: string | undefined; let args: readonly unknown[] | undefined; @@ -2301,28 +2212,30 @@ export function TransactionsWatcher() { ); useEffect(() => { - for (const write of Object.values(observerWrites)) { - const hash = write.hash; - if (hash) { - // TODO: add back -> && write.address.toLowerCase() === worldAddress.toLowerCase() - const transaction = transactions.find((transaction) => transaction.hash === hash); - if (!transaction) { - handleTransaction(hash, BigInt(write.time) / 1000n); - } - } - } + // for (const write of Object.values(observerWrites)) { + // const hash = write.hash; + // if (hash) { + // // TODO: add back -> && write.address.toLowerCase() === worldAddress.toLowerCase() + // const transaction = transactions.find((transaction) => transaction.hash === hash); + // if (!transaction) { + // handleTransaction(hash, BigInt(write.time) / 1000n); + // } + // } + // } }, [handleTransaction, observerWrites, transactions, worldAddress]); - // useWatchBlocks({ - // onBlock(block) { - // for (const hash of block.transactions) { - // if (transactions.find((transaction) => transaction.hash === hash)) continue; - // handleTransaction(hash, block.timestamp); - // } - // }, - // chainId, - // pollingInterval: 500, - // }); + useWatchBlocks({ + onBlock(block) { + for (const hash of block.transactions) { + console.log("hash", hash); + + if (transactions.find((transaction) => transaction.hash === hash)) continue; + handleTransaction(hash, block.timestamp); + } + }, + chainId, + pollingInterval: 500, + }); return null; } diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useObservedTransactions.ts b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useObservedTransactions.ts index 6f1340df28..92ea9641a9 100644 --- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useObservedTransactions.ts +++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useObservedTransactions.ts @@ -63,6 +63,14 @@ export function useObservedTransactions() { args: [], }, calls: write.calls, // TODO: rename to functions? + receipt: write.events.find( + (event): event is Message<"waitForUserOperationReceipt:result"> => + event.type === "waitForUserOperationReceipt:result", + )?.receipt, + logs: write.events.find( + (event): event is Message<"waitForUserOperationReceipt:result"> => + event.type === "waitForUserOperationReceipt:result", + )?.logs, value: 0n, error: undefined, }); diff --git a/packages/explorer/src/observer/store.ts b/packages/explorer/src/observer/store.ts index dace3d9d20..cd5e3d5444 100644 --- a/packages/explorer/src/observer/store.ts +++ b/packages/explorer/src/observer/store.ts @@ -48,6 +48,10 @@ channel.addEventListener("message", ({ data }: MessageEvent) => { data.type === "waitForTransactionReceipt" || data.type === "waitForUserOperationReceipt:result" ? data.hash : write.hash, + receipt: + data.type === "waitForTransactionReceipt:result" || data.type === "waitForUserOperationReceipt:result" + ? data.receipt + : write.receipt, events: [...write.events, data], }, },