Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions apps/dashboard/src/@/api/universal-bridge/token-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { NEXT_PUBLIC_DASHBOARD_CLIENT_ID } from "@/constants/public-envs";
import { UB_BASE_URL } from "./constants";
import type { TokenMetadata } from "./types";

export async function getUniversalBridgeTokens(props: {
chainId?: number;
address?: string;
}) {
Comment on lines +5 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add explicit return type (and optional AbortSignal).

Conform to TS guidelines and make cancellation possible without breaking callers.

-export async function getUniversalBridgeTokens(props: {
-  chainId?: number;
-  address?: string;
-}) {
+export async function getUniversalBridgeTokens(props: {
+  chainId?: number;
+  address?: string;
+  signal?: AbortSignal;
+}): Promise<TokenMetadata[]> {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function getUniversalBridgeTokens(props: {
chainId?: number;
address?: string;
}) {
export async function getUniversalBridgeTokens(props: {
chainId?: number;
address?: string;
signal?: AbortSignal;
}): Promise<TokenMetadata[]> {
🤖 Prompt for AI Agents
In apps/dashboard/src/@/api/universal-bridge/token-list.ts around lines 5–8,
update the function signature to include an explicit return type and an optional
AbortSignal parameter: e.g. export async function
getUniversalBridgeTokens(props: { chainId?: number; address?: string; signal?:
AbortSignal; }): Promise<YourTokenType[]> { ... }. Replace YourTokenType with
the appropriate token interface (or import it), ensure all internal fetch/async
calls accept/forward props.signal to enable cancellation, and update any
callers/tests if needed to satisfy the new return type and optional signal
parameter.

const url = new URL(`${UB_BASE_URL}/v1/tokens`);

Comment on lines +9 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard missing UB_BASE_URL and build URL with a base.

Avoid “Invalid URL” at runtime and produce a clear config error.

-  const url = new URL(`${UB_BASE_URL}/v1/tokens`);
+  if (!UB_BASE_URL) {
+    throw new Error("Missing NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST for Universal Bridge.");
+  }
+  const url = new URL("/v1/tokens", UB_BASE_URL);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const url = new URL(`${UB_BASE_URL}/v1/tokens`);
// apps/dashboard/src/@/api/universal-bridge/token-list.ts
// …previous code…
if (!UB_BASE_URL) {
throw new Error("Missing NEXT_PUBLIC_THIRDWEB_BRIDGE_HOST for Universal Bridge.");
}
const url = new URL("/v1/tokens", UB_BASE_URL);
// …subsequent code…
🤖 Prompt for AI Agents
In apps/dashboard/src/@/api/universal-bridge/token-list.ts around lines 9 to 10,
the code constructs a URL with `${UB_BASE_URL}/v1/tokens` without validating
UB_BASE_URL which can cause an "Invalid URL" at runtime; add a guard that throws
a clear, descriptive configuration error if UB_BASE_URL is missing or empty, and
build the token endpoint using the URL constructor with the base parameter
(e.g., new URL('/v1/tokens', UB_BASE_URL)) to avoid malformed URL creation.

if (props.chainId) {
url.searchParams.append("chainId", String(props.chainId));
}
if (props.address) {
url.searchParams.append("tokenAddress", props.address);
}
Comment on lines +11 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix truthy check for chainId (0 should be valid).

Use a nullish check so chainId=0 isn’t dropped.

-  if (props.chainId) {
+  if (props.chainId != null) {
     url.searchParams.append("chainId", String(props.chainId));
   }
-  if (props.address) {
+  if (props.address) {
     url.searchParams.append("tokenAddress", props.address);
   }

Optional: normalize props.address to lowercase if backend is case-insensitive.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (props.chainId) {
url.searchParams.append("chainId", String(props.chainId));
}
if (props.address) {
url.searchParams.append("tokenAddress", props.address);
}
if (props.chainId != null) {
url.searchParams.append("chainId", String(props.chainId));
}
if (props.address) {
url.searchParams.append("tokenAddress", props.address);
}
🤖 Prompt for AI Agents
In apps/dashboard/src/@/api/universal-bridge/token-list.ts around lines 11 to
16, the current truthy check drops chainId when it is 0; change the condition to
a nullish check (e.g., props.chainId != null) so 0 is allowed, keep appending
chainId via String(props.chainId). Also change the address check to explicitly
test for null/undefined (e.g., props.address != null) and, if the backend is
case-insensitive, normalize by using props.address.toLowerCase() before
appending tokenAddress.

url.searchParams.append("limit", "1000");
url.searchParams.append("includePrices", "false");

const res = await fetch(url.toString(), {
headers: {
"Content-Type": "application/json",
"x-client-id": NEXT_PUBLIC_DASHBOARD_CLIENT_ID,
} as Record<string, string>,
method: "GET",
});

if (!res.ok) {
const text = await res.text();
throw new Error(text);
}

const json = await res.json();
return json.data as Array<TokenMetadata>;
}
Comment on lines +33 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Return typed payload and validate shape.

Avoids implicit any, aligns with “return typed results”.

-  const json = await res.json();
-  return json.data as Array<TokenMetadata>;
+  const { data } = (await res.json()) as { data: TokenMetadata[] };
+  if (!Array.isArray(data)) {
+    throw new Error("Malformed response: expected data: TokenMetadata[].");
+  }
+  return data;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const json = await res.json();
return json.data as Array<TokenMetadata>;
}
const { data } = (await res.json()) as { data: TokenMetadata[] };
if (!Array.isArray(data)) {
throw new Error("Malformed response: expected data: TokenMetadata[].");
}
return data;
}
🤖 Prompt for AI Agents
In apps/dashboard/src/@/api/universal-bridge/token-list.ts around lines 33 to
35, the function currently returns json.data without a typed response or
validation; change it to return a properly typed payload (define or import an
interface such as { data: TokenMetadata[] } for the response) and validate the
runtime shape before returning (e.g., ensure json is an object and json.data is
an array of objects, optionally checking required TokenMetadata fields); if
validation fails, throw a descriptive error; finally return json.data typed as
Array<TokenMetadata>.

42 changes: 1 addition & 41 deletions apps/dashboard/src/@/api/universal-bridge/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,8 @@
"use server";
import type { ProjectResponse } from "@thirdweb-dev/service-utils";
import { getAuthToken } from "@/api/auth-token";
import { DASHBOARD_THIRDWEB_SECRET_KEY } from "@/constants/server-envs";
import { UB_BASE_URL } from "./constants";

export type TokenMetadata = {
name: string;
symbol: string;
address: string;
decimals: number;
chainId: number;
iconUri?: string;
};

export async function getUniversalBridgeTokens(props: {
chainId?: number;
address?: string;
}) {
const url = new URL(`${UB_BASE_URL}/v1/tokens`);

if (props.chainId) {
url.searchParams.append("chainId", String(props.chainId));
}
if (props.address) {
url.searchParams.append("tokenAddress", props.address);
}
url.searchParams.append("limit", "1000");

const res = await fetch(url.toString(), {
headers: {
"Content-Type": "application/json",
"x-secret-key": DASHBOARD_THIRDWEB_SECRET_KEY,
} as Record<string, string>,
method: "GET",
});

if (!res.ok) {
const text = await res.text();
throw new Error(text);
}

const json = await res.json();
return json.data as Array<TokenMetadata>;
}
import type { TokenMetadata } from "./types";

export async function addUniversalBridgeTokenRoute(props: {
chainId: number;
Expand Down
8 changes: 8 additions & 0 deletions apps/dashboard/src/@/api/universal-bridge/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type TokenMetadata = {
name: string;
symbol: string;
address: string;
decimals: number;
chainId: number;
iconUri?: string;
};
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/components/blocks/TokenSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
type ThirdwebClient,
} from "thirdweb";
import { shortenAddress } from "thirdweb/utils";
import type { TokenMetadata } from "@/api/universal-bridge/tokens";
import type { TokenMetadata } from "@/api/universal-bridge/types";
import { Img } from "@/components/blocks/Img";
import { SelectWithSearch } from "@/components/blocks/select-with-search";
import { Badge } from "@/components/ui/badge";
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/hooks/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
import { getUniversalBridgeTokens } from "@/api/universal-bridge/tokens";
import { getUniversalBridgeTokens } from "@/api/universal-bridge/token-list";

export function useTokensData({
chainId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { checksumAddress } from "thirdweb/utils";
import z from "zod";
import { reportPaymentLinkCreated } from "@/analytics/report";
import { createPaymentLink } from "@/api/universal-bridge/developer";
import { getUniversalBridgeTokens } from "@/api/universal-bridge/tokens";
import { getUniversalBridgeTokens } from "@/api/universal-bridge/token-list";
import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors";
import { TokenSelector } from "@/components/blocks/TokenSelector";
import { Button } from "@/components/ui/button";
Expand Down
Loading