Skip to content

Commit 2821b4e

Browse files
Make contract webhook addresses mandatory (#7544)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent adfa56b commit 2821b4e

File tree

3 files changed

+119
-74
lines changed

3 files changed

+119
-74
lines changed

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ export function CreateContractWebhookButton({
109109
await handleSubmit(data);
110110
} catch (error) {
111111
toast.error(
112-
`Failed to process webhook: ${error instanceof Error ? error.message : String(error)}`,
112+
`Failed to process webhook: ${
113+
error instanceof Error ? error.message : String(error)
114+
}`,
113115
);
114116
}
115117
};
@@ -142,7 +144,9 @@ export function CreateContractWebhookButton({
142144
router.refresh();
143145
} catch (error) {
144146
toast.error(
145-
`Failed to create webhook: ${error instanceof Error ? error.message : String(error)}`,
147+
`Failed to create webhook: ${
148+
error instanceof Error ? error.message : String(error)
149+
}`,
146150
);
147151
} finally {
148152
setIsLoading(false);
@@ -173,7 +177,11 @@ export function CreateContractWebhookButton({
173177
if (filterType === "event") {
174178
result = await formHook.trigger(["chainIds", "addresses"]);
175179
} else {
176-
result = await formHook.trigger(["chainIds", "toAddresses"]);
180+
result = await formHook.trigger([
181+
"chainIds",
182+
"toAddresses",
183+
"fromAddresses",
184+
]);
177185
}
178186

179187
if (result) {

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ export function FilterDetailsStep({
131131
render={({ field }) => (
132132
<FormItem className="flex flex-col">
133133
<div className="flex items-center justify-between text-xs">
134-
<FormLabel>Contract Addresses</FormLabel>
134+
<FormLabel>
135+
Contract Addresses <span className="text-red-500">*</span>
136+
</FormLabel>
135137
<p className="text-muted-foreground">
136138
Enter a contract address
137139
</p>
@@ -196,7 +198,9 @@ export function FilterDetailsStep({
196198
render={({ field }) => (
197199
<FormItem className="flex flex-col">
198200
<div className="flex items-center justify-between text-xs">
199-
<FormLabel>From Address</FormLabel>
201+
<FormLabel>
202+
From Address <span className="text-red-500">*</span>
203+
</FormLabel>
200204
<p className="text-muted-foreground">
201205
Enter a from address
202206
</p>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/utils/webhookTypes.ts

Lines changed: 102 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -24,75 +24,108 @@ const inputAbi = z.object({
2424
type: z.string(),
2525
});
2626

27-
export const webhookFormSchema = z.object({
28-
abi: z.string().optional(),
29-
addresses: z
30-
.string()
31-
.optional()
32-
.refine(
33-
(val) => {
34-
if (val === undefined || val.trim() === "") {
35-
return true;
36-
}
37-
return val
38-
.split(/[,\s]+/)
39-
.filter(Boolean)
40-
.every((a) => isAddress(a.trim()));
41-
},
42-
{
43-
message: "Enter valid addresses (comma-separated) or leave empty",
44-
},
45-
),
46-
chainIds: z
47-
.array(z.string())
48-
.min(1, { message: "Select at least one chain" }),
49-
eventTypes: z.array(z.string()).optional(),
50-
filterType: z.enum(["event", "transaction"]),
51-
fromAddresses: z
52-
.string()
53-
.optional()
54-
.refine(
55-
(val) => {
56-
if (val === undefined || val.trim() === "") {
57-
return true;
58-
}
59-
return val
60-
.split(/[,\s]+/)
61-
.filter(Boolean)
62-
.every((a) => isAddress(a.trim()));
63-
},
64-
{
65-
message: "Enter valid addresses (comma-separated) or leave empty",
66-
},
67-
),
68-
inputAbi: z.array(inputAbi).optional(),
69-
name: z
70-
.string()
71-
.min(3, { message: "Name must be at least 3 characters long" })
72-
.max(100, { message: "Name must be at most 100 characters long" }),
73-
params: z.record(z.unknown()).optional(),
74-
secret: z.string().optional(),
75-
sigHash: z.string().optional(),
76-
sigHashAbi: z.string().optional(),
77-
toAddresses: z
78-
.string()
79-
.optional()
80-
.refine(
81-
(val) => {
82-
if (val === undefined || val.trim() === "") {
83-
return true;
84-
}
85-
return val
86-
.split(/[,\s]+/)
87-
.filter(Boolean)
88-
.every((a) => isAddress(a.trim()));
89-
},
90-
{
91-
message: "Enter valid addresses (comma-separated) or leave empty",
92-
},
93-
),
94-
webhookUrl: z.string().url({ message: "Must be a valid URL" }),
95-
});
27+
export const webhookFormSchema = z
28+
.object({
29+
abi: z.string().optional(),
30+
addresses: z
31+
.string()
32+
.optional()
33+
.refine(
34+
(val: string | undefined) => {
35+
if (val === undefined || val.trim() === "") {
36+
return true;
37+
}
38+
return val
39+
.split(/[,\s]+/)
40+
.filter(Boolean)
41+
.every((a: string) => isAddress(a.trim()));
42+
},
43+
{
44+
message: "Enter valid addresses (comma-separated)",
45+
},
46+
),
47+
chainIds: z
48+
.array(z.string())
49+
.min(1, { message: "Select at least one chain" }),
50+
eventTypes: z.array(z.string()).optional(),
51+
filterType: z.enum(["event", "transaction"]),
52+
fromAddresses: z
53+
.string()
54+
.optional()
55+
.refine(
56+
(val: string | undefined) => {
57+
if (val === undefined || val.trim() === "") {
58+
return true;
59+
}
60+
return val
61+
.split(/[,\s]+/)
62+
.filter(Boolean)
63+
.every((a: string) => isAddress(a.trim()));
64+
},
65+
{
66+
message: "Enter valid addresses (comma-separated)",
67+
},
68+
),
69+
inputAbi: z.array(inputAbi).optional(),
70+
name: z
71+
.string()
72+
.min(3, { message: "Name must be at least 3 characters long" })
73+
.max(100, { message: "Name must be at most 100 characters long" }),
74+
params: z.record(z.unknown()).optional(),
75+
secret: z.string().optional(),
76+
sigHash: z.string().optional(),
77+
sigHashAbi: z.string().optional(),
78+
toAddresses: z
79+
.string()
80+
.optional()
81+
.refine(
82+
(val: string | undefined) => {
83+
if (val === undefined || val.trim() === "") {
84+
return true;
85+
}
86+
return val
87+
.split(/[,\s]+/)
88+
.filter(Boolean)
89+
.every((a: string) => isAddress(a.trim()));
90+
},
91+
{
92+
message: "Enter valid addresses (comma-separated) or leave empty",
93+
},
94+
),
95+
webhookUrl: z.string().url({ message: "Must be a valid URL" }),
96+
})
97+
.refine(
98+
(data: {
99+
filterType: "event" | "transaction";
100+
addresses?: string;
101+
fromAddresses?: string;
102+
}) => {
103+
if (data.filterType === "event") {
104+
return data.addresses && data.addresses.trim() !== "";
105+
}
106+
return true;
107+
},
108+
{
109+
message: "Contract address is required for event webhooks",
110+
path: ["addresses"],
111+
},
112+
)
113+
.refine(
114+
(data: {
115+
filterType: "event" | "transaction";
116+
addresses?: string;
117+
fromAddresses?: string;
118+
}) => {
119+
if (data.filterType === "transaction") {
120+
return data.fromAddresses && data.fromAddresses.trim() !== "";
121+
}
122+
return true;
123+
},
124+
{
125+
message: "From address is required for transaction webhooks",
126+
path: ["fromAddresses"],
127+
},
128+
);
96129

97130
export type WebhookFormValues = z.infer<typeof webhookFormSchema>;
98131

0 commit comments

Comments
 (0)