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
38 changes: 0 additions & 38 deletions src/assets/arbitrum-logo.svg

This file was deleted.

3 changes: 0 additions & 3 deletions src/assets/base-logo.svg

This file was deleted.

1 change: 0 additions & 1 deletion src/assets/hardhat-logo.svg

This file was deleted.

1 change: 0 additions & 1 deletion src/assets/optimism-logo.svg

This file was deleted.

86 changes: 9 additions & 77 deletions src/components/common/NetworkBlockIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,9 @@
import { useContext, useEffect, useMemo, useState } from "react";
import { useLocation } from "react-router-dom";
// Network configuration with logos
import arbitrumLogo from "../../assets/arbitrum-logo.svg";
import baseLogo from "../../assets/base-logo.svg";
import hardhatLogo from "../../assets/hardhat-logo.svg";
import optimismLogo from "../../assets/optimism-logo.svg";
import { getRPCUrls } from "../../config/rpcConfig";
import { AppContext } from "../../context/AppContext";
import { AppContext, useNetworks } from "../../context/AppContext";
import { RPCClient } from "../../services/EVM/common/RPCClient";

interface NetworkInfo {
name: string;
logo: string | null;
color: string;
}

const NETWORK_INFO: Record<number, NetworkInfo> = {
1: { name: "Ethereum", logo: null, color: "#627EEA" },
11155111: { name: "Sepolia", logo: null, color: "#F0CDC2" },
42161: { name: "Arbitrum", logo: arbitrumLogo, color: "#28A0F0" },
10: { name: "Optimism", logo: optimismLogo, color: "#FF0420" },
8453: { name: "Base", logo: baseLogo, color: "#0052FF" },
56: { name: "BSC", logo: "bsc", color: "#F0B90B" },
97: { name: "BSC Testnet", logo: "bsc", color: "#F0B90B" },
137: { name: "Polygon", logo: "polygon", color: "#8247E5" },
31337: { name: "Localhost", logo: hardhatLogo, color: "#FFF100" },
};

// Ethereum SVG for networks without a logo
const EthereumIcon = ({ color }: { color: string }) => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <TODO>
<svg width="20" height="20" viewBox="0 0 256 417" xmlns="http://www.w3.org/2000/svg">
<path fill={color} d="m127.961 0-2.795 9.5v275.668l2.795 2.79 127.962-75.638z" />
<path fill={`${color}99`} d="M127.962 0 0 212.32l127.962 75.639V154.158z" />
<path fill={color} d="m127.961 312.187-1.575 1.92v98.199l1.575 4.6L256 236.587z" />
<path fill={`${color}99`} d="M127.962 416.905v-104.72L0 236.585z" />
</svg>
);

// BSC/BNB SVG icon
const BscIcon = ({ color, opacity = 1 }: { color: string; opacity?: number }) => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <TODO>
<svg width="20" height="20" viewBox="0 0 126.61 126.61" xmlns="http://www.w3.org/2000/svg">
<path
fill={color}
fillOpacity={opacity}
d="m38.73 53.2 24.59-24.58 24.6 24.6 14.3-14.31L63.32 0l-38.9 38.9zM0 63.31l14.3-14.31 14.31 14.31-14.31 14.3zM38.73 73.41l24.59 24.59 24.6-24.6 14.31 14.29-38.9 38.91-38.91-38.88zM97.99 63.31l14.3-14.31 14.32 14.31-14.31 14.3z"
/>
<path
fill={color}
fillOpacity={opacity}
d="m77.83 63.3-14.51-14.52-10.73 10.73-1.24 1.23-2.54 2.54 14.51 14.5 14.51-14.47z"
/>
</svg>
);

// Polygon SVG icon
const PolygonIcon = ({ color }: { color: string }) => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <TODO>
<svg width="20" height="20" viewBox="0 0 38 33" xmlns="http://www.w3.org/2000/svg">
<path
fill={color}
d="M29 10.2c-.7-.4-1.6-.4-2.4 0L21 13.5l-3.8 2.1-5.5 3.3c-.7.4-1.6.4-2.4 0L5 16.3c-.7-.4-1.2-1.2-1.2-2.1v-5c0-.8.4-1.6 1.2-2.1l4.3-2.5c.7-.4 1.6-.4 2.4 0L16 7.2c.7.4 1.2 1.2 1.2 2.1v3.3l3.8-2.2V7c0-.8-.4-1.6-1.2-2.1l-8-4.7c-.7-.4-1.6-.4-2.4 0L1.2 5C.4 5.4 0 6.2 0 7v9.4c0 .8.4 1.6 1.2 2.1l8.1 4.7c.7.4 1.6.4 2.4 0l5.5-3.2 3.8-2.2 5.5-3.2c.7-.4 1.6-.4 2.4 0l4.3 2.5c.7.4 1.2 1.2 1.2 2.1v5c0 .8-.4 1.6-1.2 2.1L29 29.5c-.7.4-1.6.4-2.4 0l-4.3-2.5c-.7-.4-1.2-1.2-1.2-2.1v-3.2l-3.8 2.2v3.3c0 .8.4 1.6 1.2 2.1l8.1 4.7c.7.4 1.6.4 2.4 0l8.1-4.7c.7-.4 1.2-1.2 1.2-2.1V18c0-.8-.4-1.6-1.2-2.1L29 10.2z"
/>
</svg>
);
import { NetworkIcon } from "./NetworkIcon";

interface NetworkBlockIndicatorProps {
className?: string;
Expand All @@ -73,6 +12,7 @@ interface NetworkBlockIndicatorProps {
export function NetworkBlockIndicator({ className }: NetworkBlockIndicatorProps) {
const location = useLocation();
const { rpcUrls } = useContext(AppContext);
const { getNetwork } = useNetworks();
const [blockNumber, setBlockNumber] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState(false);

Expand All @@ -84,7 +24,7 @@ export function NetworkBlockIndicator({ className }: NetworkBlockIndicatorProps)
: null;
}, [location.pathname]);

const networkInfo = chainId ? NETWORK_INFO[chainId] : null;
const network = chainId ? getNetwork(chainId) : undefined;

useEffect(() => {
if (!chainId) {
Expand Down Expand Up @@ -126,25 +66,17 @@ export function NetworkBlockIndicator({ className }: NetworkBlockIndicatorProps)
};
}, [chainId, rpcUrls]);

if (!chainId || !networkInfo) return null;
if (!chainId || !network) return null;

return (
<div
className={`network-block-indicator ${className || ""}`}
style={{ borderColor: `${networkInfo.color}40` }}
title={networkInfo.name}
style={{ borderColor: `${network.color}40` }}
title={network.name}
>
<div className="network-block-pulse" style={{ background: networkInfo.color }} />
<div className="network-block-pulse" style={{ background: network.color }} />
<div className="network-block-logo">
{networkInfo.logo === "bsc" ? (
<BscIcon color={networkInfo.color} />
) : networkInfo.logo === "polygon" ? (
<PolygonIcon color={networkInfo.color} />
) : networkInfo.logo ? (
<img src={networkInfo.logo} alt={networkInfo.name} />
) : (
<EthereumIcon color={networkInfo.color} />
)}
<NetworkIcon network={network} size={20} />
</div>
<span className="network-block-number">
{isLoading ? "..." : blockNumber !== null ? `#${blockNumber.toLocaleString()}` : "---"}
Expand Down
77 changes: 22 additions & 55 deletions src/components/common/NetworkIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import type React from "react";
import arbitrumLogo from "../../assets/arbitrum-logo.svg";
import baseLogo from "../../assets/base-logo.svg";
import hardhatLogo from "../../assets/hardhat-logo.svg";
import optimismLogo from "../../assets/optimism-logo.svg";
import type { NetworkConfig } from "../../config/networks";
import { useState } from "react";
import { getNetworkLogoUrl } from "../../services/MetadataService";
import type { NetworkConfig } from "../../types";

interface NetworkIconProps {
network: NetworkConfig;
size?: number;
}

// Ethereum SVG icon
// Fallback Ethereum SVG icon (used when logo fails to load)
const EthereumIcon: React.FC<{ color: string; size: number }> = ({ color, size }) => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <TODO>
<svg width={size} height={size} viewBox="0 0 256 417" xmlns="http://www.w3.org/2000/svg">
Expand All @@ -21,57 +19,26 @@ const EthereumIcon: React.FC<{ color: string; size: number }> = ({ color, size }
</svg>
);

// BSC/BNB SVG icon
const BscIcon: React.FC<{ color: string; size: number; opacity?: number }> = ({
color,
size,
opacity = 1,
}) => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <TODO>
<svg width={size} height={size} viewBox="0 0 126.61 126.61" xmlns="http://www.w3.org/2000/svg">
<path
fill={color}
fillOpacity={opacity}
d="m38.73 53.2 24.59-24.58 24.6 24.6 14.3-14.31L63.32 0l-38.9 38.9zM0 63.31l14.3-14.31 14.31 14.31-14.31 14.3zM38.73 73.41l24.59 24.59 24.6-24.6 14.31 14.29-38.9 38.91-38.91-38.88zM97.99 63.31l14.3-14.31 14.32 14.31-14.31 14.3z"
/>
<path
fill={color}
fillOpacity={opacity}
d="m77.83 63.3-14.51-14.52-10.73 10.73-1.24 1.23-2.54 2.54 14.51 14.5 14.51-14.47z"
/>
</svg>
);

// Polygon SVG icon
const PolygonIcon: React.FC<{ color: string; size: number }> = ({ color, size }) => (
// biome-ignore lint/a11y/noSvgWithoutTitle: <TODO>
<svg width={size} height={size} viewBox="0 0 38 33" xmlns="http://www.w3.org/2000/svg">
<path
fill={color}
d="M29 10.2c-.7-.4-1.6-.4-2.4 0L21 13.5l-3.8 2.1-5.5 3.3c-.7.4-1.6.4-2.4 0L5 16.3c-.7-.4-1.2-1.2-1.2-2.1v-5c0-.8.4-1.6 1.2-2.1l4.3-2.5c.7-.4 1.6-.4 2.4 0L16 7.2c.7.4 1.2 1.2 1.2 2.1v3.3l3.8-2.2V7c0-.8-.4-1.6-1.2-2.1l-8-4.7c-.7-.4-1.6-.4-2.4 0L1.2 5C.4 5.4 0 6.2 0 7v9.4c0 .8.4 1.6 1.2 2.1l8.1 4.7c.7.4 1.6.4 2.4 0l5.5-3.2 3.8-2.2 5.5-3.2c.7-.4 1.6-.4 2.4 0l4.3 2.5c.7.4 1.2 1.2 1.2 2.1v5c0 .8-.4 1.6-1.2 2.1L29 29.5c-.7.4-1.6.4-2.4 0l-4.3-2.5c-.7-.4-1.2-1.2-1.2-2.1v-3.2l-3.8 2.2v3.3c0 .8.4 1.6 1.2 2.1l8.1 4.7c.7.4 1.6.4 2.4 0l8.1-4.7c.7-.4 1.2-1.2 1.2-2.1V18c0-.8-.4-1.6-1.2-2.1L29 10.2z"
/>
</svg>
);

export const NetworkIcon: React.FC<NetworkIconProps> = ({ network, size = 32 }) => {
switch (network.logoType) {
case "ethereum":
return <EthereumIcon color={network.color} size={size} />;
case "bsc":
return <BscIcon color={network.color} size={size} opacity={network.isTestnet ? 0.7 : 1} />;
case "polygon":
return <PolygonIcon color={network.color} size={size} />;
case "arbitrum":
return <img src={arbitrumLogo} alt="Arbitrum" width={size} height={size} />;
case "optimism":
return <img src={optimismLogo} alt="Optimism" width={size} height={size} />;
case "base":
return <img src={baseLogo} alt="Base" width={size} height={size} />;
case "hardhat":
return <img src={hardhatLogo} alt="Localhost" width={size} height={size} />;
default:
return <EthereumIcon color={network.color} size={size} />;
const [imageError, setImageError] = useState(false);

// If logo failed to load, show fallback
if (imageError || !network.logo) {
return <EthereumIcon color={network.color} size={size} />;
}

const logoUrl = getNetworkLogoUrl(network.logo);

return (
<img
src={logoUrl}
alt={network.name}
width={size}
height={size}
onError={() => setImageError(true)}
style={{ objectFit: "contain" }}
/>
);
};

export default NetworkIcon;
20 changes: 13 additions & 7 deletions src/components/pages/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { getEnabledNetworks, type NetworkConfig } from "../../config/networks";
import type { NetworkConfig } from "../../config/networks";
import { useNetworks } from "../../context/AppContext";
import NetworkIcon from "../common/NetworkIcon";

interface NetworkCardProps {
Expand Down Expand Up @@ -46,8 +47,9 @@ const NetworkCard: React.FC<NetworkCardProps> = ({ network }) => {
};

export default function Home() {
const enabledNetworks = useMemo(() => {
const allNetworks = getEnabledNetworks();
const { enabledNetworks: allNetworks, isLoading } = useNetworks();

const displayNetworks = useMemo(() => {
const environment = process.env.REACT_APP_ENVIRONMENT;
const isDevelopment = environment === "development";
const envNetworks = process.env.REACT_APP_OPENSCAN_NETWORKS;
Expand All @@ -65,7 +67,7 @@ export default function Home() {
}

return allNetworks;
}, []);
}, [allNetworks]);

return (
<div className="home-container">
Expand All @@ -74,9 +76,13 @@ export default function Home() {
<p className="subtitle">Select a blockchain network to explore</p>

<div className="network-grid">
{enabledNetworks.map((network) => (
<NetworkCard key={network.chainId} network={network} />
))}
{isLoading && displayNetworks.length === 0 ? (
<p className="loading-text">Loading networks...</p>
) : (
displayNetworks.map((network) => (
<NetworkCard key={network.chainId} network={network} />
))
)}
</div>
</div>
</div>
Expand Down
Loading
Loading