diff --git a/packages/app/public/images/gray_square_background.svg b/packages/app/public/images/gray_square_background.svg
index 4cc524c4c..ef21f98b8 100644
--- a/packages/app/public/images/gray_square_background.svg
+++ b/packages/app/public/images/gray_square_background.svg
@@ -2,7 +2,7 @@
-
+
diff --git a/packages/app/public/images/onramp/alchemy_pay.webp b/packages/app/public/images/onramp/alchemy_pay.webp
new file mode 100644
index 000000000..f472d41fd
Binary files /dev/null and b/packages/app/public/images/onramp/alchemy_pay.webp differ
diff --git a/packages/app/public/images/onramp/banxa.webp b/packages/app/public/images/onramp/banxa.webp
new file mode 100644
index 000000000..817ca4ef1
Binary files /dev/null and b/packages/app/public/images/onramp/banxa.webp differ
diff --git a/packages/app/public/images/onramp/coinbase.webp b/packages/app/public/images/onramp/coinbase.webp
new file mode 100644
index 000000000..58d055533
Binary files /dev/null and b/packages/app/public/images/onramp/coinbase.webp differ
diff --git a/packages/app/public/images/onramp/kado.webp b/packages/app/public/images/onramp/kado.webp
new file mode 100644
index 000000000..520b87e67
Binary files /dev/null and b/packages/app/public/images/onramp/kado.webp differ
diff --git a/packages/app/public/images/onramp/mt_pelerin.webp b/packages/app/public/images/onramp/mt_pelerin.webp
new file mode 100644
index 000000000..3d5a8c04d
Binary files /dev/null and b/packages/app/public/images/onramp/mt_pelerin.webp differ
diff --git a/packages/app/public/images/onramp/onramp.webp b/packages/app/public/images/onramp/onramp.webp
new file mode 100644
index 000000000..2444befaf
Binary files /dev/null and b/packages/app/public/images/onramp/onramp.webp differ
diff --git a/packages/app/public/images/onramp/ramp.webp b/packages/app/public/images/onramp/ramp.webp
new file mode 100644
index 000000000..6dd99239f
Binary files /dev/null and b/packages/app/public/images/onramp/ramp.webp differ
diff --git a/packages/app/public/images/onramp/simplex.webp b/packages/app/public/images/onramp/simplex.webp
new file mode 100644
index 000000000..794e69b92
Binary files /dev/null and b/packages/app/public/images/onramp/simplex.webp differ
diff --git a/packages/app/public/images/onramp/transak.webp b/packages/app/public/images/onramp/transak.webp
new file mode 100644
index 000000000..e1338e98b
Binary files /dev/null and b/packages/app/public/images/onramp/transak.webp differ
diff --git a/packages/app/src/app/(embed)/bridge/embed/EmbedPageWrapper.tsx b/packages/app/src/app/(embed)/bridge/embed/EmbedPageWrapper.tsx
index 23c5c8604..b74311026 100644
--- a/packages/app/src/app/(embed)/bridge/embed/EmbedPageWrapper.tsx
+++ b/packages/app/src/app/(embed)/bridge/embed/EmbedPageWrapper.tsx
@@ -4,7 +4,7 @@ import BridgeClient from '../../../(with-sidebar)/bridge/BridgeClient';
import { BridgePageProps, initializeBridgePage } from '../../../../utils/bridgePageUtils';
export default async function EmbedPageWrapper({ searchParams, redirectPath }: BridgePageProps) {
- await initializeBridgePage(searchParams, redirectPath);
+ await initializeBridgePage({ searchParams, redirectPath });
return (
<>
diff --git a/packages/app/src/app/(embed)/bridge/embed/buy/[slug]/page.tsx b/packages/app/src/app/(embed)/bridge/embed/buy/[slug]/page.tsx
new file mode 100644
index 000000000..ceb4cce63
--- /dev/null
+++ b/packages/app/src/app/(embed)/bridge/embed/buy/[slug]/page.tsx
@@ -0,0 +1,19 @@
+import { Slug } from 'packages/app/src/utils/bridgePageUtils';
+
+import { PathnameEnum } from '@/bridge/constants';
+
+import EmbedPageWrapper from '../../EmbedPageWrapper';
+
+type Props = {
+ searchParams: { [key: string]: string | string[] | undefined };
+ params: { slug: Slug };
+};
+
+export default async function EmbeddedBuyOnrampServicePage({ searchParams, params }: Props) {
+ return (
+
+ );
+}
diff --git a/packages/app/src/app/(with-sidebar)/bridge/BridgePageWrapper.tsx b/packages/app/src/app/(with-sidebar)/bridge/BridgePageWrapper.tsx
index 42f94f1c3..5e7c7ec41 100644
--- a/packages/app/src/app/(with-sidebar)/bridge/BridgePageWrapper.tsx
+++ b/packages/app/src/app/(with-sidebar)/bridge/BridgePageWrapper.tsx
@@ -2,7 +2,7 @@ import { BridgePageProps, initializeBridgePage } from '../../../utils/bridgePage
import BridgeClient from './BridgeClient';
export default async function BridgePageWrapper({ searchParams, redirectPath }: BridgePageProps) {
- await initializeBridgePage(searchParams, redirectPath);
+ await initializeBridgePage({ searchParams, redirectPath });
return (
diff --git a/packages/app/src/app/(with-sidebar)/bridge/buy/[slug]/page.tsx b/packages/app/src/app/(with-sidebar)/bridge/buy/[slug]/page.tsx
new file mode 100644
index 000000000..3afcd041e
--- /dev/null
+++ b/packages/app/src/app/(with-sidebar)/bridge/buy/[slug]/page.tsx
@@ -0,0 +1,26 @@
+import type { Metadata } from 'next';
+import { Slug } from 'packages/app/src/utils/bridgePageUtils';
+
+import { PathnameEnum } from '@/bridge/constants';
+
+import BridgePageWrapper from '../../BridgePageWrapper';
+
+type Props = {
+ searchParams: { [key: string]: string | string[] | undefined };
+ params: { slug: Slug };
+};
+
+export const metadata: Metadata = {
+ title: 'On-Ramp to Arbitrum',
+ description:
+ "On-ramp directly to Arbitrum with one of several third party providers. Built to scale Ethereum, Arbitrum brings you 10x lower costs while inheriting Ethereum's security model. Arbitrum is a Layer 2 Optimistic Rollup.",
+};
+
+export default async function BridgeBuyOnrampServicePage({ searchParams, params }: Props) {
+ return (
+
+ );
+}
diff --git a/packages/app/src/utils/bridgePageUtils.tsx b/packages/app/src/utils/bridgePageUtils.tsx
index 227907905..0ea49527c 100644
--- a/packages/app/src/utils/bridgePageUtils.tsx
+++ b/packages/app/src/utils/bridgePageUtils.tsx
@@ -1,17 +1,17 @@
+import { onrampServices } from '@/bridge/components/BuyPanel/utils';
import { PathnameEnum } from '@/bridge/constants';
import { addOrbitChainsToArbitrumSDK } from '../initialization';
import { sanitizeAndRedirect } from './sanitizeAndRedirect';
+export type Slug = (typeof onrampServices)[number]['slug'];
+
export interface BridgePageProps {
searchParams: { [key: string]: string | string[] | undefined };
- redirectPath: PathnameEnum;
+ redirectPath: PathnameEnum | `${PathnameEnum.BUY}/${Slug}` | `${PathnameEnum.EMBED_BUY}/${Slug}`;
}
-export async function initializeBridgePage(
- searchParams: { [key: string]: string | string[] | undefined },
- redirectPath: PathnameEnum,
-) {
+export async function initializeBridgePage({ searchParams, redirectPath }: BridgePageProps) {
/**
* This code is run on every query param change,
* we don't want to sanitize every query param change.
diff --git a/packages/app/tailwind.config.js b/packages/app/tailwind.config.js
index 96734fdeb..68a654bec 100644
--- a/packages/app/tailwind.config.js
+++ b/packages/app/tailwind.config.js
@@ -19,7 +19,6 @@ module.exports = {
gradientCelebration: 'linear-gradient(to right, #1B4ADD6F, #E573106F)',
highlight:
'linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.1) 25%, rgba(255, 255, 255, 0.1) 75%, rgba(255, 255, 255, 0))',
- eclipse: 'radial-gradient(ellipse 550px 200px at center, #262626 70%, transparent 70%)',
},
colors: {
// ACTION
diff --git a/packages/arb-token-bridge-ui/src/components/BuyPanel/BackButton.tsx b/packages/arb-token-bridge-ui/src/components/BuyPanel/BackButton.tsx
new file mode 100644
index 000000000..03898dbbe
--- /dev/null
+++ b/packages/arb-token-bridge-ui/src/components/BuyPanel/BackButton.tsx
@@ -0,0 +1,33 @@
+'use client';
+
+import { ChevronLeftIcon } from '@heroicons/react/24/outline';
+import Link from 'next/link';
+import { useSearchParams } from 'next/navigation';
+
+import { PathnameEnum } from '@/bridge/constants';
+import { useMode } from '@/bridge/hooks/useMode';
+
+import { Button } from '../common/Button';
+
+export function BackButton() {
+ const { embedMode } = useMode();
+ const searchParams = useSearchParams();
+
+ return (
+
+
+ Back
+
+ );
+}
diff --git a/packages/arb-token-bridge-ui/src/components/BuyPanel.tsx b/packages/arb-token-bridge-ui/src/components/BuyPanel/BuyPanel.tsx
similarity index 58%
rename from packages/arb-token-bridge-ui/src/components/BuyPanel.tsx
rename to packages/arb-token-bridge-ui/src/components/BuyPanel/BuyPanel.tsx
index 1b28764de..8bd5af66e 100644
--- a/packages/arb-token-bridge-ui/src/components/BuyPanel.tsx
+++ b/packages/arb-token-bridge-ui/src/components/BuyPanel/BuyPanel.tsx
@@ -1,7 +1,9 @@
import { ChevronDownIcon } from '@heroicons/react/24/outline';
+import { ExclamationCircleIcon } from '@heroicons/react/24/solid';
import { BigNumber, utils } from 'ethers';
import dynamic from 'next/dynamic';
-import React, { PropsWithChildren, memo, useCallback } from 'react';
+import { usePathname } from 'next/navigation';
+import React, { PropsWithChildren, memo, useMemo } from 'react';
import { twMerge } from 'tailwind-merge';
import { Chain } from 'viem';
import { useAccount, useBalance } from 'wagmi';
@@ -10,69 +12,26 @@ import { shallow } from 'zustand/shallow';
import { getProviderForChainId } from '@/token-bridge-sdk/utils';
-import { useETHPrice } from '../hooks/useETHPrice';
-import { useMode } from '../hooks/useMode';
-import { useNativeCurrency } from '../hooks/useNativeCurrency';
-import { ChainId } from '../types/ChainId';
-import { getAPIBaseUrl } from '../util';
-import { formatAmount, formatUSD } from '../util/NumberUtils';
-import { isOnrampEnabled, isOnrampServiceEnabled } from '../util/featureFlag';
-import { getNetworkName } from '../util/networks';
-import { TokenLogoFallback } from './TransferPanel/TokenInfo';
-import { Button } from './common/Button';
-import { Dialog } from './common/Dialog';
-import { DialogProps, DialogWrapper, useDialog2 } from './common/Dialog2';
-import { NetworkImage } from './common/NetworkImage';
-import { NetworksPanel } from './common/NetworkSelectionContainer';
-import { SafeImage } from './common/SafeImage';
-import { SearchPanel } from './common/SearchPanel/SearchPanel';
-import { Loader } from './common/atoms/Loader';
-
-function MoonPaySkeleton({ children }: PropsWithChildren) {
- const { embedMode } = useMode();
-
- return (
-
-
-
-
-
}
- />
-
MoonPay
-
- PayPal, Debit Card, Apple Pay
-
-
-
div]:!m-0 [&>div]:!w-full [&>div]:!border-x-0 [&>div]:!border-none [&>div]:!p-0 sm:[&>div]:!rounded sm:[&>div]:!border-x',
- '[&_iframe]:rounded-xl',
- )}
- >
- {children}
-
-
- On-Ramps are not directly endorsed by Arbitrum. Please use at your own risk.
-
-
- );
-}
+import { useETHPrice } from '../../hooks/useETHPrice';
+import { useMode } from '../../hooks/useMode';
+import { useNativeCurrency } from '../../hooks/useNativeCurrency';
+import { ChainId } from '../../types/ChainId';
+import { formatAmount, formatUSD } from '../../util/NumberUtils';
+import { isOnrampEnabled, isOnrampServiceEnabled } from '../../util/featureFlag';
+import { getNetworkName } from '../../util/networks';
+import { TokenLogoFallback } from '../TransferPanel/TokenInfo';
+import { Button } from '../common/Button';
+import { Dialog } from '../common/Dialog';
+import { DialogProps, DialogWrapper, useDialog2 } from '../common/Dialog2';
+import { NetworkImage } from '../common/NetworkImage';
+import { NetworksPanel } from '../common/NetworkSelectionContainer';
+import { SafeImage } from '../common/SafeImage';
+import { SearchPanel } from '../common/SearchPanel/SearchPanel';
+import { Loader } from '../common/atoms/Loader';
+import { Homepage } from './Homepage';
+import { LinkoutOnrampPanel } from './LinkoutOnrampPanel';
+import { MoonPayPanel, MoonPaySkeleton } from './MoonPayPanel';
+import { onrampServices } from './utils';
const MoonPayProvider = dynamic(
() => import('@moonpay/moonpay-react').then((mod) => mod.MoonPayProvider),
@@ -179,7 +138,6 @@ function BuyPanelNetworkButton({
);
}
-/* eslint-disable @typescript-eslint/no-unused-vars */
const BalanceWrapper = memo(function BalanceWrapper() {
const { address, isConnected } = useAccount();
const { ethToUSD } = useETHPrice();
@@ -192,6 +150,13 @@ const BalanceWrapper = memo(function BalanceWrapper() {
error: balanceError,
} = useBalance({ chainId: selectedChainId, address });
const showPriceInUsd = nativeCurrency.symbol.toLowerCase() === 'eth';
+ const balanceInUsd = useMemo(() => {
+ if (!balanceState || !showPriceInUsd) {
+ return null;
+ }
+ return ethToUSD(Number(utils.formatEther(BigNumber.from(balanceState.value))));
+ }, [balanceState, ethToUSD, showPriceInUsd]);
+ const isBalanceLessThan15Usd = Number(balanceInUsd) < 15;
const [dialogProps, openDialog] = useDialog2();
const openBuyPanelNetworkSelectionDialog = () => {
openDialog('buy_panel_network_selection');
@@ -228,58 +193,52 @@ const BalanceWrapper = memo(function BalanceWrapper() {
{!isLoadingBalance && (balanceError || typeof balanceState === 'undefined') && (
Failed to load balance.
)}
- {balanceState && showPriceInUsd && (
-
- ({formatUSD(ethToUSD(Number(utils.formatEther(BigNumber.from(balanceState.value)))))})
+ {balanceInUsd !== null && balanceInUsd !== 0 && (
+
+ {balanceInUsd} {formatUSD(balanceInUsd)}
)}
+ {isBalanceLessThan15Usd && (
+
+
+ Low wallet balance
+
+ )}
+
);
});
-const MoonPayPanel = memo(function MoonPayPanel() {
- const { address } = useAccount();
- const showMoonPay = isOnrampServiceEnabled('moonpay');
-
- const handleGetSignature = useCallback(async (widgetUrl: string): Promise => {
- const response = await fetch(`${getAPIBaseUrl()}/api/moonpay`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({ url: widgetUrl }),
- });
- const { signature } = await response.json();
- return signature;
- }, []);
-
- if (!showMoonPay) {
- return null;
- }
-
- const MoonPayBuyWidget = dynamic(
- () => import('@moonpay/moonpay-react').then((mod) => mod.MoonPayBuyWidget),
- {
- ssr: false,
- },
- );
+function OnrampDisclaimer() {
+ const { embedMode } = useMode();
return (
-
-
-
+
+ On-Ramps are not endorsed by Arbitrum. Please use at your own risk.
+
);
-});
+}
+
+function OnrampServicePanel() {
+ const pathname = usePathname();
+ const onrampService = pathname.split('/').pop();
+ const allOnrampServices = onrampServices.map((service) => service.slug);
+
+ switch (onrampService) {
+ case 'moonpay':
+ if (!isMoonPayEnabled) {
+ return null;
+ }
+ return ;
+ case allOnrampServices.find((service) => service === onrampService):
+ return ;
+ default:
+ return ;
+ }
+}
export function BuyPanel() {
const { embedMode } = useMode();
@@ -287,13 +246,17 @@ export function BuyPanel() {
return (
+
+
-
+
+
+
);
}
diff --git a/packages/arb-token-bridge-ui/src/components/BuyPanel/Homepage.tsx b/packages/arb-token-bridge-ui/src/components/BuyPanel/Homepage.tsx
new file mode 100644
index 000000000..7c59fe488
--- /dev/null
+++ b/packages/arb-token-bridge-ui/src/components/BuyPanel/Homepage.tsx
@@ -0,0 +1,100 @@
+import { ArrowUpRightIcon } from '@heroicons/react/24/outline';
+import Image from 'next/image';
+import Link from 'next/link';
+import { usePathname, useRouter, useSearchParams } from 'next/navigation';
+import { useCallback } from 'react';
+import { twMerge } from 'tailwind-merge';
+
+import { trackEvent } from '@/bridge/util/AnalyticsUtils';
+import MoonPay from '@/images/onramp/moonpay.svg';
+
+import { Button } from '../common/Button';
+import { onrampServices } from './utils';
+
+function OnrampServiceTile({ name, logo, slug }: { name: string; logo: string; slug: string }) {
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
+
+ return (
+ {
+ trackEvent('Onramp Service Click', { service: name });
+ }}
+ >
+
+
+ );
+}
+
+function MoonPayTile() {
+ const pathname = usePathname();
+ const searchParams = useSearchParams();
+
+ return (
+ {
+ trackEvent('Onramp Service Click', { service: 'MoonPay' });
+ }}
+ >
+
+
+ );
+}
+
+export function Homepage() {
+ const router = useRouter();
+
+ const allOnrampOnClick = useCallback(() => {
+ router.push('/projects?subcategories=fiat-on-ramp');
+ }, [router]);
+
+ return (
+
+
+ {onrampServices.map((service) => (
+
+ ))}
+
+
+ );
+}
diff --git a/packages/arb-token-bridge-ui/src/components/BuyPanel/LinkoutOnrampPanel.tsx b/packages/arb-token-bridge-ui/src/components/BuyPanel/LinkoutOnrampPanel.tsx
new file mode 100644
index 000000000..d3da86cf6
--- /dev/null
+++ b/packages/arb-token-bridge-ui/src/components/BuyPanel/LinkoutOnrampPanel.tsx
@@ -0,0 +1,54 @@
+import { ArrowUpRightIcon } from '@heroicons/react/24/outline';
+import Image from 'next/image';
+import { twMerge } from 'tailwind-merge';
+
+import { useMode } from '@/bridge/hooks/useMode';
+
+import { Button } from '../common/Button';
+import { ExternalLink } from '../common/ExternalLink';
+import { BackButton } from './BackButton';
+import { onrampServices } from './utils';
+
+export function LinkoutOnrampPanel({ serviceSlug }: { serviceSlug: string }) {
+ const { embedMode } = useMode();
+ const service = onrampServices.find((s) => s.slug === serviceSlug);
+
+ if (!service) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+
{service.name}
+
+ Buy and transfer instantly using your debit card, bank account with Thirdweb{' '}
+ {service.name}.
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/arb-token-bridge-ui/src/components/BuyPanel/MoonPayPanel.tsx b/packages/arb-token-bridge-ui/src/components/BuyPanel/MoonPayPanel.tsx
new file mode 100644
index 000000000..a31c68c23
--- /dev/null
+++ b/packages/arb-token-bridge-ui/src/components/BuyPanel/MoonPayPanel.tsx
@@ -0,0 +1,90 @@
+import dynamic from 'next/dynamic';
+import { PropsWithChildren, memo, useCallback } from 'react';
+import { twMerge } from 'tailwind-merge';
+import { useAccount } from 'wagmi';
+
+import { useMode } from '@/bridge/hooks/useMode';
+
+import { getAPIBaseUrl } from '../../util';
+import { isOnrampServiceEnabled } from '../../util/featureFlag';
+import { SafeImage } from '../common/SafeImage';
+import { BackButton } from './BackButton';
+
+export function MoonPaySkeleton({ children }: PropsWithChildren) {
+ const { embedMode } = useMode();
+
+ return (
+
+
+
+
}
+ />
+
MoonPay
+
+ PayPal, Debit Card, Apple Pay
+
+
+
div]:!m-0 [&>div]:!w-full [&>div]:!border-x-0 [&>div]:!border-none [&>div]:!p-0 sm:[&>div]:!rounded sm:[&>div]:!border-x',
+ '[&_iframe]:rounded-xl',
+ )}
+ >
+ {children}
+
+
+
+ );
+}
+
+export const MoonPayPanel = memo(function MoonPayPanel() {
+ const { address } = useAccount();
+ const showMoonPay = isOnrampServiceEnabled('moonpay');
+
+ const handleGetSignature = useCallback(async (widgetUrl: string): Promise => {
+ const response = await fetch(`${getAPIBaseUrl()}/api/moonpay`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ url: widgetUrl }),
+ });
+ const { signature } = await response.json();
+ return signature;
+ }, []);
+
+ if (!showMoonPay) {
+ return null;
+ }
+
+ const MoonPayBuyWidget = dynamic(
+ () => import('@moonpay/moonpay-react').then((mod) => mod.MoonPayBuyWidget),
+ {
+ ssr: false,
+ },
+ );
+
+ return (
+
+
+
+ );
+});
diff --git a/packages/arb-token-bridge-ui/src/components/BuyPanel/utils.ts b/packages/arb-token-bridge-ui/src/components/BuyPanel/utils.ts
new file mode 100644
index 000000000..a50d538eb
--- /dev/null
+++ b/packages/arb-token-bridge-ui/src/components/BuyPanel/utils.ts
@@ -0,0 +1,67 @@
+import { ChainId } from '@/bridge/types/ChainId';
+
+export const onrampServices = [
+ {
+ name: 'Transak',
+ slug: 'transak',
+ logo: '/images/onramp/transak.webp',
+ link: 'https://global.transak.com',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+ {
+ name: 'Ramp',
+ slug: 'ramp',
+ logo: '/images/onramp/ramp.webp',
+ link: 'https://ramp.network/buy',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+ {
+ name: 'Mt Pelerin',
+ slug: 'mt-pelerin',
+ logo: '/images/onramp/mt_pelerin.webp',
+ link: 'https://www.mtpelerin.com/buy-crypto',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+ {
+ name: 'Coinbase Pay',
+ slug: 'coinbase-pay',
+ logo: '/images/onramp/coinbase.webp',
+ link: 'https://login.coinbase.com/signin?client_id=258660e1-9cfe-4202-9eda-d3beedb3e118&oauth_challenge=851bae2a-c907-413d-9a12-71c1dfaa5d4f',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+ {
+ name: 'Onramp',
+ slug: 'onramp',
+ logo: '/images/onramp/onramp.webp',
+ link: 'https://onramp.money',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+ {
+ name: 'Banxa',
+ slug: 'banxa',
+ logo: '/images/onramp/banxa.webp',
+ link: 'https://checkout.banxa.com',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+ {
+ name: 'Simplex',
+ slug: 'simplex',
+ logo: '/images/onramp/simplex.webp',
+ link: 'https://buy.simplex.com',
+ chains: [ChainId.Ethereum],
+ },
+ {
+ name: 'Kado',
+ slug: 'kado',
+ logo: '/images/onramp/kado.webp',
+ link: 'https://swapped.com/',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+ {
+ name: 'Alchemy Pay',
+ slug: 'alchemy-pay',
+ logo: '/images/onramp/alchemy_pay.webp',
+ link: 'https://ramp.alchemypay.org/#/index',
+ chains: [ChainId.Ethereum, ChainId.ArbitrumOne],
+ },
+] as const;
diff --git a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx
index 9db18eaaf..c27f95b2f 100644
--- a/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx
+++ b/packages/arb-token-bridge-ui/src/components/MainContent/MainContent.tsx
@@ -3,12 +3,12 @@ import { useLocalStorage } from '@uidotdev/usehooks';
import { usePathname } from 'next/navigation';
import { Fragment, useMemo } from 'react';
-import { PathnameEnum } from '@/bridge/constants';
+import { isBridgeBuyOrSubpages } from '@/bridge/util/pathnameUtils';
import { isOnrampFeatureEnabled } from '@/bridge/util/queryParamUtils';
import { useArbQueryParams } from '../../hooks/useArbQueryParams';
import { useMode } from '../../hooks/useMode';
-import { BuyPanel } from '../BuyPanel';
+import { BuyPanel } from '../BuyPanel/BuyPanel';
import { RecoverFunds } from '../RecoverFunds';
import { TopNavBar } from '../TopNavBar';
import { TransactionHistory } from '../TransactionHistory/TransactionHistory';
@@ -28,7 +28,7 @@ export function MainContent() {
// `tab` from useArbQueryParams will never be 0 when showBuyPanel is true
// because we use /buy and don't use ?tab=buy
// so we need to hardcode to return 0 rather than `tab`
- if (pathname === PathnameEnum.BUY) {
+ if (isBridgeBuyOrSubpages(pathname)) {
return 0;
}
return tab;
@@ -52,7 +52,7 @@ export function MainContent() {
{}}>
-
+
{showBuyPanel && (
diff --git a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx
index 854669f6f..b29daff18 100644
--- a/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx
+++ b/packages/arb-token-bridge-ui/src/components/TopNavBar.tsx
@@ -9,6 +9,7 @@ import { twMerge } from 'tailwind-merge';
import { PathnameEnum } from '../constants';
import { useArbQueryParams } from '../hooks/useArbQueryParams';
import { useMode } from '../hooks/useMode';
+import { isBridgeBuyOrSubpages } from '../util/pathnameUtils';
import { TabParamEnum, isOnrampFeatureEnabled } from '../util/queryParamUtils';
import { useTransactionReminderInfo } from './TransactionHistory/useTransactionReminderInfo';
@@ -24,7 +25,7 @@ function StyledTab({
hrefQuery?: string;
}>) {
const pathname = usePathname();
- const isBuyTab = pathname === PathnameEnum.BUY;
+ const isBuyTab = isBridgeBuyOrSubpages(pathname);
const { embedMode } = useMode();
return (
@@ -56,7 +57,7 @@ export function TopNavBar() {
const showBuyPanel = isOnrampFeatureEnabled({ disabledFeatures });
const { embedMode } = useMode();
const pathname = usePathname();
- const isBuyTab = pathname === PathnameEnum.BUY;
+ const isBuyTab = isBridgeBuyOrSubpages(pathname);
const searchParams = useSearchParams();
const searchParamsWithoutTab = useMemo(() => {
diff --git a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx
index 1f990f0df..957ac1705 100644
--- a/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx
+++ b/packages/arb-token-bridge-ui/src/components/TransferPanel/TransferPanel.tsx
@@ -19,10 +19,11 @@ import { useTransactionHistory } from '@/bridge/hooks/useTransactionHistory';
import { BridgeTransfer, TransferOverrides } from '@/bridge/token-bridge-sdk/BridgeTransferStarter';
import { BridgeTransferStarterFactory } from '@/bridge/token-bridge-sdk/BridgeTransferStarterFactory';
import { CctpTransferStarter } from '@/bridge/token-bridge-sdk/CctpTransferStarter';
+import { isEmbeddedBridgeBuyOrSubpages } from '@/bridge/util/pathnameUtils';
import { LifiTransferStarter } from '@/token-bridge-sdk/LifiTransferStarter';
import { getTokenOverride } from '../../app/api/crosschain-transfers/utils';
-import { DOCS_DOMAIN, GET_HELP_LINK, PathnameEnum } from '../../constants';
+import { DOCS_DOMAIN, GET_HELP_LINK } from '../../constants';
import { useIsBatchTransferSupported } from '../../hooks/TransferPanel/useIsBatchTransferSupported';
import { useSetInputAmount } from '../../hooks/TransferPanel/useSetInputAmount';
import { useAccountType } from '../../hooks/useAccountType';
@@ -1259,7 +1260,7 @@ export function TransferPanel() {
};
if (embedMode) {
- if (pathname === PathnameEnum.EMBED_BUY && showBuyPanel) {
+ if (isEmbeddedBridgeBuyOrSubpages(pathname) && showBuyPanel) {
return ;
}
diff --git a/packages/arb-token-bridge-ui/src/components/Widget/WidgetBuyPanel.tsx b/packages/arb-token-bridge-ui/src/components/Widget/WidgetBuyPanel.tsx
index a51e4ef95..9426afb7a 100644
--- a/packages/arb-token-bridge-ui/src/components/Widget/WidgetBuyPanel.tsx
+++ b/packages/arb-token-bridge-ui/src/components/Widget/WidgetBuyPanel.tsx
@@ -1,6 +1,6 @@
import { twMerge } from 'tailwind-merge';
-import { BuyPanel } from '../BuyPanel';
+import { BuyPanel } from '../BuyPanel/BuyPanel';
import { DialogProps, DialogWrapper, OpenDialogFunction } from '../common/Dialog2';
import { WidgetHeaderRow } from './WidgetHeaderRow';
diff --git a/packages/arb-token-bridge-ui/src/components/Widget/WidgetModeDropdown.tsx b/packages/arb-token-bridge-ui/src/components/Widget/WidgetModeDropdown.tsx
index d5a40e29f..28d805f2c 100644
--- a/packages/arb-token-bridge-ui/src/components/Widget/WidgetModeDropdown.tsx
+++ b/packages/arb-token-bridge-ui/src/components/Widget/WidgetModeDropdown.tsx
@@ -13,6 +13,7 @@ import { useSearchParams } from 'next/navigation';
import { twMerge } from 'tailwind-merge';
import { PathnameEnum } from '@/bridge/constants';
+import { isEmbeddedBridgeBuyOrSubpages } from '@/bridge/util/pathnameUtils';
import { useArbQueryParams } from '../../hooks/useArbQueryParams';
import { isOnrampFeatureEnabled } from '../../util/queryParamUtils';
@@ -45,7 +46,7 @@ export const WidgetModeDropdown = () => {
const router = useRouter();
const searchParams = useSearchParams();
const pathname = usePathname();
- const isBuyTab = pathname === PathnameEnum.EMBED_BUY;
+ const isBuyTab = isEmbeddedBridgeBuyOrSubpages(pathname);
const isBridgeTab = pathname === PathnameEnum.EMBED;
const [{ disabledFeatures }] = useArbQueryParams();
diff --git a/packages/arb-token-bridge-ui/src/components/common/Button.tsx b/packages/arb-token-bridge-ui/src/components/common/Button.tsx
index e040b84d0..2734b700e 100644
--- a/packages/arb-token-bridge-ui/src/components/common/Button.tsx
+++ b/packages/arb-token-bridge-ui/src/components/common/Button.tsx
@@ -1,4 +1,4 @@
-import { ArrowRightIcon } from '@heroicons/react/24/outline';
+import { ChevronRightIcon } from '@heroicons/react/24/outline';
import React, { forwardRef, useState } from 'react';
import { twMerge } from 'tailwind-merge';
@@ -74,9 +74,9 @@ export const Button = forwardRef(
{children}
{props.showArrow && (
-