Skip to content
Open
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
21 changes: 18 additions & 3 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
SupportedChainId,
SUPPORTED_CHAINS,
CHAIN_CONFIGS,
supportsFastTransfer,
} from "@/lib/chains";
import { ProgressSteps } from "@/components/progress-step";
import { TransferLog } from "@/components/transfer-log";
Expand Down Expand Up @@ -69,7 +70,9 @@ export default function Home() {
const [elapsedSeconds, setElapsedSeconds] = useState(0);
const [isTransferring, setIsTransferring] = useState(false);
const [showFinalTime, setShowFinalTime] = useState(false);
const [transferType, setTransferType] = useState<"fast" | "standard">("fast");
const [transferType, setTransferType] = useState<"fast" | "standard">(
"standard",
);
const [balance, setBalance] = useState("0");
const [wallets, setWallets] = useState<WalletConnections>({
evm: null,
Expand All @@ -90,6 +93,7 @@ export default function Home() {

const sourceEcosystem = CHAIN_CONFIGS[sourceChain].ecosystem;
const destinationEcosystem = CHAIN_CONFIGS[destinationChain].ecosystem;
const fastTransferSupported = supportsFastTransfer(sourceChain);
const needsEvmWallet =
sourceEcosystem === "evm" || destinationEcosystem === "evm";
const needsSolanaWallet =
Expand Down Expand Up @@ -129,6 +133,14 @@ export default function Home() {
setElapsedSeconds(0);
};

const handleSourceChainChange = (value: string) => {
const chainId = Number(value) as SupportedChainId;
setSourceChain(chainId);
if (!supportsFastTransfer(chainId)) {
setTransferType("standard");
}
};

const handleEvmWalletClick = async () => {
if (wallets.evm) {
setWallets((current) => ({ ...current, evm: null }));
Expand Down Expand Up @@ -367,9 +379,12 @@ export default function Home() {
<TransferTypeSelector
value={transferType}
onChange={setTransferType}
fastDisabled={!fastTransferSupported}
/>
<p className="text-sm text-muted-foreground">
{transferType === "fast"
{!fastTransferSupported
? `Fast Transfer is not available from ${CHAIN_CONFIGS[sourceChain].name}.`
: transferType === "fast"
? "Faster transfers with lower finality threshold (1000 blocks)"
: "Standard transfers with higher finality (2000 blocks)"}
</p>
Expand All @@ -379,7 +394,7 @@ export default function Home() {
<Label>Source Chain</Label>
<Select
value={String(sourceChain)}
onValueChange={(value) => setSourceChain(Number(value))}
onValueChange={handleSourceChainChange}
>
<SelectTrigger>
<SelectValue placeholder="Select source chain" />
Expand Down
8 changes: 6 additions & 2 deletions src/components/transfer-type.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,20 @@ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
export function TransferTypeSelector({
value,
onChange,
fastDisabled = false,
}: {
value: "fast" | "standard";
onChange: (value: "fast" | "standard") => void;
fastDisabled?: boolean;
}) {
return (
<Tabs value={value} onValueChange={(v) => onChange(v as "fast" | "standard")}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="fast">Fast Transfer</TabsTrigger>
<TabsTrigger value="fast" disabled={fastDisabled}>
Fast Transfer
</TabsTrigger>
<TabsTrigger value="standard">Standard Transfer</TabsTrigger>
</TabsList>
</Tabs>
);
}
}
18 changes: 16 additions & 2 deletions src/hooks/use-cross-chain-transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
CHAIN_CONFIGS,
SOLANA_RPC_ENDPOINT,
IRIS_API_URL,
supportsFastTransfer,
} from "@/lib/chains";
import {
ensureEvmChain,
Expand All @@ -72,6 +73,7 @@ interface AttestationResponse {
}

interface FastTransferFeeResponse {
finalityThreshold: number;
minimumFee: number | string;
}

Expand Down Expand Up @@ -111,6 +113,15 @@ export function useCrossChainTransfer() {
const destinationEcosystem =
CHAIN_CONFIGS[destinationChainId as SupportedChainId].ecosystem;

if (
transferType === "fast" &&
!supportsFastTransfer(sourceChainId as SupportedChainId)
) {
throw new Error(
`Fast Transfer is not available from ${CHAIN_CONFIGS[sourceChainId as SupportedChainId].name}.`,
);
}

const sourceClient = getClients(sourceChainId, wallets);
const destinationClient = getClients(destinationChainId, wallets);
const defaultDestination = getDestinationAddress(
Expand Down Expand Up @@ -1024,9 +1035,12 @@ export function useCrossChainTransfer() {
}

const feePayload = (await response.json()) as FastTransferFeeResponse[];
const feeEntry = feePayload[0];
const feeEntry = feePayload.find(
({ finalityThreshold }) =>
finalityThreshold === FAST_FINALITY_THRESHOLD,
);
if (!feeEntry) {
throw new Error("No fee returned for this route");
throw new Error("No Fast Transfer fee returned for this route");
}

const minimumFeeBpsHundredths = parseFeeBps(feeEntry.minimumFee);
Expand Down
20 changes: 20 additions & 0 deletions src/lib/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,26 @@ export const CHAIN_CONFIGS: Record<SupportedChainId, ChainConfig> = {
export const SUPPORTED_CHAINS = (Object.keys(CHAIN_CONFIGS).map(Number) as SupportedChainId[])
.sort((a, b) => CHAIN_CONFIGS[a].name.localeCompare(CHAIN_CONFIGS[b].name));

const FAST_TRANSFER_SOURCE_CHAINS = new Set<SupportedChainId>([
SupportedChainId.ARBITRUM_SEPOLIA,
SupportedChainId.BASE_SEPOLIA,
SupportedChainId.CODEX_TESTNET,
SupportedChainId.EDGE_TESTNET,
SupportedChainId.ETH_SEPOLIA,
SupportedChainId.INK_SEPOLIA,
SupportedChainId.LINEA_SEPOLIA,
SupportedChainId.MORPH_HOODI,
SupportedChainId.OPTIMISM_SEPOLIA,
SupportedChainId.PLUME_SEPOLIA,
SupportedChainId.SOLANA_DEVNET,
SupportedChainId.UNICHAIN_SEPOLIA,
SupportedChainId.WORLDCHAIN_SEPOLIA,
]);

export function supportsFastTransfer(chainId: SupportedChainId) {
return FAST_TRANSFER_SOURCE_CHAINS.has(chainId);
}

export const SOLANA_RPC_ENDPOINT = "https://api.devnet.solana.com";

export const IRIS_API_URL = "https://iris-api-sandbox.circle.com";
Expand Down