diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.stories.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.stories.tsx index 5fd50ea3e1e..190d9a22520 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.stories.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.stories.tsx @@ -104,3 +104,17 @@ export const NotFound: Story = { client: storybookThirdwebClient, }, }; + +export const WithPurchaseData: Story = { + args: { + bridgeStatus: { + ...completedStatus, + purchaseData: { + userId: "68d645b7ded999651272bf1e", + credits: 32000, + transactionId: "fd2606d1-90df-45c6-bd2c-b19a34764a31", + }, + }, + client: storybookThirdwebClient, + }, +}; diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.tsx index 5d4ed8b5cd4..ac220c4b6fa 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/bridge-status.tsx @@ -1,10 +1,11 @@ "use client"; import { useQuery } from "@tanstack/react-query"; +import { CodeClient } from "@workspace/ui/components/code/code.client"; import { Img } from "@workspace/ui/components/img"; import { Spinner } from "@workspace/ui/components/spinner"; import { ArrowRightIcon, CircleCheckIcon, CircleXIcon } from "lucide-react"; import Link from "next/link"; -import type { ThirdwebClient } from "thirdweb"; +import { NATIVE_TOKEN_ADDRESS, type ThirdwebClient } from "thirdweb"; import type { Status, Token } from "thirdweb/bridge"; import { status } from "thirdweb/bridge"; import { toTokens } from "thirdweb/utils"; @@ -15,11 +16,17 @@ import { cn } from "@/lib/utils"; import { fetchChain } from "@/utils/fetchChain"; import { resolveSchemeWithErrorHandler } from "@/utils/resolveSchemeWithErrorHandler"; +type PurchaseData = Exclude["purchaseData"]; + export function BridgeStatus(props: { bridgeStatus: Status; client: ThirdwebClient; }) { const { bridgeStatus } = props; + const purchaseDataString = + bridgeStatus.status !== "NOT_FOUND" && bridgeStatus.purchaseData + ? getPurchaseData(bridgeStatus.purchaseData) + : undefined; return (
@@ -39,7 +46,7 @@ export function BridgeStatus(props: { /> )} -
+

Status

@@ -80,6 +87,18 @@ export function BridgeStatus(props: { />
+ + {purchaseDataString && ( +
+

Purchase Data

+ +
+ )}
); } @@ -96,7 +115,7 @@ function TokenInfo(props: { const chainQuery = useChainQuery(props.token.chainId); return ( -
+

{props.label} @@ -107,7 +126,12 @@ function TokenInfo(props: {
+

Transactions

@@ -370,3 +394,11 @@ export function BridgeStatusWithPolling(props: { /> ); } + +function getPurchaseData(purchaseData: PurchaseData) { + try { + return JSON.stringify(purchaseData, null, 2); + } catch { + return undefined; + } +} diff --git a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/page.tsx b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/page.tsx index b67d14b1c66..85c94ea603e 100644 --- a/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/page.tsx +++ b/apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/tx/[txHash]/page.tsx @@ -8,7 +8,6 @@ import { Clock4Icon, InfoIcon, } from "lucide-react"; -import { notFound } from "next/navigation"; import { toTokens } from "thirdweb"; import { status } from "thirdweb/bridge"; import type { ChainMetadata } from "thirdweb/chains"; @@ -55,10 +54,10 @@ export default async function Page(props: { const [transaction, receipt, bridgeStatus] = await Promise.all([ eth_getTransactionByHash(rpcRequest, { hash: params.txHash, - }), + }).catch(() => undefined), eth_getTransactionReceipt(rpcRequest, { hash: params.txHash, - }), + }).catch(() => undefined), status({ chainId: chain.chainId, transactionHash: params.txHash, @@ -66,8 +65,17 @@ export default async function Page(props: { }).catch(() => undefined), ]); - if (!transaction.blockHash) { - notFound(); + if (!transaction?.blockHash || !receipt) { + return ( +
+
+
+ +
+ Transaction not found +
+
+ ); } const block = await eth_getBlockByHash(rpcRequest, { diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx index 2a2be2c15a5..521e867939c 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscovery.tsx @@ -2,6 +2,9 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@tanstack/react-query"; import type { ProjectResponse } from "@thirdweb-dev/service-utils"; +import { Button } from "@workspace/ui/components/button"; +import { Spinner } from "@workspace/ui/components/spinner"; +import { PlusIcon } from "lucide-react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import type { ThirdwebClient } from "thirdweb"; @@ -19,7 +22,6 @@ import { type RouteDiscoveryValidationSchema, routeDiscoveryValidationSchema, } from "@/schema/validations"; -import { RouteDiscoveryCard } from "./RouteDiscoveryCard"; export const RouteDiscovery = ({ project, @@ -77,33 +79,21 @@ export const RouteDiscovery = ({ }, ); + const errorText = form.getFieldState("tokenAddress").error?.message; + return (
- -
-

+
+
+

Add a token to Bridge

-

+

Select your chain and input the token address to automatically kick-off the token route discovery process.
This may take up to 20-40 minutes to complete.

-
- +
+
+ {errorText ? ( +

{errorText}

+ ) : ( +
+ )} + + +
+
+
); diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscoveryCard.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscoveryCard.tsx deleted file mode 100644 index 9a3cf057ad9..00000000000 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/RouteDiscoveryCard.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import type React from "react"; -import { Button } from "@/components/ui/button"; -import { Spinner } from "@/components/ui/Spinner"; -import { cn } from "@/lib/utils"; - -export function RouteDiscoveryCard( - props: React.PropsWithChildren<{ - bottomText: React.ReactNode; - header?: { - description: string | undefined; - title: string; - }; - errorText: string | undefined; - noPermissionText: string | undefined; - saveButton?: { - onClick?: () => void; - disabled: boolean; - isPending: boolean; - type?: "submit"; - variant?: - | "ghost" - | "default" - | "primary" - | "destructive" - | "outline" - | "secondary"; - className?: string; - label?: string; - }; - }>, -) { - return ( -
-
- {props.header && ( - <> -

- {props.header.title} -

- {props.header.description && ( -

- {props.header.description} -

- )} - - )} - - {props.children} -
- -
- {props.noPermissionText ? ( -

- {props.noPermissionText} -

- ) : props.errorText ? ( -

{props.errorText}

- ) : ( -

{props.bottomText}

- )} - - {props.saveButton && !props.noPermissionText && ( - - )} -
-
- ); -} diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/page.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/page.tsx index 4c6f13ff4a8..9a1d6f99911 100644 --- a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/page.tsx +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/page.tsx @@ -11,6 +11,7 @@ import { PayAnalytics } from "../payments/components/PayAnalytics"; import { getUniversalBridgeFiltersFromSearchParams } from "../payments/components/time"; import { QuickStartSection } from "./QuickstartSection.client"; import { RouteDiscovery } from "./RouteDiscovery"; +import { ViewTxStatus } from "./view-tx-status"; export default async function Page(props: { params: Promise<{ @@ -84,7 +85,7 @@ export default async function Page(props: { ], }} > -
+
- + + +
+ +
); diff --git a/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/view-tx-status.tsx b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/view-tx-status.tsx new file mode 100644 index 00000000000..07925e9fd2c --- /dev/null +++ b/apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/bridge/view-tx-status.tsx @@ -0,0 +1,72 @@ +"use client"; +import { Button } from "@workspace/ui/components/button"; +import { ArrowUpRightIcon } from "lucide-react"; +import Link from "next/link"; +import { useState } from "react"; +import type { ThirdwebClient } from "thirdweb"; +import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +export function ViewTxStatus(props: { client: ThirdwebClient }) { + const [txHash, setTxHash] = useState(""); + const [chainId, setChainId] = useState(1); + + const isValidTxHash = txHash.length === 66 && txHash.startsWith("0x"); + const isEnabled = !!txHash && !!chainId && isValidTxHash; + + return ( +
+
+

+ View transaction status +

+

+ Select chain and enter the transaction hash to view the transaction + status. +

+ +
+
+ + +
+
+ + setTxHash(e.target.value)} + placeholder="0x.." + /> + {txHash && !isValidTxHash && ( +

+ Invalid transaction hash +

+ )} +
+
+
+ +
+ +
+
+ ); +}