diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx index 40232ef2e9d..9d2ee68c3d4 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx @@ -109,7 +109,9 @@ export function CreateContractWebhookButton({ await handleSubmit(data); } catch (error) { toast.error( - `Failed to process webhook: ${error instanceof Error ? error.message : String(error)}`, + `Failed to process webhook: ${ + error instanceof Error ? error.message : String(error) + }`, ); } }; @@ -142,7 +144,9 @@ export function CreateContractWebhookButton({ router.refresh(); } catch (error) { toast.error( - `Failed to create webhook: ${error instanceof Error ? error.message : String(error)}`, + `Failed to create webhook: ${ + error instanceof Error ? error.message : String(error) + }`, ); } finally { setIsLoading(false); @@ -173,7 +177,11 @@ export function CreateContractWebhookButton({ if (filterType === "event") { result = await formHook.trigger(["chainIds", "addresses"]); } else { - result = await formHook.trigger(["chainIds", "toAddresses"]); + result = await formHook.trigger([ + "chainIds", + "toAddresses", + "fromAddresses", + ]); } if (result) { diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx index 10e5ad73289..ef164b83391 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx @@ -131,7 +131,9 @@ export function FilterDetailsStep({ render={({ field }) => (
- Contract Addresses + + Contract Addresses * +

Enter a contract address

@@ -196,7 +198,9 @@ export function FilterDetailsStep({ render={({ field }) => (
- From Address + + From Address * +

Enter a from address

diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/utils/webhookTypes.ts b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/utils/webhookTypes.ts index 79d8c4c3ff8..beac699f2a9 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/utils/webhookTypes.ts +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/utils/webhookTypes.ts @@ -24,75 +24,108 @@ const inputAbi = z.object({ type: z.string(), }); -export const webhookFormSchema = z.object({ - abi: z.string().optional(), - addresses: z - .string() - .optional() - .refine( - (val) => { - if (val === undefined || val.trim() === "") { - return true; - } - return val - .split(/[,\s]+/) - .filter(Boolean) - .every((a) => isAddress(a.trim())); - }, - { - message: "Enter valid addresses (comma-separated) or leave empty", - }, - ), - chainIds: z - .array(z.string()) - .min(1, { message: "Select at least one chain" }), - eventTypes: z.array(z.string()).optional(), - filterType: z.enum(["event", "transaction"]), - fromAddresses: z - .string() - .optional() - .refine( - (val) => { - if (val === undefined || val.trim() === "") { - return true; - } - return val - .split(/[,\s]+/) - .filter(Boolean) - .every((a) => isAddress(a.trim())); - }, - { - message: "Enter valid addresses (comma-separated) or leave empty", - }, - ), - inputAbi: z.array(inputAbi).optional(), - name: z - .string() - .min(3, { message: "Name must be at least 3 characters long" }) - .max(100, { message: "Name must be at most 100 characters long" }), - params: z.record(z.unknown()).optional(), - secret: z.string().optional(), - sigHash: z.string().optional(), - sigHashAbi: z.string().optional(), - toAddresses: z - .string() - .optional() - .refine( - (val) => { - if (val === undefined || val.trim() === "") { - return true; - } - return val - .split(/[,\s]+/) - .filter(Boolean) - .every((a) => isAddress(a.trim())); - }, - { - message: "Enter valid addresses (comma-separated) or leave empty", - }, - ), - webhookUrl: z.string().url({ message: "Must be a valid URL" }), -}); +export const webhookFormSchema = z + .object({ + abi: z.string().optional(), + addresses: z + .string() + .optional() + .refine( + (val: string | undefined) => { + if (val === undefined || val.trim() === "") { + return true; + } + return val + .split(/[,\s]+/) + .filter(Boolean) + .every((a: string) => isAddress(a.trim())); + }, + { + message: "Enter valid addresses (comma-separated)", + }, + ), + chainIds: z + .array(z.string()) + .min(1, { message: "Select at least one chain" }), + eventTypes: z.array(z.string()).optional(), + filterType: z.enum(["event", "transaction"]), + fromAddresses: z + .string() + .optional() + .refine( + (val: string | undefined) => { + if (val === undefined || val.trim() === "") { + return true; + } + return val + .split(/[,\s]+/) + .filter(Boolean) + .every((a: string) => isAddress(a.trim())); + }, + { + message: "Enter valid addresses (comma-separated)", + }, + ), + inputAbi: z.array(inputAbi).optional(), + name: z + .string() + .min(3, { message: "Name must be at least 3 characters long" }) + .max(100, { message: "Name must be at most 100 characters long" }), + params: z.record(z.unknown()).optional(), + secret: z.string().optional(), + sigHash: z.string().optional(), + sigHashAbi: z.string().optional(), + toAddresses: z + .string() + .optional() + .refine( + (val: string | undefined) => { + if (val === undefined || val.trim() === "") { + return true; + } + return val + .split(/[,\s]+/) + .filter(Boolean) + .every((a: string) => isAddress(a.trim())); + }, + { + message: "Enter valid addresses (comma-separated) or leave empty", + }, + ), + webhookUrl: z.string().url({ message: "Must be a valid URL" }), + }) + .refine( + (data: { + filterType: "event" | "transaction"; + addresses?: string; + fromAddresses?: string; + }) => { + if (data.filterType === "event") { + return data.addresses && data.addresses.trim() !== ""; + } + return true; + }, + { + message: "Contract address is required for event webhooks", + path: ["addresses"], + }, + ) + .refine( + (data: { + filterType: "event" | "transaction"; + addresses?: string; + fromAddresses?: string; + }) => { + if (data.filterType === "transaction") { + return data.fromAddresses && data.fromAddresses.trim() !== ""; + } + return true; + }, + { + message: "From address is required for transaction webhooks", + path: ["fromAddresses"], + }, + ); export type WebhookFormValues = z.infer;