fix(token-aggregation): enforce wallet-safe Chain 138 RPC URLs
Ensure MetaMask add-chain payloads never inherit LAN HTTP RPCs by routing Chain 138 wallet payloads through a public HTTPS-only resolver. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,8 +6,25 @@ export const CHAIN138_PUBLIC_RPC_URLS = [
|
||||
] as const;
|
||||
|
||||
const DEFAULT_CHAIN138_PUBLIC_RPC_URL = CHAIN138_PUBLIC_RPC_URLS[0];
|
||||
const DEFAULT_CHAIN138_WALLET_RPC_URL = CHAIN138_PUBLIC_RPC_URLS[1];
|
||||
const DEFAULT_CHAIN138_SERVER_RPC_URL = 'http://192.168.11.235:8545';
|
||||
|
||||
export function isWalletSafeRpcUrl(url: string | undefined): boolean {
|
||||
if (!url) return false;
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
if (parsed.protocol !== 'https:') return false;
|
||||
const host = parsed.hostname.toLowerCase();
|
||||
if (host === 'localhost' || host === '127.0.0.1' || host.endsWith('.local')) return false;
|
||||
if (/^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[0-1])\./.test(host)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** HTTPS FQDN for wallets, MetaMask, and browser JSON-RPC (same on LAN and external). */
|
||||
export function resolveChain138PublicRpcUrl(): string {
|
||||
return String(
|
||||
@@ -19,6 +36,18 @@ export function resolveChain138PublicRpcUrl(): string {
|
||||
).trim();
|
||||
}
|
||||
|
||||
export function resolveChain138WalletRpcUrl(): string {
|
||||
const candidates = [
|
||||
process.env.CHAIN138_WALLET_RPC_URL,
|
||||
process.env.CHAIN138_RPC_HTTP_PUBLIC_FQDN,
|
||||
process.env.RPC_URL_138_PUBLIC,
|
||||
process.env.CHAIN138_RPC_URL_PUBLIC,
|
||||
process.env.CHAIN_138_RPC_URL,
|
||||
DEFAULT_CHAIN138_WALLET_RPC_URL,
|
||||
];
|
||||
return candidates.map((url) => String(url || '').trim()).find(isWalletSafeRpcUrl) || DEFAULT_CHAIN138_WALLET_RPC_URL;
|
||||
}
|
||||
|
||||
export function resolveChain138PublicRpcUrls(): string[] {
|
||||
const fromEnv = process.env.CHAIN138_PUBLIC_RPC_URLS?.trim();
|
||||
if (fromEnv) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { NetworkEntry } from '../config/networks';
|
||||
import { resolveChain138PublicRpcUrl } from '../config/chain138-rpc';
|
||||
import { resolveChain138WalletRpcUrl } from '../config/chain138-rpc';
|
||||
import { filterMetamaskSafeIconUrls } from '../config/metamask-wallet-images';
|
||||
|
||||
/** EIP-3085 fields accepted by MetaMask `wallet_addEthereumChain` (strict subset). */
|
||||
@@ -28,7 +28,7 @@ export function toWalletAddEthereumChainParams(
|
||||
const httpsList = httpsOnly(network.rpcUrls) ?? ['https://rpc-http-pub.d-bis.org'];
|
||||
// MetaMask rejects some multi-rpc addEthereumChain payloads; ship one canonical HTTPS URL.
|
||||
const primaryRpc =
|
||||
network.chainId === '0x8a' ? resolveChain138PublicRpcUrl() : httpsList[0];
|
||||
network.chainId === '0x8a' ? resolveChain138WalletRpcUrl() : httpsList[0];
|
||||
const rpcUrls = primaryRpc ? [primaryRpc] : httpsList.slice(0, 1);
|
||||
const out: WalletAddEthereumChainParams = {
|
||||
chainId: network.chainId,
|
||||
|
||||
Reference in New Issue
Block a user