Skip to content

Commit 88b9daf

Browse files
frolicalvrs
andauthored
feat(explorer): get observer url from chain config (#3366)
Co-authored-by: alvarius <alvarius@lattice.xyz>
1 parent 07b6be8 commit 88b9daf

4 files changed

Lines changed: 129 additions & 89 deletions

File tree

.changeset/orange-houses-drop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@latticexyz/explorer": patch
3+
---
4+
5+
Observer transport now uses the `blockExplorers.worldsExplorer.url` from the chain config if no `explorerUrl` is provided.

.changeset/yellow-spoons-mate.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@latticexyz/common": patch
3+
---
4+
5+
Updated Rhodolite chain config.

packages/common/src/chains/rhodolite.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { chainConfig } from "viem/op-stack";
22
import { MUDChain } from "./types";
3+
import { Chain } from "viem";
34

45
const sourceId = 17001;
56

7+
const defaultRpcUrls = {
8+
http: ["https://rpc.rhodolitechain.com"],
9+
webSocket: ["wss://rpc.rhodolitechain.com"],
10+
} as const satisfies Chain["rpcUrls"]["default"];
11+
612
export const rhodolite = {
713
...chainConfig,
814
name: "Rhodolite Devnet",
@@ -11,9 +17,26 @@ export const rhodolite = {
1117
sourceId,
1218
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
1319
rpcUrls: {
20+
default: defaultRpcUrls,
21+
bundler: defaultRpcUrls,
22+
quarryPassIssuer: defaultRpcUrls,
23+
wiresaw: defaultRpcUrls,
24+
},
25+
contracts: {
26+
...chainConfig.contracts,
27+
quarryPaymaster: {
28+
address: "0x61f22c3827d90c390e0e2aaf220971524ac0a68d",
29+
blockCreated: 11262,
30+
},
31+
},
32+
blockExplorers: {
1433
default: {
15-
http: ["https://rpc.rhodolitechain.com"],
16-
webSocket: ["wss://rpc.rhodolitechain.com"],
34+
name: "Blockscout",
35+
url: "https://explorer.rhodolitechain.com",
36+
},
37+
worldsExplorer: {
38+
name: "MUD Worlds Explorer",
39+
url: "https://explorer.mud.dev/rhodolite/worlds",
1740
},
1841
},
1942
iconUrls: ["https://redstone.xyz/chain-icons/rhodolite.png"],

packages/explorer/src/observer/decorator.ts

Lines changed: 94 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -15,106 +15,113 @@ export type ObserverOptions = {
1515

1616
let writeCounter = 0;
1717

18-
export function observer({ explorerUrl = "http://localhost:13690", waitForTransaction }: ObserverOptions = {}): <
18+
export function observer({ explorerUrl: initialExplorerUrl, waitForTransaction }: ObserverOptions = {}): <
1919
transport extends Transport,
2020
chain extends Chain | undefined = Chain | undefined,
2121
account extends Account | undefined = Account | undefined,
2222
>(
2323
client: Client<transport, chain, account>,
2424
) => Pick<WalletActions<chain, account>, "writeContract"> & Pick<BundlerActions, "sendUserOperation"> {
25-
const emit = createBridge({ url: `${explorerUrl}/internal/observer-relay` });
26-
27-
return (client) => ({
28-
async sendUserOperation(args) {
29-
const writeId = `${client.uid}-${++writeCounter}`;
30-
const write = getAction(client, sendUserOperation, "sendUserOperation")(args);
31-
if (!("calls" in args) || !args.calls) return write;
32-
33-
const calls = args.calls
34-
.map((call) => {
35-
if (!call || typeof call !== "object") return undefined;
36-
if (!("to" in call) || typeof call.to !== "string") return undefined;
37-
if (!("functionName" in call) || typeof call.functionName !== "string") return undefined;
38-
if (!("args" in call) || !Array.isArray(call.args)) return undefined;
39-
40-
return {
41-
to: call.to as Address,
42-
functionName: call.functionName,
43-
args: call.args,
44-
...("value" in call && typeof call.value === "bigint" && { value: call.value }),
45-
};
46-
})
47-
.filter(isDefined);
48-
if (calls.length === 0) return write;
49-
50-
emit("write", {
51-
writeId,
52-
// TODO: type as SessionClient once available from entrykit
53-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
54-
from: (client as any).userAddress ?? client.account!.address,
55-
calls,
56-
});
57-
Promise.allSettled([write]).then(([result]) => {
58-
emit("write:result", { ...result, writeId });
59-
});
60-
61-
write.then((userOpHash) => {
62-
const receipt = getAction(
63-
client,
64-
waitForUserOperationReceipt,
65-
"waitForUserOperationReceipt",
66-
)({ hash: userOpHash });
67-
68-
emit("waitForUserOperationReceipt", { writeId, userOpHash });
69-
Promise.allSettled([receipt]).then(([result]) => {
70-
emit("waitForUserOperationReceipt:result", { ...result, writeId });
25+
return (client) => {
26+
const explorerUrl = initialExplorerUrl ?? client.chain?.blockExplorers?.worldsExplorer?.url;
27+
if (!explorerUrl) return {} as never;
28+
29+
const emit = createBridge({ url: new URL("/internal/observer-relay", explorerUrl).toString() });
30+
31+
return {
32+
async sendUserOperation(args) {
33+
const writeId = `${client.uid}-${++writeCounter}`;
34+
const write = getAction(client, sendUserOperation, "sendUserOperation")(args);
35+
if (!("calls" in args) || !args.calls) return write;
36+
37+
const calls = args.calls
38+
.map((call) => {
39+
if (!call || typeof call !== "object") return undefined;
40+
if (!("to" in call) || typeof call.to !== "string") return undefined;
41+
if (!("functionName" in call) || typeof call.functionName !== "string") return undefined;
42+
if (!("args" in call) || !Array.isArray(call.args)) return undefined;
43+
44+
return {
45+
to: call.to as Address,
46+
functionName: call.functionName,
47+
args: call.args,
48+
...("value" in call && typeof call.value === "bigint" && { value: call.value }),
49+
};
50+
})
51+
.filter(isDefined);
52+
if (calls.length === 0) return write;
53+
54+
emit("write", {
55+
writeId,
56+
// TODO: type as SessionClient once available from entrykit
57+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
58+
from: (client as any).userAddress ?? client.account!.address,
59+
calls,
7160
});
72-
});
73-
74-
return write;
75-
},
76-
77-
async writeContract(args) {
78-
const writeId = `${client.uid}-${++writeCounter}`;
79-
const write = getAction(client, writeContract, "writeContract")(args);
80-
81-
emit("write", {
82-
writeId,
83-
from: client.account!.address,
84-
calls: [
85-
{
86-
to: args.address,
87-
functionName: args.functionName,
88-
args: (args.args ?? []) as never,
89-
...(args.value && { value: args.value }),
90-
},
91-
],
92-
});
93-
Promise.allSettled([write]).then(([result]) => {
94-
emit("write:result", { ...result, writeId });
95-
});
96-
97-
write.then((hash) => {
98-
const receipt = getAction(client, waitForTransactionReceipt, "waitForTransactionReceipt")({ hash });
99-
100-
emit("waitForTransactionReceipt", { writeId, hash });
101-
Promise.allSettled([receipt]).then(([result]) => {
102-
emit("waitForTransactionReceipt:result", { ...result, writeId });
61+
Promise.allSettled([write]).then(([result]) => {
62+
emit("write:result", { ...result, writeId });
63+
});
64+
65+
write.then((userOpHash) => {
66+
const receipt = getAction(
67+
client,
68+
waitForUserOperationReceipt,
69+
"waitForUserOperationReceipt",
70+
)({ hash: userOpHash });
71+
72+
emit("waitForUserOperationReceipt", { writeId, userOpHash });
73+
Promise.allSettled([receipt]).then(([result]) => {
74+
emit("waitForUserOperationReceipt:result", { ...result, writeId });
75+
});
76+
});
77+
78+
return write;
79+
},
80+
81+
async writeContract(args) {
82+
const writeId = `${client.uid}-${++writeCounter}`;
83+
const write = getAction(client, writeContract, "writeContract")(args);
84+
85+
emit("write", {
86+
writeId,
87+
// TODO: type as SessionClient once available from entrykit
88+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
89+
from: (client as any).userAddress ?? client.account!.address,
90+
calls: [
91+
{
92+
to: args.address,
93+
functionName: args.functionName,
94+
args: (args.args ?? []) as never,
95+
...(args.value && { value: args.value }),
96+
},
97+
],
98+
});
99+
Promise.allSettled([write]).then(([result]) => {
100+
emit("write:result", { ...result, writeId });
103101
});
104-
});
105102

106-
if (waitForTransaction) {
107103
write.then((hash) => {
108-
const receipt = waitForTransaction(hash);
104+
const receipt = getAction(client, waitForTransactionReceipt, "waitForTransactionReceipt")({ hash });
109105

110-
emit("waitForTransaction", { writeId });
106+
emit("waitForTransactionReceipt", { writeId, hash });
111107
Promise.allSettled([receipt]).then(([result]) => {
112-
emit("waitForTransaction:result", { ...result, writeId });
108+
emit("waitForTransactionReceipt:result", { ...result, writeId });
113109
});
114110
});
115-
}
116111

117-
return write;
118-
},
119-
});
112+
if (waitForTransaction) {
113+
write.then((hash) => {
114+
const receipt = waitForTransaction(hash);
115+
116+
emit("waitForTransaction", { writeId });
117+
Promise.allSettled([receipt]).then(([result]) => {
118+
emit("waitForTransaction:result", { ...result, writeId });
119+
});
120+
});
121+
}
122+
123+
return write;
124+
},
125+
};
126+
};
120127
}

0 commit comments

Comments
 (0)