Repository: thirdweb-dev/js
Pull request #7209: Add support for chain filtering in webhook creation
Author: login: AmineAfia
Labels: name: Dashboard
description: Involves changes to the Dashboard.
Created: 2025-05-29T22:40:11Z | Merged: 2025-05-29T23:42:28Z

## [Dashboard] Feature: Add Chain Filtering for Webhooks

## Notes for the reviewer
This PR adds support for filtering available chains in the webhook creation flow. It fetches supported chains from the Insight API and restricts the chain selection in the UI to only those chains that are supported for webhooks.

## How to test
1. Navigate to the webhooks page for a project
2. Verify that the chain selector in the webhook creation modal only shows supported chains
3. Confirm that the API call to fetch supported chains works correctly

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added support for dynamically fetching and displaying supported blockchain networks when creating or managing webhooks.
  - The network selector now only shows networks that are currently supported for webhooks.
  - Users are notified if no supported blockchain networks are available for webhooks.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on enhancing the handling of supported chain IDs across multiple components in the dashboard, allowing for better filtering and management of webhooks based on the selected blockchain networks.

### Detailed summary
- Added `chainIds` prop to `NetworkSelectors` and filtered `allChains` based on it.
- Introduced `supportedChainIds` prop in `CreateWebhookModal` and `WebhooksTable`.
- Updated `CreateWebhookModal` and `WebhooksTable` to use `supportedChainIds`.
- Implemented `getSupportedWebhookChains` function to fetch supported chains.
- Updated `WebhooksPage` to retrieve and handle supported chain IDs.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->


Diff:
diff --git a/apps/dashboard/src/@/api/insight/webhooks.ts b/apps/dashboard/src/@/api/insight/webhooks.ts
index 8ac3733f84c..1b14d0a790e 100644
--- a/apps/dashboard/src/@/api/insight/webhooks.ts
+++ b/apps/dashboard/src/@/api/insight/webhooks.ts
@@ -67,6 +67,10 @@ interface TestWebhookResponse {
   error?: string;
 }
 
+type SupportedWebhookChainsResponse =
+  | { chains: Array<number> }
+  | { error: string };
+
 export async function createWebhook(
   payload: CreateWebhookPayload,
   clientId: string,
@@ -205,3 +209,31 @@ export async function testWebhook(
     };
   }
 }
+
+export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
+  try {
+    const response = await fetch(
+      `https://${THIRDWEB_INSIGHT_API_DOMAIN}/service/chains`,
+      {
+        method: "GET",
+        headers: {
+          "x-service-api-key": process.env.INSIGHT_SERVICE_API_KEY || "",
+        },
+      },
+    );
+
+    if (!response.ok) {
+      const errorText = await response.json();
+      return { error: `Failed to fetch supported chains: ${errorText.error}` };
+    }
+
+    const data = await response.json();
+    if (Array.isArray(data.data)) {
+      return { chains: data.data };
+    }
+    return { error: "Unexpected response format" };
+  } catch (error) {
+    console.error("Error fetching supported chains:", error);
+    return { error: `Failed to fetch supported chains: ${error}` };
+  }
+}
diff --git a/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx b/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
index a16b0b40548..9f0dde55afa 100644
--- a/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
+++ b/apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
@@ -27,8 +27,15 @@ export function MultiNetworkSelector(props: {
   side?: "left" | "right" | "top" | "bottom";
   showSelectedValuesInModal?: boolean;
   client: ThirdwebClient;
+  chainIds?: number[];
 }) {
-  const { allChains, idToChain } = useAllChainsData();
+  let { allChains, idToChain } = useAllChainsData();
+
+  if (props.chainIds && props.chainIds.length > 0) {
+    allChains = allChains.filter((chain) =>
+      props.chainIds?.includes(chain.chainId),
+    );
+  }
 
   const options = useMemo(() => {
     let sortedChains = allChains;
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 e9e33451966..35cdaf0387f 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
@@ -41,9 +41,13 @@ import {
 
 interface CreateWebhookModalProps {
   clientId: string;
+  supportedChainIds: Array<number>;
 }
 
-export function CreateWebhookModal({ clientId }: CreateWebhookModalProps) {
+export function CreateWebhookModal({
+  clientId,
+  supportedChainIds,
+}: CreateWebhookModalProps) {
   const router = useDashboardRouter();
   const thirdwebClient = useThirdwebClient();
   const [isOpen, setIsOpen] = useState(false);
@@ -270,6 +274,7 @@ export function CreateWebhookModal({ clientId }: CreateWebhookModalProps) {
                   goToNextStep={goToNextStep}
                   goToPreviousStep={goToPreviousStep}
                   isLoading={isLoading}
+                  supportedChainIds={supportedChainIds}
                 />
               )}
 
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 1a1fbf01038..df8140ed8a8 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
@@ -1,6 +1,5 @@
 "use client";
 
-import { MultiNetworkSelector } from "@/components/blocks/NetworkSelectors";
 import { Spinner } from "@/components/ui/Spinner/Spinner";
 import { Badge } from "@/components/ui/badge";
 import { Button } from "@/components/ui/button";
@@ -24,6 +23,7 @@ import { useThirdwebClient } from "@/constants/thirdweb.client";
 import { useQueryClient } from "@tanstack/react-query";
 import type { UseFormReturn } from "react-hook-form";
 
+import { MultiNetworkSelector } from "@/components/blocks/NetworkSelectors";
 import { truncateMiddle } from "../utils/abiUtils";
 import type {
   AbiData,
@@ -45,6 +45,7 @@ interface FilterDetailsStepProps {
   goToNextStep: () => void;
   goToPreviousStep: () => void;
   isLoading: boolean;
+  supportedChainIds: Array<number>;
 }
 
 export function FilterDetailsStep({
@@ -60,6 +61,7 @@ export function FilterDetailsStep({
   goToNextStep,
   goToPreviousStep,
   isLoading,
+  supportedChainIds,
 }: FilterDetailsStepProps) {
   const thirdwebClient = useThirdwebClient();
   const queryClient = useQueryClient();
@@ -106,6 +108,7 @@ export function FilterDetailsStep({
                   }
                   onChange={(chainIds) => field.onChange(chainIds.map(String))}
                   client={thirdwebClient}
+                  chainIds={supportedChainIds}
                 />
               </FormControl>
               <FormMessage />
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx
index 93f133227a4..b7a89b3f20b 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx
@@ -32,9 +32,14 @@ function getEventType(filters: WebhookFilters): string {
 interface WebhooksTableProps {
   webhooks: WebhookResponse[];
   clientId: string;
+  supportedChainIds: number[];
 }
 
-export function WebhooksTable({ webhooks, clientId }: WebhooksTableProps) {
+export function WebhooksTable({
+  webhooks,
+  clientId,
+  supportedChainIds,
+}: WebhooksTableProps) {
   const [isDeleting, setIsDeleting] = useState<Record<string, boolean>>({});
   const { testWebhookEndpoint, isTestingMap } = useTestWebhook(clientId);
   const router = useDashboardRouter();
@@ -210,7 +215,10 @@ export function WebhooksTable({ webhooks, clientId }: WebhooksTableProps) {
   return (
     <div className="w-full">
       <div className="mb-4 flex items-center justify-end">
-        <CreateWebhookModal clientId={clientId} />
+        <CreateWebhookModal
+          clientId={clientId}
+          supportedChainIds={supportedChainIds}
+        />
       </div>
       <TWTable
         data={sortedWebhooks}
diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
index d4214321766..b411631b3d6 100644
--- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
+++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
@@ -1,4 +1,8 @@
-import { type WebhookResponse, getWebhooks } from "@/api/insight/webhooks";
+import {
+  type WebhookResponse,
+  getSupportedWebhookChains,
+  getWebhooks,
+} from "@/api/insight/webhooks";
 import { getProject } from "@/api/projects";
 import { TrackedUnderlineLink } from "@/components/ui/tracked-link";
 import { notFound } from "next/navigation";
@@ -11,6 +15,7 @@ export default async function WebhooksPage({
   let webhooks: WebhookResponse[] = [];
   let clientId = "";
   let errorMessage = "";
+  let supportedChainIds: number[] = [];
 
   try {
     // Await params before accessing properties
@@ -32,6 +37,13 @@ export default async function WebhooksPage({
     } else if (webhooksRes.data) {
       webhooks = webhooksRes.data;
     }
+
+    const supportedChainsRes = await getSupportedWebhookChains();
+    if ("chains" in supportedChainsRes) {
+      supportedChainIds = supportedChainsRes.chains;
+    } else {
+      errorMessage = supportedChainsRes.error;
+    }
   } catch (error) {
     errorMessage = "Failed to load webhooks. Please try again later.";
     console.error("Error loading project or webhooks", error);
@@ -71,7 +83,11 @@ export default async function WebhooksPage({
             </div>
           </div>
         ) : webhooks.length > 0 ? (
-          <WebhooksTable webhooks={webhooks} clientId={clientId} />
+          <WebhooksTable
+            webhooks={webhooks}
+            clientId={clientId}
+            supportedChainIds={supportedChainIds}
+          />
         ) : (
           <div className="flex flex-col items-center justify-center gap-4 rounded-lg border border-border p-12 text-center">
             <div>
@@ -80,7 +96,10 @@ export default async function WebhooksPage({
                 Create a webhook to get started.
               </p>
             </div>
-            <CreateWebhookModal clientId={clientId} />
+            <CreateWebhookModal
+              clientId={clientId}
+              supportedChainIds={supportedChainIds}
+            />
           </div>
         )}
       </div>


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx (1)</summary><blockquote>

`44-44`: **LGTM!**

The new `supportedChainIds` prop is correctly typed. Note: Consider using `number[]` for consistency with other files, though `Array<number>` is functionally equivalent.

</blockquote></details>
<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx (1)</summary><blockquote>

`48-48`: **Consider type consistency across the codebase.**

The interface uses `Array<number>` while the page component uses `number[]`. Both are functionally equivalent, but consistency would improve maintainability.


Consider using consistent type notation across files:

```diff
-supportedChainIds: Array<number>;
+supportedChainIds: number[];
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 1f42ad2a2dd58640485bc7cca1bc01a045a0d744 and 49943b43c20eab5d160d08a96c995b2f2cdf7f67.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `apps/dashboard/src/@/api/insight/webhooks.ts` (2 hunks)
* `apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx` (1 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx` (2 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx` (4 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx` (2 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx` (5 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Graph Analysis (3)</summary>

<details>
<summary>apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx (1)</summary><blockquote>

<details>
<summary>apps/dashboard/src/hooks/chains/allChains.ts (1)</summary>

* `useAllChainsData` (125-148)

</details>

</blockquote></details>
<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx (1)</summary><blockquote>

<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx (1)</summary>

* `CreateWebhookModal` (47-299)

</details>

</blockquote></details>
<details>
<summary>apps/dashboard/src/@/api/insight/webhooks.ts (1)</summary><blockquote>

<details>
<summary>apps/dashboard/src/constants/urls.ts (1)</summary>

* `THIRDWEB_INSIGHT_API_DOMAIN` (24-25)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (7)</summary>

* GitHub Check: Size
* GitHub Check: E2E Tests (pnpm, esbuild)
* GitHub Check: Build Packages
* GitHub Check: E2E Tests (pnpm, webpack)
* GitHub Check: E2E Tests (pnpm, vite)
* GitHub Check: Lint Packages
* GitHub Check: Analyze (javascript)

</details>

<details>
<summary>🔇 Additional comments (15)</summary><blockquote>

<details>
<summary>apps/dashboard/src/@/api/insight/webhooks.ts (1)</summary>

`70-72`: **LGTM!**

The type definition correctly models the possible return shapes from the API function.

</details>
<details>
<summary>apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx (2)</summary>

`30-30`: **LGTM!**

The optional `chainIds` prop addition maintains backward compatibility while enabling chain filtering functionality.

---

`34-38`: **LGTM!**

The filtering logic is correctly implemented:
- Only filters when `chainIds` is provided and non-empty
- Uses appropriate array methods
- Positioned correctly before other chain processing logic

</details>
<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx (3)</summary>

`35-35`: **LGTM!**

The new `supportedChainIds` prop is correctly typed and added to the interface.

---

`38-42`: **LGTM!**

The component signature correctly includes the new prop in the destructuring.

---

`218-221`: **LGTM!**

The `supportedChainIds` prop is correctly passed down to the `CreateWebhookModal` component.

</details>
<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx (2)</summary>

`47-50`: **LGTM!**

The component signature correctly includes the new prop in the destructuring.

---

`277-277`: **LGTM!**

The `supportedChainIds` prop is correctly passed down to the `FilterDetailsStep` component.

</details>
<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx (4)</summary>

`1-5`: **LGTM! Import statement correctly updated.**

The import statement properly includes the new `getSupportedWebhookChains` function alongside existing imports.

---

`18-18`: **LGTM! Variable initialization is appropriate.**

The `supportedChainIds` array is properly initialized as an empty array, maintaining type consistency with the expected `number[]` type.

---

`83-93`: **LGTM! Error state handling for unsupported chains is well implemented.**

The conditional rendering properly handles the scenario when no supported chains are available, providing clear user feedback with appropriate styling and messaging.

---

`95-99`: **LGTM! Props are correctly passed to child components.**

The `supportedChainIds` prop is properly passed to both `WebhooksTable` and `CreateWebhookModal` components, maintaining the data flow through the component hierarchy.



Also applies to: 108-111

</details>
<details>
<summary>apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx (3)</summary>

`26-26`: **LGTM! Import reordering has no functional impact.**

The import statement was reordered without changing functionality, which is acceptable.

---

`64-64`: **LGTM! Parameter correctly added to component signature.**

The `supportedChainIds` parameter is properly added to match the updated interface.

---

`111-111`: <details>
<summary>✅ Verification successful</summary>

**Verify that MultiNetworkSelector supports the chainIds prop.**

The code passes `chainIds={supportedChainIds}` to `MultiNetworkSelector`, but we should verify this prop is properly implemented in the component.

---

🏁 Script executed:

```shell
#!/bin/bash
# Description: Verify that MultiNetworkSelector component accepts chainIds prop
# Expected: Find the component implementation and check if chainIds prop is defined

# Search for MultiNetworkSelector component definition
ast-grep --pattern $'export function MultiNetworkSelector({
  $$$
}) {
  $$$
}'

# Also search for interface/type definitions
rg -A 10 "interface.*MultiNetworkSelector" 
rg -A 10 "type.*MultiNetworkSelector"
```

Length of output: 165

---

🏁 Script executed:

```shell
#!/bin/bash
# Locate where MultiNetworkSelector is imported to determine its source
rg -n "import.*MultiNetworkSelector" -g '*.tsx'

# Find all files referencing MultiNetworkSelector
rg -l "MultiNetworkSelector" -g '*.tsx'
```

Length of output: 1679

---

🏁 Script executed:

```shell
#!/bin/bash
# Inspect implementation of MultiNetworkSelector in dashboard components
echo "----- apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx (first 200 lines) -----"
sed -n '1,200p' apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx || true

echo
echo "----- Searching for chainIds prop in dashboard component -----"
rg -n "chainIds" -C 3 apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx || true

echo
echo "----- Searching for MultiNetworkSelector declaration/export -----"
rg -n "MultiNetworkSelector" -C 5 apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx || true
```

Length of output: 8486

---



</details>

**chainIds prop support confirmed in MultiNetworkSelector**

The `MultiNetworkSelector` component in  
`apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx`  
defines `chainIds?: number[]` in its props and applies it to filter `allChains` (lines 30–38). The passed `supportedChainIds` will correctly limit the selectable networks.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T22:44:21Z
user: login: AmineAfia
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T22:48:00Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T22:48:37Z
user: login: AmineAfia
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T23:03:33Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T23:04:39Z
user: login: MananTank
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-29T23:27:15Z

Review comments:
diff_hunk: @@ -205,3 +209,26 @@ export async function testWebhook(
     };
   }
 }
+
+export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
+  try {
+    const response = await fetch(
+      `https://${THIRDWEB_INSIGHT_API_DOMAIN}/service/chains`,
+      {
+        method: "GET",
+        headers: {
+          "x-service-api-key": process.env.INSIGHT_SERVICE_API_KEY || "",
+        },
+      },
+    );
+
+    const data = await response.json();
+    if (Array.isArray(data.data)) {
+      return { chains: data.data };
+    }
+    return { error: "Unexpected response format" };
+  } catch (error) {
+    console.error("Error fetching chains:", error);
+    return { error: `Failed to fetch chains: ${error}` };
+  }
+}
path: apps/dashboard/src/@/api/insight/webhooks.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Add response validation and improve error handling.**

The function has several issues that need to be addressed:

1. Missing HTTP response status check before parsing JSON
2. Environment variable could be undefined, resulting in an empty API key
3. Response structure assumes `data.data` without validation



Apply this diff to improve the implementation:

```diff
 export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
   try {
+    const apiKey = process.env.INSIGHT_SERVICE_API_KEY;
+    if (!apiKey) {
+      return { error: "Missing INSIGHT_SERVICE_API_KEY environment variable" };
+    }
+
     const response = await fetch(
       `https://${THIRDWEB_INSIGHT_API_DOMAIN}/service/chains`,
       {
         method: "GET",
         headers: {
-          "x-service-api-key": process.env.INSIGHT_SERVICE_API_KEY || "",
+          "x-service-api-key": apiKey,
         },
       },
     );

+    if (!response.ok) {
+      return { error: `HTTP ${response.status}: ${response.statusText}` };
+    }
+
     const data = await response.json();
-    if (Array.isArray(data.data)) {
+    if (data && typeof data === 'object' && Array.isArray(data.data)) {
       return { chains: data.data };
     }
     return { error: "Unexpected response format" };
   } catch (error) {
     console.error("Error fetching chains:", error);
     return { error: `Failed to fetch chains: ${error}` };
   }
 }
```

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In apps/dashboard/src/@/api/insight/webhooks.ts around lines 213 to 234, improve
the getSupportedWebhookChains function by first checking the HTTP response
status before parsing JSON to handle non-200 responses properly. Validate that
the environment variable INSIGHT_SERVICE_API_KEY is defined and throw or handle
an error if it is missing instead of using an empty string. Also, add validation
to ensure the response JSON has the expected structure with a data property that
is an array before accessing data.data. Adjust error handling to reflect these
checks and provide clearer error messages.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-29T22:44:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,6 +37,11 @@ export default async function WebhooksPage({
     } else if (webhooksRes.data) {
       webhooks = webhooksRes.data;
     }
+
+    const supportedChainsRes = await getSupportedWebhookChains();
+    if ("chains" in supportedChainsRes) {
+      supportedChainIds = supportedChainsRes.chains;
+    }
path: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Improve error handling consistency for supported chains API call.**

The supported chains API call lacks the same error handling pattern as the webhooks API call. Consider adding consistent error handling.


Apply this diff to improve error handling consistency:

```diff
 const supportedChainsRes = await getSupportedWebhookChains();
 if ("chains" in supportedChainsRes) {
   supportedChainIds = supportedChainsRes.chains;
+} else if (supportedChainsRes.error) {
+  console.error("Error fetching supported chains:", supportedChainsRes.error);
 }
```

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
around lines 41 to 44, the supported chains API call does not have consistent
error handling like the webhooks API call. Modify the code to check for error
conditions explicitly and handle them similarly to the webhooks call, such as
verifying the response structure and managing error cases gracefully, ensuring
any errors are caught and handled or logged appropriately.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-29T22:44:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,6 +37,11 @@ export default async function WebhooksPage({
     } else if (webhooksRes.data) {
       webhooks = webhooksRes.data;
     }
+
+    const supportedChainsRes = await getSupportedWebhookChains();
+    if ("chains" in supportedChainsRes) {
+      supportedChainIds = supportedChainsRes.chains;
+    }
path: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
user: login: AmineAfia
body: The error state is handled differently here. see the jsx snippet bellow. Im showing an error if I the supportedChainIds is empty
created_at: 2025-05-29T22:48:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,6 +37,11 @@ export default async function WebhooksPage({
     } else if (webhooksRes.data) {
       webhooks = webhooksRes.data;
     }
+
+    const supportedChainsRes = await getSupportedWebhookChains();
+    if ("chains" in supportedChainsRes) {
+      supportedChainIds = supportedChainsRes.chains;
+    }
path: apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
user: login: coderabbitai[bot]
body: @AmineAfia Thank you for the clarification! I see now that you're handling the error state at the UI level by checking `supportedChainIds.length === 0` in the JSX conditional rendering. This approach makes sense for your use case - treating an empty supported chains array as an error condition and displaying the appropriate error UI to the user.

Your strategy of UI-level error handling is a valid alternative to explicit API error handling, and it provides a good user experience by clearly communicating when no supported chains are available. I understand the different approach you've taken here.


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@AmineAfia Thank you for the clarification! I see now that you're handling the error state at the UI level by checking `supportedChainIds.length === 0` in the JSX conditional rendering. This approach makes sense for your use case - treating an empty supported chains array as an error condition and displaying the appropriate error UI to the user.

Your strategy of UI-level error handling is a valid alternative to explicit API error handling, and it provides a good user experience by clearly communicating when no supported chains are available. I understand the different approach you've taken here.</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-29T22:48:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -205,3 +209,26 @@ export async function testWebhook(
     };
   }
 }
+
+export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
+  try {
+    const response = await fetch(
+      `https://${THIRDWEB_INSIGHT_API_DOMAIN}/service/chains`,
+      {
+        method: "GET",
+        headers: {
+          "x-service-api-key": process.env.INSIGHT_SERVICE_API_KEY || "",
+        },
+      },
+    );
+
+    const data = await response.json();
+    if (Array.isArray(data.data)) {
+      return { chains: data.data };
+    }
+    return { error: "Unexpected response format" };
+  } catch (error) {
+    console.error("Error fetching chains:", error);
+    return { error: `Failed to fetch chains: ${error}` };
+  }
+}
path: apps/dashboard/src/@/api/insight/webhooks.ts
user: login: AmineAfia
body: done
created_at: 2025-05-29T23:03:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -205,3 +209,26 @@ export async function testWebhook(
     };
   }
 }
+
+export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
+  try {
+    const response = await fetch(
+      `https://${THIRDWEB_INSIGHT_API_DOMAIN}/service/chains`,
+      {
+        method: "GET",
+        headers: {
+          "x-service-api-key": process.env.INSIGHT_SERVICE_API_KEY || "",
+        },
+      },
+    );
+
+    const data = await response.json();
+    if (Array.isArray(data.data)) {
+      return { chains: data.data };
+    }
+    return { error: "Unexpected response format" };
+  } catch (error) {
+    console.error("Error fetching chains:", error);
+    return { error: `Failed to fetch chains: ${error}` };
+  }
+}
path: apps/dashboard/src/@/api/insight/webhooks.ts
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# Check the current implementation of getSupportedWebhookChains function
ast-grep --pattern 'export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
  $$$
}'
```

Length of output: 2280

---



</details>

@AmineAfia Good work on adding the HTTP response status check! I can see that improvement was implemented. However, there are still a few important items from my original suggestions that need attention:

1. **Environment variable validation** - The function still uses `process.env.INSIGHT_SERVICE_API_KEY || ""` which could result in an empty API key being sent
2. **Enhanced response structure validation** - The current check `Array.isArray(data.data)` doesn't validate that `data` is an object first
3. **Environment variable check at function start** - Missing the early return if the API key is undefined

Here's what still needs to be addressed:

```typescript
export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
  try {
    // Add this validation at the start
    const apiKey = process.env.INSIGHT_SERVICE_API_KEY;
    if (!apiKey) {
      return { error: "Missing INSIGHT_SERVICE_API_KEY environment variable" };
    }

    const response = await fetch(
      `https://${THIRDWEB_INSIGHT_API_DOMAIN}/service/chains`,
      {
        method: "GET",
        headers: {
          "x-service-api-key": apiKey, // Use the validated apiKey
        },
      },
    );

    if (!response.ok) {
      const errorText = await response.json();
      return { error: `Failed to fetch supported chains: ${errorText.error}` };
    }

    const data = await response.json();
    // Improve this validation
    if (data && typeof data === 'object' && Array.isArray(data.data)) {
      return { chains: data.data };
    }
    return { error: "Unexpected response format" };
  } catch (error) {
    console.error("Error fetching supported chains:", error);
    return { error: `Failed to fetch supported chains: ${error}` };
  }
}
```

These remaining changes will make the error handling more robust and prevent potential runtime issues.

---

<details>
<summary>🧬 Code Graph Analysis Results</summary>

### Relevant snippet from `apps/dashboard/src/constants/urls.ts` (lines 24-25)
```typescript
export const THIRDWEB_INSIGHT_API_DOMAIN =
  process.env.NEXT_PUBLIC_INSIGHT_API_URL || "insight.thirdweb-dev.com";
```
- Defines the domain used for the Insight API.
- Uses an environment variable `NEXT_PUBLIC_INSIGHT_API_URL` if set; otherwise defaults to `"insight.thirdweb-dev.com"`.

---

### Relevant snippet from `apps/dashboard/src/app/(app)/api/lib/getAuthToken.ts` (lines 6-14)
```typescript
export async function getAuthToken() {
  const cookiesManager = await cookies();
  const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
  const token = activeAccount
    ? cookiesManager.get(COOKIE_PREFIX_TOKEN + activeAccount)?.value
    : null;

  return token;
}
```
- Asynchronously retrieves an authentication token from cookies.
- Uses `COOKIE_ACTIVE_ACCOUNT` to find the active account.
- Retrieves the token associated with the active account using a prefixed cookie key.
- Returns the token string or `null` if not found.

---

These snippets provide context for the constants and authentication token retrieval used in the `webhooks.ts` API functions.

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer><details>
<summary>🧩 Analysis chain</summary>

🏁 Script executed:
```
#!/bin/bash
# Check the current implementation of getSupportedWebhookChains function
ast-grep --pattern 'export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
  $$$
}'
```

Length of output: 2280

---



</details>

@AmineAfia Good work on adding the HTTP response status check! I can see that improvement was implemented. However, there are still a few important items from my original suggestions that need attention:

1. **Environment variable validation** - The function still uses `process.env.INSIGHT_SERVICE_API_KEY || ""` which could result in an empty API key being sent
2. **Enhanced response structure validation** - The current check `Array.isArray(data.data)` doesn't validate that `data` is an object first
3. **Environment variable check at function start** - Missing the early return if the API key is undefined

Here's what still needs to be addressed:

```typescript
export async function getSupportedWebhookChains(): Promise<SupportedWebhookChainsResponse> {
  try {
    // Add this validation at the start
    const apiKey = process.env.INSIGHT_SERVICE_API_KEY;
    if (!apiKey) {
      return { error: "Missing INSIGHT_SERVICE_API_KEY environment variable" };
    }

    const response = await fetch(
      `https://${THIRDWEB_INSIGHT_API_DOMAIN}/service/chains`,
      {
        method: "GET",
        headers: {
          "x-service-api-key": apiKey, // Use the validated apiKey
        },
      },
    );

    if (!response.ok) {
      const errorText = await response.json();
      return { error: `Failed to fetch supported chains: ${errorText.error}` };
    }

    const data = await response.json();
    // Improve this validation
    if (data && typeof data === 'object' && Array.isArray(data.data)) {
      return { chains: data.data };
    }
    return { error: "Unexpected response format" };
  } catch (error) {
    console.error("Error fetching supported chains:", error);
    return { error: `Failed to fetch supported chains: ${error}` };
  }
}
```

These remaining changes will make the error handling more robust and prevent potential runtime issues.

---

<details>
<summary>🧬 Code Graph Analysis Results</summary>

### Relevant snippet from `apps/dashboard/src/constants/urls.ts` (lines 24-25)
```typescript
export const THIRDWEB_INSIGHT_API_DOMAIN =
  process.env.NEXT_PUBLIC_INSIGHT_API_URL || "insight.thirdweb-dev.com";
```
- Defines the domain used for the Insight API.
- Uses an environment variable `NEXT_PUBLIC_INSIGHT_API_URL` if set; otherwise defaults to `"insight.thirdweb-dev.com"`.

---

### Relevant snippet from `apps/dashboard/src/app/(app)/api/lib/getAuthToken.ts` (lines 6-14)
```typescript
export async function getAuthToken() {
  const cookiesManager = await cookies();
  const activeAccount = cookiesManager.get(COOKIE_ACTIVE_ACCOUNT)?.value;
  const token = activeAccount
    ? cookiesManager.get(COOKIE_PREFIX_TOKEN + activeAccount)?.value
    : null;

  return token;
}
```
- Asynchronously retrieves an authentication token from cookies.
- Uses `COOKIE_ACTIVE_ACCOUNT` to find the active account.
- Retrieves the token associated with the active account using a prefixed cookie key.
- Returns the token string or `null` if not found.

---

These snippets provide context for the constants and authentication token retrieval used in the `webhooks.ts` API functions.

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-29T23:04:39Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-05-29T22:40:13Z
body: [vc]: #DG1Cg53GFY4Ay1YpiJRsyWfiF0O2/kRdSMYtroemT3A=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJsb2dpbiIsInJvb3REaXJlY3RvcnkiOiJhcHBzL2xvZ2luIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdGhpcmR3ZWIvbG9naW4vcFZvZzVZTVUyVVMzUXI3ZGZGMjRoaHhtRTh1dyIsInByZXZpZXdVcmwiOiIiLCJuZXh0Q29tbWl0U3RhdHVzIjoiU0tJUFBFRCJ9LHsibmFtZSI6IndhbGxldC11aSIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3dhbGxldC11aSIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3RoaXJkd2ViL3dhbGxldC11aS84bXFRYVdQZUNCY05NMVkyWExhOXZ6dmduZVYyIiwicHJldmlld1VybCI6IiIsIm5leHRDb21taXRTdGF0dXMiOiJTS0lQUEVEIn0seyJuYW1lIjoidGhpcmR3ZWJfcGxheWdyb3VuZCIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3BsYXlncm91bmQtd2ViIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdGhpcmR3ZWIvdGhpcmR3ZWJfcGxheWdyb3VuZC9ER0NUUDZLUnJ3bmFVRkxIUXZaU1oxMXBXeHVjIiwicHJldmlld1VybCI6IiIsIm5leHRDb21taXRTdGF0dXMiOiJTS0lQUEVEIn0seyJuYW1lIjoidGhpcmR3ZWItd3d3Iiwicm9vdERpcmVjdG9yeSI6ImFwcHMvZGFzaGJvYXJkIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3RoaXJkd2ViL3RoaXJkd2ViLXd3dy9HWTh3cXZyR0ozZ3BZanBCZDNGUWlXNnFFMmp2IiwicHJldmlld1VybCI6InRoaXJkd2ViLXd3dy1naXQtYWRkc3VwcG9ydGZvcmNoYWluZmlsdGVyaW5naW53ZWJob29rY3JlYXRpb24udGhpcmR3ZWItcHJldmlldy5jb20iLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoidGhpcmR3ZWItd3d3LWdpdC1hZGRzdXBwb3J0Zm9yY2hhaW5maWx0ZXJpbmdpbndlYmhvb2tjcmVhdGlvbi50aGlyZHdlYi1wcmV2aWV3LmNvbSJ9fSx7Im5hbWUiOiJkb2NzLXYyIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvcG9ydGFsIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdGhpcmR3ZWIvZG9jcy12Mi9FRmNpeVZRMjNxN1JYdHBiVkhXd3RwSDhnTEtBIiwicHJldmlld1VybCI6IiIsIm5leHRDb21taXRTdGF0dXMiOiJTS0lQUEVEIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **thirdweb-www** | ✅ Ready ([Inspect](https://vercel.com/thirdweb/thirdweb-www/GY8wqvrGJ3gpYjpBd3FQiW6qE2jv)) | [Visit Preview](https://thirdweb-www-git-addsupportforchainfilteringinwebhookcreation.thirdweb-preview.com) | 💬 [**Add feedback**](https://vercel.live/open-feedback/thirdweb-www-git-addsupportforchainfilteringinwebhookcreation.thirdweb-preview.com?via=pr-comment-feedback-link) | May 29, 2025 11:41pm |

<details><summary>4 Skipped Deployments</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **docs-v2** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/docs-v2/EFciyVQ23q7RXtpbVHWwtpH8gLKA)) |  |  | May 29, 2025 11:41pm |
| **login** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/login/pVog5YMU2US3Qr7dfF24hhxmE8uw)) |  |  | May 29, 2025 11:41pm |
| **thirdweb_playground** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/thirdweb_playground/DGCTP6KRrwnaUFLHQvZSZ11pWxuc)) |  |  | May 29, 2025 11:41pm |
| **wallet-ui** | ⬜️ Skipped ([Inspect](https://vercel.com/thirdweb/wallet-ui/8mqQaWPeCBcNM1Y2XLa9vzvgneV2)) |  |  | May 29, 2025 11:41pm |
</details>






author_association: NONE
user: login: changeset-bot[bot]
created_at: 2025-05-29T22:40:14Z
body: ###  ⚠️  No Changeset found

Latest commit: 9611a17fc773091ae623cd36b674d6c0d7bfa0ca

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/thirdweb-dev/js/new/Add_support_for_chain_filtering_in_webhook_creation?filename=.changeset/four-readers-fetch.md&value=---%0A%22thirdweb-dashboard%22%3A%20patch%0A---%0A%0AAdd%20support%20for%20chain%20filtering%20in%20webhook%20creation%0A)


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-29T22:40:17Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

"""
## Walkthrough

Support for dynamically fetching and propagating supported blockchain chain IDs for webhooks was implemented. A new API function retrieves supported chains, and this data is passed through the webhooks page, table, modal, and selector components. Components now restrict selectable networks based on the fetched supported chain IDs and handle cases where none are available.

## Changes

| Files/Paths                                                                                              | Change Summary                                                                                                   |
|----------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| .../api/insight/webhooks.ts                                                                              | Added type alias for supported chains response and a new async function to fetch supported webhook chains.       |
| .../components/blocks/NetworkSelectors.tsx                                                               | Added optional `chainIds` prop to `MultiNetworkSelector` and filtered chains accordingly.                        |
| .../webhooks/components/CreateWebhookModal.tsx                                                           | Added `supportedChainIds` prop to props interface and passed it to `FilterDetailsStep`.                         |
| .../webhooks/components/FilterDetailsStep.tsx                                                            | Added `supportedChainIds` prop to props and passed it to `MultiNetworkSelector` as `chainIds`.                  |
| .../webhooks/components/WebhooksTable.tsx                                                                | Added `supportedChainIds` prop to props and passed it to `CreateWebhookModal`.                                  |
| .../webhooks/page.tsx                                                                                    | Fetched supported chain IDs, handled empty state, and passed them to child components.                          |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant WebhooksPage
    participant API
    participant WebhooksTable
    participant CreateWebhookModal
    participant FilterDetailsStep
    participant MultiNetworkSelector

    User->>WebhooksPage: Load page
    WebhooksPage->>API: getSupportedWebhookChains()
    API-->>WebhooksPage: { chains: [...] } or { error: ... }
    WebhooksPage->>WebhooksTable: supportedChainIds=[...]
    WebhooksTable->>CreateWebhookModal: supportedChainIds=[...]
    CreateWebhookModal->>FilterDetailsStep: supportedChainIds=[...]
    FilterDetailsStep->>MultiNetworkSelector: chainIds=[...]
    MultiNetworkSelector-->>FilterDetailsStep: Render filtered networks
```

## Possibly related PRs

- thirdweb-dev/js#7171: Adds the `getSupportedWebhookChains` API function in the same module, extending webhook API client functionality.

## Suggested labels

`Portal`

## Suggested reviewers

- MananTank
- jnsdls
"""

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9611a17fc773091ae623cd36b674d6c0d7bfa0ca and deeca886a43c766c910f027221a3ffea8aa6f01b.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `apps/dashboard/src/@/api/insight/webhooks.ts` (2 hunks)
* `apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx` (1 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx` (2 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx` (4 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx` (2 hunks)
* `apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx` (5 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (6)</summary>

* apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
* apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/WebhooksTable.tsx
* apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/page.tsx
* apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/CreateWebhookModal.tsx
* apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/webhooks/components/FilterDetailsStep.tsx
* apps/dashboard/src/@/api/insight/webhooks.ts

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (8)</summary>

* GitHub Check: E2E Tests (pnpm, esbuild)
* GitHub Check: Size
* GitHub Check: E2E Tests (pnpm, webpack)
* GitHub Check: E2E Tests (pnpm, vite)
* GitHub Check: Build Packages
* GitHub Check: Lint Packages
* GitHub Check: Unit Tests
* GitHub Check: Analyze (javascript)

</details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=thirdweb-dev/js&utm_content=7209):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: AmineAfia
created_at: 2025-05-29T22:40:25Z
body: * **#7213** <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/7213?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#7209** <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/7209?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/thirdweb-dev/js/7209?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`
<hr />

## How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:
- <kbd>merge-queue</kbd> - adds this PR to the back of the merge queue
- <kbd>hotfix</kbd> - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using [this link](https://app.graphite.dev/invite/github/thirdweb-dev?ref=merge-queue-instructions-in-stack-comment&prId=7209).

<h2></h2>
<sub>

An organization admin has enabled the [Graphite Merge Queue](https://graphite.dev/docs/graphite-merge-queue) in this repository.

</sub>

<sub>

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

</sub>

<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-05-29T22:41:51Z
body: ## [Codecov](https://app.codecov.io/gh/thirdweb-dev/js/pull/7209?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 55.63%. Comparing base [(`31c4bb2`)](https://app.codecov.io/gh/thirdweb-dev/js/commit/31c4bb22fc4b9fb5e99824f31ccb25427aaa409e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) to head [(`deeca88`)](https://app.codecov.io/gh/thirdweb-dev/js/commit/deeca886a43c766c910f027221a3ffea8aa6f01b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #7209   +/-   ##
=======================================
  Coverage   55.63%   55.63%           
=======================================
  Files         908      908           
  Lines       58546    58546           
  Branches     4128     4128           
=======================================
  Hits        32572    32572           
  Misses      25868    25868           
  Partials      106      106           
```

| [Flag](https://app.codecov.io/gh/thirdweb-dev/js/pull/7209/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) | Coverage Δ | |
|---|---|---|
| [packages](https://app.codecov.io/gh/thirdweb-dev/js/pull/7209/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=thirdweb-dev) | `55.63% <ø> (ø)` | |

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-29T22:45:24Z
body: ## size-limit report 📦 
| Path                                    | Size           | Loading time (3g) | Running time (snapdragon) | Total time |
| --------------------------------------- | -------------- | ----------------- | ------------------------- | ---------- |
| thirdweb (esm)                          | 62.11 KB (0%)  | 1.3 s (0%)        | 262 ms (+128.35% 🔺)      | 1.6 s      |
| thirdweb (cjs)                          | 345.28 KB (0%) | 7 s (0%)          | 964 ms (+2.07% 🔺)        | 7.9 s      |
| thirdweb (minimal + tree-shaking)       | 5.7 KB (0%)    | 114 ms (0%)       | 108 ms (+1238.71% 🔺)     | 222 ms     |
| thirdweb/chains (tree-shaking)          | 531 B (0%)     | 11 ms (0%)        | 24 ms (+898.34% 🔺)       | 35 ms      |
| thirdweb/react (minimal + tree-shaking) | 19.52 KB (0%)  | 391 ms (0%)       | 78 ms (+243.13% 🔺)       | 469 ms     |
author_association: CONTRIBUTOR
user: login: graphite-app[bot]
created_at: 2025-05-29T23:27:21Z
body: ### Merge activity

* **May 29, 11:27 PM UTC**: `AmineAfia` added this pull request to the [Graphite merge queue](https://app.graphite.dev/merges?org=thirdweb-dev&repo=js).
* **May 29, 11:42 PM UTC**: Merged by the [Graphite merge queue](https://app.graphite.dev/merges?org=thirdweb-dev&repo=js).

author_association: CONTRIBUTOR