Skip to content
Closed
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 app/src/app/bridge/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export default async function BridgePage({ searchParams }: { searchParams: Promi
<h2 id="bridge-know">Good to know</h2>
<ul className={styles.points}>
<li>Elysium sends USDC only. Its gas token, HYPE, can be received there but not sent, so receive a little HYPE first if you have none to pay gas on Elysium.</li>
<li>Funds on an exchange? The panel&apos;s exchange guide shows where to withdraw them (Base, Ethereum, Arbitrum or Optimism), watches for them to land, then opens the bridge on that route.</li>
<li>Funds go from your wallet to Relay&apos;s deposit contract, or to Circle&apos;s transfer contract on a Circle route. They never pass through an openlaunch wallet or contract.</li>
<li>Relay and Circle are third parties. openlaunch operates neither and cannot reverse a transfer.</li>
<li>A transfer is saved in this browser while it is in progress. Reopen the bridge with the same wallet to pick it up.</li>
Expand Down
27 changes: 24 additions & 3 deletions app/src/app/ui-review-bridge/BridgeReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import { useEffect, useState } from "react";
import { Dialog } from "@base-ui/react/dialog";
import { X } from "lucide-react";
import { BridgeForm, Transfer } from "@/components/bridge/BridgeDialog";
import { BridgeForm, ExchangeGuide, Transfer } from "@/components/bridge/BridgeDialog";
import { EXCHANGE_IDLE, type ExchangeView } from "@/components/bridge/useExchangeWatch";
import signal from "@/components/bridge/Signal.module.css";
import type useBridge from "@/components/bridge/useBridge";
import { formatUnits, parseEther, parseUnits, zeroAddress } from "viem";
import { canReverseBridgeRoute, changeBridgeRoute, parseBridgeAmount, type BridgeRouteChange, type BridgeRouteInputs } from "@/lib/bridge/client";
Expand All @@ -16,7 +18,7 @@ import { HistoryBoard } from "@/components/bridge/BridgeHistory";
import type { BridgeHistoryEntry } from "@/lib/bridge/history";
import { FundCard } from "@/components/bridge/BridgeToBuy";
import type { BridgeHolding, FundStep } from "@/lib/bridge/fund";
import type { BridgeAsset, BridgeDestinationId } from "@/lib/bridge/types";
import type { BridgeAsset, BridgeChainId, BridgeDestinationId } from "@/lib/bridge/types";
import { CCTP_TOKEN_MESSENGER, cctpRoute } from "@/lib/bridge/cctp";

const wallet = "0x03508bB71268BBA25ECaCC8F620e01866650532c" as const;
Expand Down Expand Up @@ -58,6 +60,20 @@ const FUND_SCENES: FundScene[] = [
{ name: "Elysium HYPE, arrived", step: "arrived", destination: 99901, asset: "HYPE", symbol: "KINO", row: HISTORY_ROWS[6], shortBy: null, suggestion: null, searching: false },
];

// "From an exchange", one guide per state it can show. Arrivals are synthetic, in real units.
type ExchangeScene = { name: string; address: typeof wallet | undefined; destinationChainId: BridgeChainId; view: ExchangeView };
const watching: ExchangeView = { checking: false, unreadable: false, arrivals: [] };
const EXCHANGE_SCENES: ExchangeScene[] = [
{ name: "no wallet", address: undefined, destinationChainId: 4663, view: EXCHANGE_IDLE },
{ name: "watching", address: wallet, destinationChainId: 4663, view: watching },
{ name: "balances unreadable", address: wallet, destinationChainId: 4663, view: { ...watching, unreadable: true } },
{ name: "USDC landed, no gas", address: wallet, destinationChainId: 5042, view: { ...watching, arrivals: [{ chainId: 42161, asset: "USDC", amount: 250_000_000n, decimals: 6, gas: 0n }] } },
{ name: "USDC and ETH landed", address: wallet, destinationChainId: 99901, view: { ...watching, arrivals: [{ chainId: 42161, asset: "USDC", amount: 250_000_000n, decimals: 6, gas: 4_000_000_000_000_000n }, { chainId: 42161, asset: "ETH", amount: 4_000_000_000_000_000n, decimals: 18, gas: null }] } },
{ name: "landed on Base, wanted on Base", address: wallet, destinationChainId: 8453, view: { ...watching, arrivals: [{ chainId: 8453, asset: "USDC", amount: 100_000_000n, decimals: 6, gas: 2_000_000_000_000_000n }] } },
];
// the dialog's guide: 25 USDC on Arbitrum with no ETH beside it, so its button opens the bridge on that route
const DIALOG_EXCHANGE: ExchangeView = { ...watching, arrivals: [{ chainId: 42161, asset: "USDC", amount: 25_000_000n, decimals: 6, gas: 0n }] };

export default function BridgeReview() {
const [historyScene, setHistoryScene] = useState<HistoryScene>("rows");
const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -134,7 +150,7 @@ export default function BridgeReview() {
<Dialog.Portal><Dialog.Backdrop className={styles.backdrop} /><Dialog.Popup className={styles.panel} initialFocus={false}>
<div className={styles.heading}><Dialog.Title className={styles.title}>Bridge</Dialog.Title><Dialog.Close className={styles.close} aria-label="Close preview"><X size={19} /></Dialog.Close></div>
<Dialog.Description className={styles.description}>Preview only · {scene} · no funds move</Dialog.Description>
{tracking ? <Transfer bridge={bridge} /> : <BridgeForm bridge={bridge} connect={() => setScene("quote")} />}
{tracking ? <Transfer bridge={bridge} /> : <BridgeForm bridge={bridge} connect={() => setScene("quote")} exchangeView={DIALOG_EXCHANGE} />}
<div className={styles.footer}><span>{viaCircle ? "Powered by Circle CCTP" : "Powered by Relay"}</span><span>0 Openlaunch fee</span></div>
</Dialog.Popup></Dialog.Portal>
</Dialog.Root>
Expand All @@ -147,6 +163,11 @@ export default function BridgeReview() {
<div className="mt-6 grid gap-6 [grid-template-columns:repeat(auto-fill,minmax(min(100%,340px),1fr))]">
{FUND_SCENES.map(({ name, ...scene }) => <figure key={name} className="m-0 grid min-w-0 content-start gap-2"><figcaption className="text-xs text-muted">{name}</figcaption><FundCard {...scene} onBridge={() => {}} onOpen={() => {}} onDismiss={() => {}} /></figure>)}
</div>
<h2 className="mt-16 text-lg font-semibold">From an exchange</h2>
<p className="mt-2 text-sm text-body">The bridge panel&apos;s guide, at panel width. No balances are read here and buttons do nothing.</p>
<div className="mt-6 grid gap-6 [grid-template-columns:repeat(auto-fill,minmax(min(100%,400px),1fr))]">
{EXCHANGE_SCENES.map(({ name, ...scene }) => <figure key={name} className="m-0 grid min-w-0 content-start gap-2"><figcaption className="text-xs text-muted">{name}</figcaption><div className={signal.pagePanel}><ExchangeGuide {...scene} onBridge={() => {}} onBack={() => {}} onConnect={() => {}} /></div></figure>)}
</div>
</div>
</>
);
Expand Down
26 changes: 25 additions & 1 deletion app/src/components/bridge/BridgeDialog.module.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading