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
1 change: 1 addition & 0 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"@marsidev/react-turnstile": "^1.1.0",
"@number-flow/react": "^0.5.10",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14",
"@radix-ui/react-avatar": "^1.1.10",
Expand Down
4 changes: 3 additions & 1 deletion apps/dashboard/src/app/(app)/(dashboard)/tokens/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Metadata } from "next";
import { unstable_cache } from "next/cache";
import { Bridge } from "thirdweb";
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
import { bridgeStats } from "../../../bridge/data";
import { PageHeader } from "./components/header";
import { TokenPage } from "./components/token-page";

Expand Down Expand Up @@ -36,7 +37,8 @@ export default async function Page() {
</h1>

<p className="text-muted-foreground">
85+ chains, 4500+ tokens and 9+ million routes supported
{bridgeStats.supportedChains} chains, {bridgeStats.supportedTokens}{" "}
tokens and {bridgeStats.supportedRoutes} routes supported
</p>
</div>
</div>
Expand Down
Binary file added apps/dashboard/src/app/bridge/assets/chains.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/dashboard/src/app/bridge/assets/routes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 51 additions & 10 deletions apps/dashboard/src/app/bridge/components/bridge-page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import type { Format } from "@number-flow/react";
import { cn } from "@workspace/ui/lib/utils";
import Image, { type StaticImageData } from "next/image";
import type { BuyAndSwapEmbedProps } from "@/components/blocks/BuyAndSwapEmbed";
import { FaqAccordion } from "@/components/blocks/faq-section";
import ChainsImage from "../assets/chains.png";
import RoutesImage from "../assets/routes.png";
import TokensImage from "../assets/tokens.png";
import { bridgeStats } from "../data";
import { AnimatedNumbers } from "./client/animated-numbers";
import { UniversalBridgeEmbed } from "./client/UniversalBridgeEmbed";
import { BridgePageHeader } from "./header";

Expand Down Expand Up @@ -35,24 +42,58 @@ function HeadingSection(props: { title: React.ReactNode }) {
<div className="mb-3 lg:mb-6">{props.title}</div>

<p className="text-muted-foreground text-sm text-pretty text-center lg:text-lg mb-6 lg:mb-8">
Seamlessly move your assets across 85+ chains with the best rates and
fastest execution
Seamlessly move your assets across {bridgeStats.supportedChains} chains
with the best rates and fastest execution
</p>

<div className="flex flex-col lg:flex-row gap-3 lg:gap-2 items-center justify-center">
<DataPill>85+ Chains Supported</DataPill>
<DataPill>4500+ Tokens Supported</DataPill>
<DataPill>9+ Million Routes Available</DataPill>
<div className="flex gap-3 items-center lg:justify-center flex-wrap">
<DataSquare data={90} label="Chains Supported" src={ChainsImage} />
<DataSquare
data={6700}
label="Tokens Supported"
src={TokensImage}
format={{ notation: "compact" }}
/>
<DataSquare
data={14000000}
format={{ notation: "compact" }}
label="Routes Available"
src={RoutesImage}
/>
</div>
</div>
);
}

function DataPill(props: { children: React.ReactNode }) {
function DataSquare(props: {
data: number;
label: string;
src: StaticImageData;
format?: Format;
imageClassName?: string;
}) {
return (
<p className="bg-card flex items-center text-xs lg:text-sm gap-1.5 text-foreground border rounded-full px-8 lg:px-3 py-1.5 hover:text-foreground transition-colors duration-300">
{props.children}
</p>
<div className="py-2 lg:py-0 size-full lg:size-[220px] rounded-xl border hover:bg-card bg-card/50 relative shrink-0 overflow-hidden">
<div className="p-4">
<div className="flex items-center gap-1 text-3xl lg:text-5xl font-medium tracking-tight font-mono mb-1 h-[45px] lg:h-[56px]">
<AnimatedNumbers
value={props.data}
format={props.format}
suffix="+"
/>
</div>
<p className="text-sm text-foreground">{props.label}</p>
</div>
<Image
draggable={false}
src={props.src}
className={cn(
"object-cover h-full lg:h-auto absolute right-0 top-0 bottom-0 left-1/2 lg:left-0 lg:top-auto invert dark:invert-0 opacity-50 lg:opacity-100",
props.imageClassName,
)}
alt=""
/>
</div>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"use client";

import NumberFlow, { type Format } from "@number-flow/react";
import { useLayoutEffect, useRef, useState } from "react";

export function AnimatedNumbers(props: {
value: number;
format?: Format;
suffix?: string;
}) {
const ref = useRef<HTMLDivElement>(null);
const [value, setValue] = useState(props.value);

useLayoutEffect(() => {
if (ref.current) {
// when the div becomes visible, set the value to the props value
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setValue(props.value);
} else {
setValue(0);
}
});
});
observer.observe(ref.current);
return () => observer.disconnect();
}
}, [props.value]);

return (
<div ref={ref}>
<NumberFlow
willChange
value={value}
format={props.format}
suffix={props.suffix}
/>
</div>
);
}
5 changes: 5 additions & 0 deletions apps/dashboard/src/app/bridge/data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const bridgeStats = {
supportedChains: "90+",
supportedTokens: "6700+",
supportedRoutes: "14M+",
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { ChainMetadata } from "thirdweb/chains";
import { serverThirdwebClient } from "@/constants/thirdweb-client.server";
import { fetchChain } from "@/utils/fetchChain";
import { BridgePageUI } from "../../components/bridge-page";
import { bridgeStats } from "../../data";
import { generateTokenPairSlugs, getTokenPairData } from "./slug-map";

type Params = {
Expand Down Expand Up @@ -35,8 +36,7 @@ export async function generateMetadata(
}

const title = getTitle(tokenMetadata);
const description =
"Bridge and swap 4500+ tokens across 85+ chains (Ethereum, Base, Optimism, Arbitrum, BNB & more). Best-price routing with near-instant finality";
const description = `Bridge and swap ${bridgeStats.supportedTokens} tokens across ${bridgeStats.supportedChains} chains (Ethereum, Base, Optimism, Arbitrum, BNB & more). Best-price routing with near-instant finality`;

const result: Metadata = {
title,
Expand Down
6 changes: 3 additions & 3 deletions apps/dashboard/src/app/bridge/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { Metadata } from "next";
import { isAddress, NATIVE_TOKEN_ADDRESS } from "thirdweb";
import { BridgePageUI } from "./components/bridge-page";
import { bridgeStats } from "./data";

const title = "thirdweb Bridge: Buy, Bridge & Swap Crypto on 85+ Chains";
const description =
"Bridge and swap 4500+ tokens across 85+ chains (Ethereum, Base, Optimism, Arbitrum, BNB & more). Best-price routing with near-instant finality";
const title = `thirdweb Bridge: Buy, Bridge & Swap Crypto on ${bridgeStats.supportedChains} Chains`;
const description = `Bridge and swap ${bridgeStats.supportedTokens} tokens across ${bridgeStats.supportedChains} chains (Ethereum, Base, Optimism, Arbitrum, BNB & more). Best-price routing with near-instant finality`;

export const metadata: Metadata = {
description,
Expand Down
Loading
Loading