Skip to content

Commit d2c77c2

Browse files
authored
feat(explorer): url params for interact and decode forms (#3713)
1 parent 1e9047e commit d2c77c2

6 files changed

Lines changed: 251 additions & 139 deletions

File tree

.changeset/four-otters-attack.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+
Interact and Decode forms now support URL parameters.

packages/explorer/src/app/(explorer)/[chainName]/worlds/[worldAddress]/decode/DecodeForm.tsx

Lines changed: 83 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import { parseAsString, useQueryState } from "nuqs";
34
import {
45
AbiFunction,
56
AbiItem,
@@ -11,7 +12,7 @@ import {
1112
} from "viem";
1213
import { formatAbiItem } from "viem/utils";
1314
import * as z from "zod";
14-
import { useMemo, useState } from "react";
15+
import { useCallback, useEffect, useMemo, useState } from "react";
1516
import "react18-json-view/src/dark.css";
1617
import "react18-json-view/src/style.css";
1718
import { useForm } from "react-hook-form";
@@ -90,66 +91,69 @@ function decodeAbiItem(abiItem: AbiFunction | AbiError, data: Hex): DecodedFunct
9091
export function DecodeForm() {
9192
const { data: worldData, isLoading: isWorldAbiLoading } = useWorldAbiQuery();
9293
const { data: systemData, isLoading: isSystemAbisLoading } = useSystemAbisQuery();
94+
const [encoded, setEncoded] = useQueryState("encoded", parseAsString.withDefault(""));
9395
const [decoded, setDecoded] = useState<AbiFunction | AbiError | ResourceItem | SelectorDatabase>();
94-
const [encoded, setEncoded] = useState<string | undefined>();
9596
const form = useForm<z.infer<typeof formSchema>>({
9697
resolver: zodResolver(formSchema),
9798
});
9899

99-
const onSubmit = async ({ encodedData }: z.infer<typeof formSchema>) => {
100-
setEncoded(encodedData);
101-
if (!encodedData) return;
100+
const onSubmit = useCallback(
101+
async ({ encodedData }: z.infer<typeof formSchema>) => {
102+
setEncoded(encodedData || null);
103+
if (!encodedData) return;
102104

103-
const selector = encodedData.substring(0, 10);
104-
const worldAbi = worldData?.abi || [];
105-
const systemsAbis = systemData ? Object.values(systemData) : [];
106-
const abis = [worldAbi, ...systemsAbis].flat();
105+
const selector = encodedData.substring(0, 10);
106+
const worldAbi = worldData?.abi || [];
107+
const systemsAbis = systemData ? Object.values(systemData) : [];
108+
const abis = [worldAbi, ...systemsAbis].flat();
107109

108-
// Try to find matching ABI item
109-
const abiItem = abis.find((item): item is AbiFunction | AbiError => {
110-
if (isAbiFunction(item)) {
111-
return toFunctionSelector(item) === selector;
112-
}
113-
if (isAbiError(item)) {
114-
return getErrorSelector(item) === selector;
115-
}
116-
return false;
117-
});
118-
119-
if (abiItem) {
120-
setDecoded(abiItem);
121-
return;
122-
}
110+
// Try to find matching ABI item
111+
const abiItem = abis.find((item): item is AbiFunction | AbiError => {
112+
if (isAbiFunction(item)) {
113+
return toFunctionSelector(item) === selector;
114+
}
115+
if (isAbiError(item)) {
116+
return getErrorSelector(item) === selector;
117+
}
118+
return false;
119+
});
123120

124-
// Try to decode as resource
125-
try {
126-
const resource = hexToResource(encodedData as Hex);
127-
if (resource) {
128-
setDecoded({ type: "resource", id: encodedData, resource });
121+
if (abiItem) {
122+
setDecoded(abiItem);
129123
return;
130124
}
131-
} catch {
132-
// Error decoding resource
133-
}
134125

135-
// Try to find in 4-byte database
136-
if (selector.length !== 10) return;
137-
try {
138-
const response = await fetch(`https://www.4byte.directory/api/v1/signatures/?hex_signature=${selector}`);
139-
const data = await response.json();
140-
if (data.results.length > 0) {
141-
setDecoded({
142-
type: "signature",
143-
selectors: data.results.map((result: { text_signature: string }) => result.text_signature),
144-
});
145-
return;
126+
// Try to decode as resource
127+
try {
128+
const resource = hexToResource(encodedData as Hex);
129+
if (resource) {
130+
setDecoded({ type: "resource", id: encodedData, resource });
131+
return;
132+
}
133+
} catch {
134+
// Error decoding resource
146135
}
147-
} catch {
148-
// Error fetching 4byte data
149-
}
150136

151-
setDecoded(undefined);
152-
};
137+
// Try to find in 4-byte database
138+
if (selector.length !== 10) return;
139+
try {
140+
const response = await fetch(`https://www.4byte.directory/api/v1/signatures/?hex_signature=${selector}`);
141+
const data = await response.json();
142+
if (data.results.length > 0) {
143+
setDecoded({
144+
type: "signature",
145+
selectors: data.results.map((result: { text_signature: string }) => result.text_signature),
146+
});
147+
return;
148+
}
149+
} catch {
150+
// Error fetching 4byte data
151+
}
152+
153+
setDecoded(undefined);
154+
},
155+
[setEncoded, worldData?.abi, systemData],
156+
);
153157

154158
const results = useMemo<Result[]>(() => {
155159
if (!decoded || !encoded) return [];
@@ -182,27 +186,44 @@ export function DecodeForm() {
182186
return [];
183187
}, [decoded, encoded]);
184188

189+
useEffect(() => {
190+
if (encoded) {
191+
form.setValue("encodedData", encoded);
192+
form.handleSubmit(onSubmit)();
193+
}
194+
}, [encoded, form, onSubmit]);
195+
185196
if (isWorldAbiLoading || isSystemAbisLoading) {
186197
return <Skeleton className="h-[152px] w-full" />;
187198
}
188199

189200
return (
190201
<Form {...form}>
191202
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
192-
<FormField
193-
control={form.control}
194-
name="encodedData"
195-
render={({ field }) => (
196-
<FormItem>
197-
<FormLabel>Encoded data</FormLabel>
198-
<FormControl>
199-
<Input placeholder="0xf0f0f0f0" type="text" {...field} />
200-
</FormControl>
201-
<FormDescription>Decode function, error or resource data</FormDescription>
202-
<FormMessage />
203-
</FormItem>
204-
)}
205-
/>
203+
<div className="flex items-center justify-between">
204+
<FormField
205+
control={form.control}
206+
name="encodedData"
207+
render={({ field }) => (
208+
<FormItem className="flex-1">
209+
<div className="flex w-full items-center justify-between">
210+
<FormLabel>Encoded data</FormLabel>
211+
<CopyButton
212+
value={window.location.href}
213+
disabled={!form.watch("encodedData")}
214+
className="ml-4 h-8 w-8"
215+
/>
216+
</div>
217+
218+
<FormControl>
219+
<Input placeholder="0xf0f0f0f0" type="text" {...field} />
220+
</FormControl>
221+
<FormDescription>Decode function, error or resource data</FormDescription>
222+
<FormMessage />
223+
</FormItem>
224+
)}
225+
/>
226+
</div>
206227

207228
{form.formState.isSubmitted && (
208229
<>

0 commit comments

Comments
 (0)