diff --git a/.changeset/neat-impalas-arrive.md b/.changeset/neat-impalas-arrive.md
new file mode 100644
index 0000000000..55829e9925
--- /dev/null
+++ b/.changeset/neat-impalas-arrive.md
@@ -0,0 +1,5 @@
+---
+"@latticexyz/explorer": patch
+---
+
+Observe tab is now populated by rejected transactions coming from the `observer` transport wrapper.
diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/Confirmations.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/Confirmations.tsx
index a3a102e06c..25c91a432e 100644
--- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/Confirmations.tsx
+++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/Confirmations.tsx
@@ -10,6 +10,7 @@ export function Confirmations({ hash }: { hash?: Hex }) {
chainId,
query: {
refetchInterval: 1000,
+ enabled: !!hash,
},
});
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 945dbff048..5e874cdebb 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
@@ -9,17 +9,29 @@ import { Confirmations } from "./Confirmations";
import { columns } from "./TransactionsTable";
import { WatchedTransaction } from "./useTransactionWatcher";
-function TranctionTableRowDataCell({ label, children }: { label: string; children: React.ReactNode }) {
+function TransactionTableRowDataCell({
+ label,
+ status,
+ children,
+}: {
+ label: string;
+ status: WatchedTransaction["status"];
+ children: React.ReactNode;
+}) {
return (
{label}
-
{children ?? }
+
+ {children ??
+ (status === "rejected" ? N/A : )}
+
);
}
export function TransactionTableRow({ row }: { row: Row }) {
const data = row?.original;
+ const status = data.status;
const logs = data?.logs;
const receipt = data?.receipt;
@@ -48,25 +60,27 @@ export function TransactionTableRow({ row }: { row: Row }) {
{data && (
<>
-
-
-
-
- {data.transaction?.value !== undefined ? `${formatEther(data.transaction.value)} ETH` : null}
-
- {receipt?.gasUsed.toString()}
-
+
+ {status !== "rejected" ? : null}
+
+
+ {data.value ? formatEther(data.value) : 0} ETH
+
+
+ {receipt?.gasUsed.toString()}
+
+
{receipt?.effectiveGasPrice.toString()}
-
-
+
+
{receipt ? `${formatEther(receipt.gasUsed * receipt.effectiveGasPrice)} ETH` : null}
-
+
-
Inputs
+
Inputs
{Array.isArray(data.functionData?.args) && data.functionData?.args.length > 0 ? (
{data.functionData?.args?.map((arg, idx) => (
@@ -85,7 +99,7 @@ export function TransactionTableRow({ row }: { row: Row
}) {
<>
-
Error
+
Error
{data.error ? (
{data.error.message}
@@ -100,7 +114,7 @@ export function TransactionTableRow({ row }: { row: Row
}) {
-
Logs
+
Logs
{Array.isArray(logs) && logs.length > 0 ? (
diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx
index a4319762ff..40a8546b29 100644
--- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx
+++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/TransactionsTable.tsx
@@ -18,6 +18,9 @@ export const columns = [
columnHelper.accessor("receipt.blockNumber", {
header: "Block",
cell: (row) => {
+ const status = row.row.original.status;
+ if (status === "rejected") return N/A;
+
const blockNumber = row.getValue();
if (!blockNumber) return ;
return (
@@ -28,7 +31,7 @@ export const columns = [
);
},
}),
- columnHelper.accessor("transaction.from", {
+ columnHelper.accessor("from", {
header: "From",
cell: (row) => {
const from = row.getValue();
@@ -49,9 +52,9 @@ export const columns = [
return (
{functionName}
- {status === "pending" && }
+ {status === "pending" && }
{status === "success" && }
- {status === "reverted" && }
+ {(status === "reverted" || status === "rejected") && }
);
},
@@ -59,6 +62,9 @@ export const columns = [
columnHelper.accessor("hash", {
header: "Tx hash",
cell: (row) => {
+ const status = row.row.original.status;
+ if (status === "rejected") return N/A;
+
const hash = row.getValue();
if (!hash) return ;
return (
diff --git a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useTransactionWatcher.ts b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useTransactionWatcher.ts
index 57b8407103..97c5125d04 100644
--- a/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useTransactionWatcher.ts
+++ b/packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/observe/useTransactionWatcher.ts
@@ -1,6 +1,7 @@
import { useParams } from "next/navigation";
import {
AbiFunction,
+ Address,
BaseError,
DecodeFunctionDataReturnType,
Hex,
@@ -15,17 +16,21 @@ import { useConfig, useWatchBlocks } from "wagmi";
import { useStore } from "zustand";
import { useCallback, useEffect, useMemo, useState } from "react";
import { getTransaction, simulateContract, waitForTransactionReceipt } from "@wagmi/core";
+import { Message } from "../../../../../../observer/messages";
import { Write, store } from "../../../../../../observer/store";
import { useChain } from "../../../../hooks/useChain";
import { useWorldAbiQuery } from "../../../../queries/useWorldAbiQuery";
export type WatchedTransaction = {
hash?: Hex;
+ writeId?: string;
+ from?: Address;
timestamp?: bigint;
transaction?: Transaction;
functionData?: DecodeFunctionDataReturnType;
+ value?: bigint;
receipt?: TransactionReceipt;
- status: "pending" | "success" | "reverted" | "unknown";
+ status: "pending" | "success" | "reverted" | "rejected" | "unknown";
write?: Write;
logs?: Log[];
error?: BaseError;
@@ -63,6 +68,7 @@ export function useTransactionWatcher() {
setTransactions((prevTransactions) => [
{
hash,
+ from: transaction.from,
timestamp,
transaction,
status: "pending",
@@ -70,6 +76,7 @@ export function useTransactionWatcher() {
functionName,
args,
},
+ value: transaction.value,
},
...prevTransactions,
]);
@@ -114,7 +121,7 @@ export function useTransactionWatcher() {
receipt,
logs,
status,
- error: transactionError,
+ error: transactionError as BaseError,
}
: transaction,
),
@@ -147,19 +154,22 @@ export function useTransactionWatcher() {
});
const mergedTransactions = useMemo((): WatchedTransaction[] => {
- const mergedMap = new Map();
+ const mergedMap = new Map();
for (const write of Object.values(observerWrites)) {
const parsedAbiItem = parseAbiItem(`function ${write.functionSignature}`) as AbiFunction;
- const functionData = {
- functionName: parsedAbiItem.name,
- args: write.args,
- };
+ const writeResult = write.events.find((event): event is Message<"write:result"> => event.type === "write:result");
- mergedMap.set(write.hash, {
- status: "pending",
+ mergedMap.set(write.hash || write.writeId, {
+ from: write.from,
+ status: writeResult?.status === "rejected" ? "rejected" : "pending",
timestamp: BigInt(write.time) / 1000n,
- functionData,
+ functionData: {
+ functionName: parsedAbiItem.name,
+ args: write.args,
+ },
+ value: write.value,
+ error: writeResult && "reason" in writeResult ? (writeResult.reason as BaseError) : undefined,
write,
});
}
diff --git a/packages/explorer/src/observer/decorator.ts b/packages/explorer/src/observer/decorator.ts
index ab4c109bf4..f5afcf1f10 100644
--- a/packages/explorer/src/observer/decorator.ts
+++ b/packages/explorer/src/observer/decorator.ts
@@ -37,8 +37,10 @@ export function observer({ explorerUrl = "http://localhost:13690", waitForTransa
emit("write", {
writeId,
address: args.address,
+ from: client.account!.address,
functionSignature: formatAbiItem(functionAbiItem),
args: (args.args ?? []) as never,
+ value: args.value,
});
Promise.allSettled([write]).then(([result]) => {
emit("write:result", { ...result, writeId });
diff --git a/packages/explorer/src/observer/messages.ts b/packages/explorer/src/observer/messages.ts
index d2d5f5db0f..2526a25fbe 100644
--- a/packages/explorer/src/observer/messages.ts
+++ b/packages/explorer/src/observer/messages.ts
@@ -6,12 +6,12 @@ export type Messages = {
write: {
writeId: string;
address: Address;
+ from: Address;
functionSignature: string;
args: unknown[];
+ value?: bigint;
};
- "write:result": PromiseSettledResult & {
- writeId: string;
- };
+ "write:result": PromiseSettledResult & { writeId: string };
waitForTransactionReceipt: {
writeId: string;
hash: Hash;
diff --git a/packages/explorer/src/observer/store.ts b/packages/explorer/src/observer/store.ts
index 5c4e78b414..19c2196dae 100644
--- a/packages/explorer/src/observer/store.ts
+++ b/packages/explorer/src/observer/store.ts
@@ -10,11 +10,14 @@ export type Write = {
writeId: string;
type: MessageType;
hash?: Hex;
+ from?: Address;
address: Address;
functionSignature: string;
args: unknown[];
+ value?: bigint;
time: number;
events: Message>[];
+ error?: Error;
};
export type State = {