From 3a32f3fc2792d9fb0aa53716edd5d5ddd080eccb Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Mon, 26 May 2025 17:40:20 -0700 Subject: [PATCH 1/6] Implement Universal Bridge components and layout for enhanced user experience - Added `constants.ts` to configure Thirdweb client for the bridge application. - Created `BridgeLayout` component to structure the bridge page layout. - Developed `BridgePage` to handle the main functionality of the Universal Bridge, including metadata and dynamic content. - Introduced `Providers.client.tsx` to wrap the application with necessary context providers. - Implemented `UniversalBridgeEmbed` component for embedding payment functionalities with theme support. This commit establishes the foundational components for the Universal Bridge feature, improving the overall user interface and experience. --- .../components/client/Providers.client.tsx | 20 ++++++++ .../client/UniversalBridgeEmbed.tsx | 13 ++---- apps/dashboard/src/app/bridge/constants.ts | 46 +++++++++++++++++++ apps/dashboard/src/app/bridge/layout.tsx | 29 ++++++++++++ .../(dashboard)/(bridge) => }/bridge/page.tsx | 15 ++---- 5 files changed, 105 insertions(+), 18 deletions(-) create mode 100644 apps/dashboard/src/app/bridge/components/client/Providers.client.tsx rename apps/dashboard/src/app/{(app)/(dashboard)/(bridge) => }/bridge/components/client/UniversalBridgeEmbed.tsx (57%) create mode 100644 apps/dashboard/src/app/bridge/constants.ts create mode 100644 apps/dashboard/src/app/bridge/layout.tsx rename apps/dashboard/src/app/{(app)/(dashboard)/(bridge) => }/bridge/page.tsx (82%) diff --git a/apps/dashboard/src/app/bridge/components/client/Providers.client.tsx b/apps/dashboard/src/app/bridge/components/client/Providers.client.tsx new file mode 100644 index 00000000000..f76044fbf44 --- /dev/null +++ b/apps/dashboard/src/app/bridge/components/client/Providers.client.tsx @@ -0,0 +1,20 @@ +"use client"; +import { ThemeProvider } from "next-themes"; +import { Toaster } from "sonner"; +import { ThirdwebProvider } from "thirdweb/react"; + +export function Providers({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + + ); +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(bridge)/bridge/components/client/UniversalBridgeEmbed.tsx b/apps/dashboard/src/app/bridge/components/client/UniversalBridgeEmbed.tsx similarity index 57% rename from apps/dashboard/src/app/(app)/(dashboard)/(bridge)/bridge/components/client/UniversalBridgeEmbed.tsx rename to apps/dashboard/src/app/bridge/components/client/UniversalBridgeEmbed.tsx index 000ccd47908..0ba8ed0ba1d 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(bridge)/bridge/components/client/UniversalBridgeEmbed.tsx +++ b/apps/dashboard/src/app/bridge/components/client/UniversalBridgeEmbed.tsx @@ -1,21 +1,18 @@ "use client"; -import { getSDKTheme } from "app/(app)/components/sdk-component-theme"; -import { useV5DashboardChain } from "lib/v5-adapter"; import { useTheme } from "next-themes"; -import type { ThirdwebClient } from "thirdweb"; import { PayEmbed } from "thirdweb/react"; +import { getSDKTheme } from "../../../(app)/components/sdk-component-theme"; +import { useV5DashboardChain } from "../../../../lib/v5-adapter"; +import { bridgeAppThirdwebClient } from "../../constants"; -export function UniversalBridgeEmbed({ - chainId, - client, -}: { chainId?: number; client: ThirdwebClient }) { +export function UniversalBridgeEmbed({ chainId }: { chainId?: number }) { const { theme } = useTheme(); const chain = useV5DashboardChain(chainId || 1); return ( + + {children} + + + ); +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(bridge)/bridge/page.tsx b/apps/dashboard/src/app/bridge/page.tsx similarity index 82% rename from apps/dashboard/src/app/(app)/(dashboard)/(bridge)/bridge/page.tsx rename to apps/dashboard/src/app/bridge/page.tsx index 7301c834584..0b3d256c6d4 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(bridge)/bridge/page.tsx +++ b/apps/dashboard/src/app/bridge/page.tsx @@ -1,6 +1,5 @@ import { ArrowUpRightIcon } from "lucide-react"; import type { Metadata } from "next"; -import { getClientThirdwebClient } from "../../../../../@/constants/thirdweb-client.client"; import { UniversalBridgeEmbed } from "./components/client/UniversalBridgeEmbed"; const title = "Universal Bridge: Swap, Bridge, and Onramp"; @@ -16,18 +15,14 @@ export const metadata: Metadata = { }, }; -export default async function RoutesPage({ +export default async function BridgePage({ searchParams, -}: { searchParams: Record }) { - const { chainId } = searchParams; - const client = getClientThirdwebClient(undefined); +}: { searchParams: Promise> }) { + const { chainId } = await searchParams; return (
- +
{/* eslint-disable-next-line @next/next/no-img-element */} @@ -37,7 +32,7 @@ export default async function RoutesPage({ className="-bottom-12 -right-12 pointer-events-none absolute lg:right-0 lg:bottom-0" /> -
+
From a4d1da175c877b28f2be2e6801171a43dc514bd2 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Mon, 26 May 2025 18:43:06 -0700 Subject: [PATCH 2/6] Enhance error handling in confirmation screens for insufficient funds - Added specific error messages for insufficient native funds in both `SwapConfirmationScreen` and `TransferConfirmationScreen`. - Improved user feedback by providing clear titles and messages when transactions cannot be approved or confirmed due to lack of funds for gas. --- .../screens/Buy/swap/ConfirmationScreen.tsx | 15 ++++++++++++++ .../Buy/swap/TransferConfirmationScreen.tsx | 20 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx index ce437bdfdb1..5b948c09299 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx @@ -78,6 +78,13 @@ export function SwapConfirmationScreen(props: { message: "Your wallet rejected the approval request.", }; } + if (error.toLowerCase().includes("insufficient funds for gas")) { + return { + title: "Insufficient Native Funds", + message: + "You do not have enough native funds to approve the transaction.", + }; + } return { title: "Failed to Approve", message: @@ -96,6 +103,13 @@ export function SwapConfirmationScreen(props: { message: "Your wallet rejected the confirmation request.", }; } + if (error.toLowerCase().includes("insufficient funds for gas")) { + return { + title: "Insufficient Native Funds", + message: + "You do not have enough native funds to confirm the transaction.", + }; + } return { title: "Failed to Confirm", message: @@ -333,6 +347,7 @@ export function SwapConfirmationScreen(props: { } catch (e) { console.error(e); setStatus("error"); + setError((e as Error).message); } } }} diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx index 1118c8cc296..e90cb641fdf 100644 --- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx +++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/swap/TransferConfirmationScreen.tsx @@ -126,6 +126,13 @@ export function TransferConfirmationScreen( message: "Your wallet rejected the approval request.", }; } + if (status.error.toLowerCase().includes("insufficient funds for gas")) { + return { + title: "Insufficient Native Funds", + message: + "You do not have enough native funds to approve the transaction.", + }; + } return { title: "Failed to Approve", message: @@ -138,12 +145,23 @@ export function TransferConfirmationScreen( status.id === "error" && status.error ) { - if (status.error.toLowerCase().includes("user rejected")) { + if ( + status.error.toLowerCase().includes("user rejected") || + status.error.toLowerCase().includes("user closed modal") || + status.error.toLowerCase().includes("user denied") + ) { return { title: "Failed to Confirm", message: "Your wallet rejected the confirmation request.", }; } + if (status.error.toLowerCase().includes("insufficient funds for gas")) { + return { + title: "Insufficient Native Funds", + message: + "You do not have enough native funds to confirm the transaction.", + }; + } return { title: "Failed to Confirm", message: From a0f132373e5ba68a43e895aa5f02135f9e9e8fb1 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Tue, 27 May 2025 11:35:01 -0700 Subject: [PATCH 3/6] feat: adds token and amount query params to bridge page --- .../client/UniversalBridgeEmbed.tsx | 11 ++++-- apps/dashboard/src/app/bridge/page.tsx | 37 ++++++++++++++++++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/apps/dashboard/src/app/bridge/components/client/UniversalBridgeEmbed.tsx b/apps/dashboard/src/app/bridge/components/client/UniversalBridgeEmbed.tsx index 0ba8ed0ba1d..60690822e79 100644 --- a/apps/dashboard/src/app/bridge/components/client/UniversalBridgeEmbed.tsx +++ b/apps/dashboard/src/app/bridge/components/client/UniversalBridgeEmbed.tsx @@ -1,12 +1,16 @@ "use client"; import { useTheme } from "next-themes"; -import { PayEmbed } from "thirdweb/react"; +import { PayEmbed, type TokenInfo } from "thirdweb/react"; import { getSDKTheme } from "../../../(app)/components/sdk-component-theme"; import { useV5DashboardChain } from "../../../../lib/v5-adapter"; import { bridgeAppThirdwebClient } from "../../constants"; -export function UniversalBridgeEmbed({ chainId }: { chainId?: number }) { +export function UniversalBridgeEmbed({ + chainId, + token, + amount, +}: { chainId?: number; token: TokenInfo | undefined; amount: string }) { const { theme } = useTheme(); const chain = useV5DashboardChain(chainId || 1); @@ -17,7 +21,8 @@ export function UniversalBridgeEmbed({ chainId }: { chainId?: number }) { mode: "fund_wallet", prefillBuy: { chain, - amount: "0.01", + token, + amount, }, }} theme={getSDKTheme(theme === "light" ? "light" : "dark")} diff --git a/apps/dashboard/src/app/bridge/page.tsx b/apps/dashboard/src/app/bridge/page.tsx index 0b3d256c6d4..e1e5eb88be4 100644 --- a/apps/dashboard/src/app/bridge/page.tsx +++ b/apps/dashboard/src/app/bridge/page.tsx @@ -1,6 +1,11 @@ import { ArrowUpRightIcon } from "lucide-react"; import type { Metadata } from "next"; +import type { Address } from "thirdweb"; +import { defineChain } from "thirdweb/chains"; +import { getContract } from "thirdweb/contract"; +import { getCurrencyMetadata } from "thirdweb/extensions/erc20"; import { UniversalBridgeEmbed } from "./components/client/UniversalBridgeEmbed"; +import { bridgeAppThirdwebClient } from "./constants"; const title = "Universal Bridge: Swap, Bridge, and Onramp"; const description = @@ -18,11 +23,39 @@ export const metadata: Metadata = { export default async function BridgePage({ searchParams, }: { searchParams: Promise> }) { - const { chainId } = await searchParams; + const { chainId, tokenAddress, amount } = await searchParams; + + const { + symbol, + decimals, + name: tokenName, + } = chainId && tokenAddress + ? await getCurrencyMetadata({ + contract: getContract({ + client: bridgeAppThirdwebClient, + // eslint-disable-next-line no-restricted-syntax + chain: defineChain(Number(chainId)), + address: tokenAddress as Address, + }), + }) + : {}; + return (
- +
{/* eslint-disable-next-line @next/next/no-img-element */} From aea940292e7c49a544478ce100c87f1746a82aa7 Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Tue, 27 May 2025 11:35:13 -0700 Subject: [PATCH 4/6] fix: webhook table alignment --- .../webhooks/components/webhooks.client.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/universal-bridge/webhooks/components/webhooks.client.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/universal-bridge/webhooks/components/webhooks.client.tsx index d7326d172f4..378460f3e8e 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/universal-bridge/webhooks/components/webhooks.client.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/connect/universal-bridge/webhooks/components/webhooks.client.tsx @@ -111,8 +111,8 @@ export function PayWebhooksPage(props: PayWebhooksPageProps) { Label Url Created - Version - Delete + Version + Delete @@ -123,8 +123,10 @@ export function PayWebhooksPage(props: PayWebhooksPageProps) { {formatDistanceToNow(webhook.createdAt, { addSuffix: true })} - {webhook.version || "v1"} - + + {webhook.version || "1"} + + Date: Tue, 27 May 2025 11:45:23 -0700 Subject: [PATCH 5/6] Update apps/dashboard/src/app/bridge/page.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Signed-off-by: greg --- apps/dashboard/src/app/bridge/page.tsx | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/dashboard/src/app/bridge/page.tsx b/apps/dashboard/src/app/bridge/page.tsx index e1e5eb88be4..ee77a16beb1 100644 --- a/apps/dashboard/src/app/bridge/page.tsx +++ b/apps/dashboard/src/app/bridge/page.tsx @@ -25,20 +25,25 @@ export default async function BridgePage({ }: { searchParams: Promise> }) { const { chainId, tokenAddress, amount } = await searchParams; - const { - symbol, - decimals, - name: tokenName, - } = chainId && tokenAddress - ? await getCurrencyMetadata({ + // Replace the existing destructuring block with error‐handled fetch + let symbol, decimals, tokenName; + + if (chainId && tokenAddress) { + try { + const metadata = await getCurrencyMetadata({ contract: getContract({ client: bridgeAppThirdwebClient, // eslint-disable-next-line no-restricted-syntax chain: defineChain(Number(chainId)), address: tokenAddress as Address, }), - }) - : {}; + }); + ({ symbol, decimals, name: tokenName } = metadata); + } catch (error) { + console.warn('Failed to fetch token metadata:', error); + // Continue with undefined values; the component should handle gracefully + } + } return (
From 68a5475550b5e9676bfd45ecba052ab552ca3dae Mon Sep 17 00:00:00 2001 From: gregfromstl Date: Tue, 27 May 2025 11:54:04 -0700 Subject: [PATCH 6/6] refactor: enhance type safety for token metadata in BridgePage - Updated variable declarations for symbol, decimals, and tokenName to include explicit types. - Improved error handling for token metadata fetching with consistent logging format. --- apps/dashboard/src/app/bridge/page.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/src/app/bridge/page.tsx b/apps/dashboard/src/app/bridge/page.tsx index ee77a16beb1..e4a22f63cf4 100644 --- a/apps/dashboard/src/app/bridge/page.tsx +++ b/apps/dashboard/src/app/bridge/page.tsx @@ -25,8 +25,9 @@ export default async function BridgePage({ }: { searchParams: Promise> }) { const { chainId, tokenAddress, amount } = await searchParams; - // Replace the existing destructuring block with error‐handled fetch - let symbol, decimals, tokenName; + let symbol: string | undefined; + let decimals: number | undefined; + let tokenName: string | undefined; if (chainId && tokenAddress) { try { @@ -40,7 +41,7 @@ export default async function BridgePage({ }); ({ symbol, decimals, name: tokenName } = metadata); } catch (error) { - console.warn('Failed to fetch token metadata:', error); + console.warn("Failed to fetch token metadata:", error); // Continue with undefined values; the component should handle gracefully } }