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
14 changes: 0 additions & 14 deletions src/components/pages/profile/ProfileNetworkDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,6 @@ const ProfileNetworkDisplay: React.FC<ProfileNetworkDisplayProps> = ({ profile }
</div>
)}

{/* RPC Endpoints - Available for tier 2+ */}
{tier >= 2 && profile.rpc?.public && profile.rpc.public.length > 0 && (
<div className="profile-section">
<h2 className="profile-section-title">Public RPC Endpoints</h2>
<div className="profile-rpc-list">
{profile.rpc.public.map((rpcUrl) => (
<div key={rpcUrl} className="profile-rpc-item">
<code>{rpcUrl}</code>
</div>
))}
</div>
</div>
)}

{/* Profile Markdown Content - Always shown for networks */}
{profile.profileMarkdown && (
<div className="profile-section">
Expand Down
149 changes: 73 additions & 76 deletions src/components/pages/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import { useSettings } from "../../../context/SettingsContext";
import { useMetaMaskExplorer } from "../../../hooks/useMetaMaskExplorer";
import { SUPPORTED_LANGUAGES } from "../../../i18n";
import { clearSupportersCache } from "../../../services/MetadataService";
import type { MetadataRpcEndpoint } from "../../../services/MetadataService";
import type { AIProvider, PromptVersion, RPCUrls, RpcUrlsContextType } from "../../../types";
import { AI_PROVIDERS, AI_PROVIDER_ORDER } from "../../../config/aiProviders";
import { clearAICache } from "../../common/AIAnalysis/aiCache";
import { logger } from "../../../utils/logger";
import { getChainIdFromNetwork } from "../../../utils/networkResolver";
import { clearMetadataRpcCache, getMetadataEndpointMap } from "../../../utils/rpcStorage";

// Infura network slugs by chain ID
const INFURA_NETWORKS: Record<number, string> = {
Expand Down Expand Up @@ -47,12 +49,6 @@ const getAlchemyUrl = (chainId: number, apiKey: string): string | null => {
const isInfuraUrl = (url: string): boolean => url.includes("infura.io");
const isAlchemyUrl = (url: string): boolean => url.includes("alchemy.com");

const getProviderBadge = (url: string): string | null => {
if (isInfuraUrl(url)) return "INFURA";
if (isAlchemyUrl(url)) return "ALCHEMY";
return null;
};

const Settings: React.FC = () => {
const { t, i18n } = useTranslation("settings");
const { rpcUrls, setRpcUrls } = useContext(AppContext);
Expand All @@ -68,7 +64,6 @@ const Settings: React.FC = () => {
networkId: string;
index: number;
} | null>(null);
const [fetchingNetworkId, setFetchingNetworkId] = useState<string | null>(null);
const [expandedChains, setExpandedChains] = useState<Set<string>>(new Set());
const [localApiKeys, setLocalApiKeys] = useState({
infura: settings.apiKeys?.infura || "",
Expand Down Expand Up @@ -119,6 +114,8 @@ const Settings: React.FC = () => {
const clearAllCaches = useCallback(() => {
// Clear metadata service caches
clearSupportersCache();
// Clear metadata RPC cache
clearMetadataRpcCache();
// Clear localStorage caches if any
localStorage.removeItem("openscan_cache");
// Clear AI analysis cache
Expand Down Expand Up @@ -204,56 +201,59 @@ const Settings: React.FC = () => {
return "";
};

// Fetch RPCs from Chainlist API (only for EVM networks)
// networkId: CAIP-2 format (e.g., "eip155:1"), chainId: numeric for Chainlist API
const fetchFromChainlist = useCallback(async (networkId: string, chainId: number | undefined) => {
// Only EVM networks with numeric chainId are supported by Chainlist
if (chainId === undefined) return;
setFetchingNetworkId(networkId);
try {
const response = await fetch("https://chainlist.org/rpcs.json");
if (!response.ok) throw new Error("Failed to fetch from Chainlist");

const chains = await response.json();
const chain = chains.find((c: { chainId: number }) => c.chainId === chainId);

if (!chain?.rpc) {
throw new Error(`No RPCs found for chain ${chainId}`);
// Build URL→endpoint lookup map from cached metadata
const metadataUrlMap = useMemo(() => {
const endpointMap = getMetadataEndpointMap();
const urlMap = new Map<string, MetadataRpcEndpoint>();
for (const endpoints of Object.values(endpointMap)) {
for (const ep of endpoints) {
urlMap.set(ep.url, ep);
}
}
return urlMap;
}, []);

// Filter for tracking: "none" and extract URLs
const newUrls = chain.rpc
.filter((rpc: { tracking?: string }) => rpc.tracking === "none")
.map((rpc: { url: string }) => rpc.url)
.filter((url: string) => url && !url.includes("${"))
.filter((url: string) => !url.startsWith("wss://"));
/**
* Get CSS class for an RPC tag based on metadata endpoint properties
* - rpc-tracking: has tracking (tracking !== "none") → yellow
* - rpc-opensource: no tracking + open source → green
* - rpc-private: no tracking + not open source → light green
* - no class: URL not found in metadata (user-added)
*/
const getRpcTagClass = useCallback(
(url: string): string => {
// Personal API key URLs have tracking enabled
if (isInfuraUrl(url) || isAlchemyUrl(url)) return "rpc-tracking";
const ep = metadataUrlMap.get(url);
if (!ep) return "";
if (ep.tracking !== "none") return "rpc-tracking";
if (ep.isOpenSource) return "rpc-opensource";
return "rpc-private";
},
[metadataUrlMap],
);

if (newUrls.length === 0) {
throw new Error(`No privacy-friendly RPCs found for chain ${chainId}`);
/**
* Get display label for an RPC tag.
* Priority: Infura/Alchemy personal → metadata provider name → hostname from URL
*/
const getRpcTagLabel = useCallback(
(url: string): string => {
if (isInfuraUrl(url)) return "Infura Personal";
if (isAlchemyUrl(url)) return "Alchemy Personal";
const ep = metadataUrlMap.get(url);
if (ep?.provider && ep.provider.toLowerCase() !== "unknown") return ep.provider;
try {
return new URL(url).hostname;
} catch {
return url;
}
},
[metadataUrlMap],
);

// Merge with existing URLs, avoiding duplicates (store by networkId)
setLocalRpc((prev) => {
const currentValue = prev[networkId];
const existingUrls = Array.isArray(currentValue)
? currentValue
: typeof currentValue === "string"
? currentValue
.split(",")
.map((s) => s.trim())
.filter(Boolean)
: [];
const mergedUrls = Array.from(new Set([...existingUrls, ...newUrls]));
return {
...prev,
[networkId]: mergedUrls,
};
});
} catch (error) {
logger.error("Error fetching from Chainlist:", error);
} finally {
setFetchingNetworkId(null);
}
const copyToClipboard = useCallback((url: string) => {
navigator.clipboard.writeText(url);
}, []);

// Handle setting OpenScan as MetaMask default explorer
Expand Down Expand Up @@ -862,6 +862,18 @@ const Settings: React.FC = () => {
<h2 className="settings-section-title">🔗 {t("rpcEndpoints.title")}</h2>
<p className="settings-section-description">{t("rpcEndpoints.description")}</p>

<div className="flex-start settings-rpc-legend">
<span className="settings-rpc-tag rpc-opensource">
{t("rpcEndpoints.legendOpensource")}
</span>
<span className="settings-rpc-tag rpc-private">
{t("rpcEndpoints.legendPrivate")}
</span>
<span className="settings-rpc-tag rpc-tracking">
{t("rpcEndpoints.legendTracking")}
</span>
</div>

<div className="flex-column settings-chain-list">
{chainConfigs.map((chain) => {
const isExpanded = expandedChains.has(chain.id);
Expand Down Expand Up @@ -921,22 +933,6 @@ const Settings: React.FC = () => {
{/* Collapsible Content */}
{isExpanded && (
<div className="settings-chain-content">
{/* Only show Chainlist fetch for EVM networks */}
{chain.chainId !== undefined && (
<div className="settings-chain-actions">
<button
type="button"
className="settings-fetch-rpc-button"
onClick={() => fetchFromChainlist(chain.id, chain.chainId)}
disabled={fetchingNetworkId === chain.id}
>
{fetchingNetworkId === chain.id
? t("rpcEndpoints.fetchButton")
: t("rpcEndpoints.fetchingButton")}
</button>
</div>
)}

<input
className="settings-rpc-input"
value={getLocalRpcString(chain.id)}
Expand Down Expand Up @@ -973,21 +969,22 @@ const Settings: React.FC = () => {
// biome-ignore lint/a11y/noStaticElementInteractions: Drag-and-drop requires these handlers
<div
key={url}
className={`settings-rpc-tag ${draggedItem?.networkId === chain.id && draggedItem?.index === idx ? "dragging" : ""}`}
className={`settings-rpc-tag ${getRpcTagClass(url)} ${draggedItem?.networkId === chain.id && draggedItem?.index === idx ? "dragging" : ""}`}
title={url}
draggable
onDragStart={() => handleDragStart(chain.id, idx)}
onDragOver={handleDragOver}
onDrop={() => handleDrop(chain.id, idx)}
onDragEnd={() => setDraggedItem(null)}
onClick={() => copyToClipboard(url)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") copyToClipboard(url);
}}
>
<span className="settings-rpc-tag-index">{idx + 1}</span>
{getProviderBadge(url) ? (
<span className="settings-rpc-tag-provider">
{getProviderBadge(url)}
</span>
) : (
<span className="settings-rpc-tag-url">{url}</span>
)}
<span className="settings-rpc-tag-provider">
{getRpcTagLabel(url)}
</span>
<button
type="button"
className="settings-rpc-tag-delete"
Expand Down
69 changes: 0 additions & 69 deletions src/config/networks.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@
},
"logo": "assets/networks/1.svg",
"profile": "profiles/networks/1.md",
"rpc": {
"public": [
"https://ethereum.publicnode.com",
"https://eth.merkle.io",
"https://eth.blockrazor.xyz",
"https://eth.llamarpc.com",
"https://rpc.ankr.com/eth"
]
},
"links": [
{
"name": "Website",
Expand Down Expand Up @@ -55,13 +46,6 @@
"color": "#0052FF",
"isTestnet": false,
"logo": "assets/networks/8453.svg",
"rpc": {
"public": [
"https://mainnet.base.org",
"https://base.publicnode.com",
"https://rpc.ankr.com/base"
]
},
"links": [
{
"name": "Website",
Expand Down Expand Up @@ -91,13 +75,6 @@
"color": "#28A0F0",
"isTestnet": false,
"logo": "assets/networks/42161.svg",
"rpc": {
"public": [
"https://arb1.arbitrum.io/rpc",
"https://rpc.ankr.com/arbitrum",
"https://arbitrum.publicnode.com"
]
},
"links": [
{
"name": "Website",
Expand Down Expand Up @@ -132,13 +109,6 @@
"color": "#FF0420",
"isTestnet": false,
"logo": "assets/networks/10.svg",
"rpc": {
"public": [
"https://mainnet.optimism.io",
"https://optimism.publicnode.com",
"https://rpc.ankr.com/optimism"
]
},
"links": [
{
"name": "Website",
Expand Down Expand Up @@ -168,13 +138,6 @@
"color": "#F0B90B",
"isTestnet": false,
"logo": "assets/networks/56.svg",
"rpc": {
"public": [
"https://bsc-dataseed.binance.org",
"https://bsc.publicnode.com",
"https://rpc.ankr.com/bsc"
]
},
"links": [
{
"name": "Website",
Expand Down Expand Up @@ -204,13 +167,6 @@
"color": "#8247E5",
"isTestnet": false,
"logo": "assets/networks/137.svg",
"rpc": {
"public": [
"https://polygon-rpc.com",
"https://polygon-bor.publicnode.com",
"https://rpc.ankr.com/polygon"
]
},
"links": [
{
"name": "Website",
Expand Down Expand Up @@ -240,13 +196,6 @@
"color": "#CFB5F0",
"isTestnet": true,
"logo": "assets/networks/11155111.svg",
"rpc": {
"public": [
"https://rpc.sepolia.org",
"https://ethereum-sepolia.publicnode.com",
"https://rpc.ankr.com/eth_sepolia"
]
},
"links": [
{
"name": "Faucet",
Expand All @@ -271,13 +220,6 @@
"color": "#F0B90B",
"isTestnet": true,
"logo": "assets/networks/97.svg",
"rpc": {
"public": [
"https://data-seed-prebsc-1-s1.binance.org:8545",
"https://bsc-testnet.publicnode.com",
"https://rpc.ankr.com/bsc_testnet_chapel"
]
},
"links": [
{
"name": "Faucet",
Expand All @@ -302,14 +244,6 @@
"color": "#F7931A",
"isTestnet": false,
"logo": "assets/networks/bitcoin-mainnet.svg",
"rpc": {
"public": [
"https://bitcoin-rpc.publicnode.com",
"https://bitcoin.api.onfinality.io/public",
"https://public-btc.nownodes.io/",
"https://bitcoin-mainnet.gateway.tatum.io/"
]
},
"links": [
{
"name": "Website",
Expand Down Expand Up @@ -339,9 +273,6 @@
"color": "#F7931A",
"isTestnet": true,
"logo": "assets/networks/bitcoin-mainnet.svg",
"rpc": {
"public": ["https://bitcoin-testnet-rpc.publicnode.com"]
},
"links": [
{
"name": "Faucet",
Expand Down
4 changes: 0 additions & 4 deletions src/config/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ interface NetworkMetadata {
features?: string[];
priority?: number;
};
rpc?: {
public: string[];
};
links?: Array<{
name: string;
url: string;
Expand All @@ -64,7 +61,6 @@ function metadataToNetworkConfig(network: NetworkMetadata): NetworkConfig {
logo: network.logo,
profile: network.profile,
explorer: network.explorer,
rpc: network.rpc,
links: network.links,
};
}
Expand Down
Loading