diff --git a/next.config.js b/next.config.js index e7bc5857..2f1338fa 100644 --- a/next.config.js +++ b/next.config.js @@ -56,6 +56,15 @@ let nextConfig = { "uuid", ] : [], + images: { + dangerouslyAllowSVG: true, + remotePatterns: [ + { + protocol: "https", + hostname: "**", + }, + ], + }, webpack: (config, { dev, isServer }) => { if (dev && isServer) checkEnv(); return config; diff --git a/public/logo-fallback.png b/public/logo-fallback.png new file mode 100644 index 00000000..e21e57c2 Binary files /dev/null and b/public/logo-fallback.png differ diff --git a/src/components/AssetSelect/AssetSelectContent.tsx b/src/components/AssetSelect/AssetSelectContent.tsx index 44a2f0ba..b3466e79 100644 --- a/src/components/AssetSelect/AssetSelectContent.tsx +++ b/src/components/AssetSelect/AssetSelectContent.tsx @@ -2,6 +2,7 @@ import { ArrowLeftIcon } from "@heroicons/react/20/solid"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import { Asset } from "@skip-router/core"; import { matchSorter } from "match-sorter"; +import Image from "next/image"; import { useEffect, useMemo, useRef, useState } from "react"; import { formatUnits } from "viem"; @@ -93,10 +94,12 @@ function AssetSelectContent({ assets = [], balances, onChange, onClose, showChai className="flex w-full items-center gap-4 rounded-xl p-4 text-left transition-colors hover:bg-[#ECD9D9] focus:-outline-offset-2" onClick={() => (onClose(), onChange?.(asset))} > - {asset.recommendedSymbol} (e.currentTarget.src = "https://api.dicebear.com/6.x/shapes/svg")} />
diff --git a/src/components/AssetSelect/index.tsx b/src/components/AssetSelect/index.tsx index b7ca3716..e4026666 100644 --- a/src/components/AssetSelect/index.tsx +++ b/src/components/AssetSelect/index.tsx @@ -1,5 +1,6 @@ import { ChevronDownIcon } from "@heroicons/react/20/solid"; import { Asset } from "@skip-router/core"; +import Image from "next/image"; import { useState } from "react"; import { Dialog, DialogContent, DialogTrigger } from "@/components/Dialog"; @@ -35,10 +36,12 @@ function AssetSelect({ asset, assets, balances, onChange, showChainInfo, isBalan data-testid="select-asset" > {asset && ( - {asset.recommendedSymbol} (event.currentTarget.src = "https://api.dicebear.com/6.x/shapes/svg")} /> )} diff --git a/src/components/ChainSelect/ChainSelectContent.tsx b/src/components/ChainSelect/ChainSelectContent.tsx index 991334b3..8ab80080 100644 --- a/src/components/ChainSelect/ChainSelectContent.tsx +++ b/src/components/ChainSelect/ChainSelectContent.tsx @@ -1,6 +1,7 @@ import { ArrowLeftIcon } from "@heroicons/react/20/solid"; import * as ScrollArea from "@radix-ui/react-scroll-area"; import { matchSorter } from "match-sorter"; +import Image from "next/image"; import { useEffect, useMemo, useRef, useState } from "react"; import { Chain } from "@/hooks/useChains"; @@ -86,12 +87,14 @@ function ChainSelectContent({ chains, onChange, onClose }: Props) { onClick={() => onChange(chain)} data-testid="chain-item" > - {chain.prettyName} (e.currentTarget.src = "https://api.dicebear.com/6.x/shapes/svg")} + className="h-[48px] w-[48px] rounded-full object-contain" + width={48} + height={48} + src={chain.logoURI || "/logo-fallback.png"} /> +

{chain.prettyName}

{chain.chainID}

diff --git a/src/components/ConnectedWalletButton.tsx b/src/components/ConnectedWalletButton.tsx index ae91a72a..3f7f6e71 100644 --- a/src/components/ConnectedWalletButton.tsx +++ b/src/components/ConnectedWalletButton.tsx @@ -1,3 +1,4 @@ +import Image from "next/image"; import { ComponentProps, forwardRef } from "react"; import { cn } from "@/utils/ui"; @@ -22,9 +23,11 @@ export const ConnectedWalletButton = forwardRef( ref={ref} > {walletLogo && ( - {walletName} )} diff --git a/src/components/RouteDisplay/RouteEnd.tsx b/src/components/RouteDisplay/RouteEnd.tsx index 5c71c856..68849d4a 100644 --- a/src/components/RouteDisplay/RouteEnd.tsx +++ b/src/components/RouteDisplay/RouteEnd.tsx @@ -1,3 +1,5 @@ +import Image from "next/image"; + import { SimpleTooltip } from "../SimpleTooltip"; export interface RouteEndProps { @@ -10,9 +12,11 @@ export interface RouteEndProps { export const RouteEnd = ({ amount, symbol, logo, chain }: RouteEndProps) => { return (
-
- + {chain} diff --git a/src/components/RouteDisplay/SwapStep.tsx b/src/components/RouteDisplay/SwapStep.tsx index 1f977ae8..bd037edb 100644 --- a/src/components/RouteDisplay/SwapStep.tsx +++ b/src/components/RouteDisplay/SwapStep.tsx @@ -1,4 +1,5 @@ import { SwapVenue } from "@skip-router/core"; +import Image from "next/image"; import { useMemo } from "react"; import { SWAP_VENUES } from "@/constants/swap-venues"; @@ -97,19 +98,23 @@ export const SwapStep = ({ action, actions, statusData }: SwapStepProps) => { Swap to - {assetOut.name} {assetOut.recommendedSymbol} on - {action.venue.name} @@ -138,16 +143,18 @@ export const SwapStep = ({ action, actions, statusData }: SwapStepProps) => { Swap - {assetIn.name} {assetIn.recommendedSymbol} on - {action.venue.name} { Swap - {assetIn.name} + {assetIn.recommendedSymbol} for - {assetOut.name} {assetOut.recommendedSymbol} on - {action.venue.name} {venue.prettyName} diff --git a/src/components/RouteDisplay/TransferStep.tsx b/src/components/RouteDisplay/TransferStep.tsx index ceeb1197..6d5c4db8 100644 --- a/src/components/RouteDisplay/TransferStep.tsx +++ b/src/components/RouteDisplay/TransferStep.tsx @@ -1,4 +1,5 @@ import { BridgeType } from "@skip-router/core"; +import Image from "next/image"; import { useMemo } from "react"; import { useAssets } from "@/context/assets"; @@ -101,9 +102,11 @@ export const TransferStep = ({ action, actions, statusData }: TransferStepProps) Transfer from - {sourceChain.prettyName} @@ -113,9 +116,11 @@ export const TransferStep = ({ action, actions, statusData }: TransferStepProps) to - {destinationChain.prettyName} @@ -126,8 +131,10 @@ export const TransferStep = ({ action, actions, statusData }: TransferStepProps) with {bridge.name.toLowerCase() !== "ibc" && ( - {bridge.name} Transfer - {asset.name} {asset.recommendedSymbol} from - {sourceChain.prettyName} @@ -185,9 +196,11 @@ export const TransferStep = ({ action, actions, statusData }: TransferStepProps) to - {destinationChain.prettyName} @@ -198,8 +211,10 @@ export const TransferStep = ({ action, actions, statusData }: TransferStepProps) with {bridge.name.toLowerCase() !== "ibc" && ( - {bridge.name} { + for (const op of route.operations) { + if ("hyperlaneTransfer" in op) return op; + } + }, [route]); const bridgingFee = useMemo(() => { - if (!axelarTransferOperation) return; - const { feeAmount, feeAsset, usdFeeAmount } = axelarTransferOperation.axelarTransfer; - const computed = (+feeAmount / Math.pow(10, feeAsset.decimals || 18)).toLocaleString("en-US", { - maximumFractionDigits: 6, - }); + if (hyperlaneTransferOperation) { + const { feeAmount, feeAsset, usdFeeAmount } = hyperlaneTransferOperation.hyperlaneTransfer; + const computed = (+feeAmount / Math.pow(10, feeAsset.decimals || 6)).toLocaleString("en-US", { + maximumFractionDigits: 6, + }); + return { inAsset: `${computed} ${feeAsset.symbol}`, inUSD: usdFeeAmount && `${formatUSD(usdFeeAmount)}` }; + } + if (axelarTransferOperation) { + const { feeAmount, feeAsset, usdFeeAmount } = axelarTransferOperation.axelarTransfer; + const computed = (+feeAmount / Math.pow(10, feeAsset.decimals || 18)).toLocaleString("en-US", { + maximumFractionDigits: 6, + }); - return { inAsset: `${computed} ${feeAsset.symbol}`, inUSD: `${formatUSD(usdFeeAmount)}` }; - }, [axelarTransferOperation]); + return { inAsset: `${computed} ${feeAsset.symbol}`, inUSD: `${formatUSD(usdFeeAmount)}` }; + } + }, [axelarTransferOperation, hyperlaneTransferOperation]); if (!(sourceChain && sourceAsset && destinationChain && destinationAsset)) { return null; diff --git a/src/components/WalletModal/WalletModal.tsx b/src/components/WalletModal/WalletModal.tsx index c120d9ec..51ca1373 100644 --- a/src/components/WalletModal/WalletModal.tsx +++ b/src/components/WalletModal/WalletModal.tsx @@ -2,6 +2,7 @@ import { useManager } from "@cosmos-kit/react"; import { ArrowTopRightOnSquareIcon } from "@heroicons/react/16/solid"; import { ArrowLeftIcon, FaceFrownIcon } from "@heroicons/react/20/solid"; import * as ScrollArea from "@radix-ui/react-scroll-area"; +import Image from "next/image"; import toast from "react-hot-toast"; import { useAccount, useConnect, useDisconnect } from "wagmi"; @@ -104,9 +105,11 @@ export function WalletModal({ chainType, onClose, wallets }: Props) { onClick={() => onWalletConnect(wallet)} > {wallet.walletInfo.logo && ( - {wallet.walletPrettyName} = { - assetmantle: "asset-mantle", - cosmoshub: "cosmos", - cryptoorgchain: "crypto-org", - fetchhub: "fetchai", - gravitybridge: "gravity-bridge", - kichain: "ki-chain", - mars: "mars-protocol", - omniflixhub: "omniflix", - terra2: "terra", -}; diff --git a/src/hooks/useChains.ts b/src/hooks/useChains.ts index 8796c0ad..8bb81f53 100644 --- a/src/hooks/useChains.ts +++ b/src/hooks/useChains.ts @@ -4,7 +4,6 @@ import { useQuery } from "@tanstack/react-query"; import { chainIdToName } from "@/chains"; import { chainIdToPrettyName } from "@/chains/pretty"; import { useSkipClient } from "@/solve"; -import { getChainLogo } from "@/utils/chain"; export type Chain = SkipChain & { prettyName: string; @@ -33,7 +32,7 @@ export function useChains(args: UseChainsQueryArgs = {}) { ...chain, chainName: chainIdToName[chain.chainID] || chain.chainName, prettyName: chainIdToPrettyName[chain.chainID] || chain.chainName, - logoURI: getChainLogo(chain), + logoURI: chain.logoURI || "/logo-fallback.png", }; }) .sort((chainA, chainB) => { diff --git a/src/pages/api/chainlist/[chainName].ts b/src/pages/api/chainlist/[chainName].ts deleted file mode 100644 index 40457783..00000000 --- a/src/pages/api/chainlist/[chainName].ts +++ /dev/null @@ -1,24 +0,0 @@ -import { PageConfig } from "next"; -import { NextRequest } from "next/server"; - -import { getChainlistURI } from "@/utils/chain"; - -export const config: PageConfig = { - runtime: "edge", -}; - -export default async function handler(req: NextRequest) { - const chainName = req.nextUrl.searchParams.get("chainName") || ""; - let response = await fetch(chainName ? getChainlistURI(chainName) : FALLBACK_URI); - if (!response.ok) { - response = await fetch(FALLBACK_URI); - } - return new Response(response.body, { - headers: { - "cache-control": "public, max-age=604800, immutable", // 1 week - "content-type": response.headers.get("content-type")!, - }, - }); -} - -const FALLBACK_URI = "https://api.dicebear.com/6.x/shapes/svg"; diff --git a/src/utils/chain.ts b/src/utils/chain.ts index 64fced4f..6e4989d7 100644 --- a/src/utils/chain.ts +++ b/src/utils/chain.ts @@ -1,27 +1,4 @@ -import { Chain, FeeAsset } from "@skip-router/core"; - -import { CHAIN_NAME_TO_CHAINLIST_ID, CHAINLIST_LOGO_CHAIN_IDS } from "@/constants/chainlist"; - -export function getChainLogo(chain: Chain) { - if (chain.logoURI && chain.logoURI.match("-light")) { - return chain.logoURI.replace("-light", "-dark"); - } - - if (CHAINLIST_LOGO_CHAIN_IDS.includes(chain.chainID)) { - return getChainlistProxyURI(chain.chainName); - } - - return chain.logoURI || getChainlistProxyURI(chain.chainName); -} - -export function getChainlistURI(chainName: string) { - const name = CHAIN_NAME_TO_CHAINLIST_ID[chainName] || chainName; - return `https://raw.githubusercontent.com/cosmostation/chainlist/main/chain/${name}/chainImg/_chainImg.svg`; -} - -export function getChainlistProxyURI(chainName: string) { - return `/api/chainlist/${chainName}`; -} +import { FeeAsset } from "@skip-router/core"; /** * - deprio denoms start with 'ibc/' and 'factory/'