Skip to content

Commit

Permalink
display logs, function names, args
Browse files Browse the repository at this point in the history
  • Loading branch information
karooolis committed Oct 24, 2024
1 parent 38ebb01 commit 001a243
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,29 @@ export function TransactionTableRow({ row }: { row: Row<ObservedTransaction> })
<Separator className="my-5" />
<div className="flex items-start gap-x-4">
<h3 className="w-[45px] flex-shrink-0 text-2xs font-bold uppercase">Inputs</h3>
{Array.isArray(data.functionData?.args) && data.functionData?.args.length > 0 ? (

{data.calls && data.calls.length > 0 ? (
<div className="flex w-full flex-col gap-y-4">
{data.calls.map((call, idx) => {
return (
<div key={idx} className="min-w-0 flex-grow border border-white/20 p-2">
{call.args?.map((arg, idx) => (
<div key={idx} className="flex">
<span className="flex-shrink-0 text-xs text-white/60">arg {idx + 1}:</span>
<span className="ml-2 break-all text-xs">
{typeof arg === "object" && arg !== null ? JSON.stringify(arg, null, 2) : String(arg)}
</span>
</div>
))}
</div>
);
})}
</div>
) : (
<p className="text-2xs uppercase text-white/60">No inputs</p>
)}

{/* {Array.isArray(data.functionData?.args) && data.functionData?.args.length > 0 ? (
<div className="min-w-0 flex-grow border border-white/20 p-2">
{data.functionData?.args?.map((arg, idx) => (
<div key={idx} className="flex">
Expand All @@ -94,7 +116,7 @@ export function TransactionTableRow({ row }: { row: Row<ObservedTransaction> })
</div>
) : (
<p className="text-2xs uppercase text-white/60">No inputs</p>
)}
)} */}
</div>

{data.error ? (
Expand Down Expand Up @@ -128,7 +150,7 @@ export function TransactionTableRow({ row }: { row: Row<ObservedTransaction> })
{Object.entries(args).map(([key, value]) => (
<li key={key} className="mt-1 flex">
<span className="flex-shrink-0 text-xs text-white/60">{key}: </span>
<span className="ml-2 break-all text-xs">{value as never}</span>
<span className="ml-2 break-all text-xs">{String(value)}</span>
</li>
))}
</ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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;

Check failure on line 2073 in packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx

View workflow job for this annotation

GitHub Actions / Run lint

'worldTo' is assigned a value but never used

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;

Check failure on line 2095 in packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsWatcher.tsx

View workflow job for this annotation

GitHub Actions / Run lint

'transactionError' is assigned a value but never used
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;
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down
4 changes: 4 additions & 0 deletions packages/explorer/src/observer/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ channel.addEventListener("message", ({ data }: MessageEvent<Message>) => {
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],
},
},
Expand Down

0 comments on commit 001a243

Please sign in to comment.