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
40 changes: 20 additions & 20 deletions public/configs/default-chains.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"type": "bioforest",
"name": "BFMeta",
"symbol": "BFM",
"icon": "/icons/bfmeta/chain.svg",
"icon": "../icons/bfmeta/chain.svg",
"tokenIconBase": [
"/icons/bfmeta/tokens",
"../icons/bfmeta/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/bfm",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/bfm"
],
Expand All @@ -27,9 +27,9 @@
"type": "bioforest",
"name": "CCChain",
"symbol": "CCC",
"icon": "/icons/ccchain/chain.svg",
"icon": "../icons/ccchain/chain.svg",
"tokenIconBase": [
"/icons/ccchain/tokens",
"../icons/ccchain/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/ccc",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/ccc"
],
Expand All @@ -43,9 +43,9 @@
"type": "bioforest",
"name": "PMChain",
"symbol": "PMC",
"icon": "/icons/pmchain/chain.svg",
"icon": "../icons/pmchain/chain.svg",
"tokenIconBase": [
"/icons/pmchain/tokens",
"../icons/pmchain/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/pmc",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/pmc"
],
Expand All @@ -59,9 +59,9 @@
"type": "bioforest",
"name": "BFChain V2",
"symbol": "BFT",
"icon": "/icons/bfchainv2/chain.svg",
"icon": "../icons/bfchainv2/chain.svg",
"tokenIconBase": [
"/icons/bfchainv2/tokens",
"../icons/bfchainv2/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/bftv2",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/bftv2"
],
Expand All @@ -75,9 +75,9 @@
"type": "bioforest",
"name": "BTGMeta",
"symbol": "BTGM",
"icon": "/icons/btgmeta/chain.svg",
"icon": "../icons/btgmeta/chain.svg",
"tokenIconBase": [
"/icons/btgmeta/tokens",
"../icons/btgmeta/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/btgm",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/btgm"
],
Expand Down Expand Up @@ -105,9 +105,9 @@
"type": "bioforest",
"name": "ETHMeta",
"symbol": "ETHM",
"icon": "/icons/ethmeta/chain.svg",
"icon": "../icons/ethmeta/chain.svg",
"tokenIconBase": [
"/icons/ethmeta/tokens",
"../icons/ethmeta/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/ethm",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/ethm"
],
Expand Down Expand Up @@ -135,9 +135,9 @@
"type": "evm",
"name": "Ethereum",
"symbol": "ETH",
"icon": "/icons/ethereum/chain.svg",
"icon": "../icons/ethereum/chain.svg",
"tokenIconBase": [
"/icons/ethereum/tokens",
"../icons/ethereum/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/eth",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/eth"
],
Expand All @@ -151,9 +151,9 @@
"type": "evm",
"name": "BNB Smart Chain",
"symbol": "BNB",
"icon": "/icons/binance/chain.svg",
"icon": "../icons/binance/chain.svg",
"tokenIconBase": [
"/icons/binance/tokens",
"../icons/binance/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/bsc",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/bsc"
],
Expand All @@ -167,9 +167,9 @@
"type": "bip39",
"name": "Tron",
"symbol": "TRX",
"icon": "/icons/tron/chain.svg",
"icon": "../icons/tron/chain.svg",
"tokenIconBase": [
"/icons/tron/tokens",
"../icons/tron/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/tron",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/tron"
],
Expand All @@ -183,9 +183,9 @@
"type": "bip39",
"name": "Bitcoin",
"symbol": "BTC",
"icon": "/icons/bitcoin/chain.svg",
"icon": "../icons/bitcoin/chain.svg",
"tokenIconBase": [
"/icons/bitcoin/tokens",
"../icons/bitcoin/tokens",
"https://bfm-fonts-cdn.oss-cn-hongkong.aliyuncs.com/meta-icon/btcm",
"https://raw.githubusercontent.com/BFChainMeta/fonts-cdn/main/src/meta-icon/btcm"
],
Expand Down
21 changes: 20 additions & 1 deletion src/components/wallet/token-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,31 @@ export interface TokenIconProps {
className?: string | undefined;
}

/**
* 检查 URL 是否是同源的(本地资源)
*/
function isSameOrigin(url: string): boolean {
// 相对路径视为同源
if (url.startsWith('/') || url.startsWith('./') || url.startsWith('../')) {
return true
}
// 检查是否与当前页面同源
try {
const urlObj = new URL(url)
return urlObj.origin === window.location.origin
} catch {
return true // 解析失败视为相对路径
}
}

/**
* 根据 base 路径和 symbol 生成图标 URL
* - 本地资源(同源):使用 {symbol}.svg 格式
* - CDN 资源(跨域):使用 icon-{symbol}.png 格式
*/
function buildIconUrl(base: string, symbol: string): string {
const lowerSymbol = symbol.toLowerCase();
if (base.startsWith('/') || base.startsWith('./')) {
if (isSameOrigin(base)) {
return `${base}/${lowerSymbol}.svg`;
}
return `${base}/icon-${lowerSymbol}.png`;
Expand Down
56 changes: 48 additions & 8 deletions src/services/chain-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ async function loadDefaultChainConfigs(): Promise<ChainConfig[]> {
throw new Error('fetch is not available in this environment')
}

const response = await fetch(getDefaultChainsUrl(), {
const jsonUrl = getDefaultChainsUrl()
const response = await fetch(jsonUrl, {
method: 'GET',
headers: { Accept: 'application/json' },
})
Expand All @@ -90,7 +91,8 @@ async function loadDefaultChainConfigs(): Promise<ChainConfig[]> {
}

const json: unknown = await response.json()
const parsed = parseConfigs(json, 'default')
// 传入 JSON 文件 URL,用于解析相对路径
const parsed = parseConfigs(json, 'default', jsonUrl)
defaultChainsCache = parsed
return parsed
})()
Expand All @@ -110,18 +112,56 @@ function parseJsonString(input: string): unknown {
}
}

function parseConfigs(input: unknown, source: ChainConfigSource): ChainConfig[] {
/**
* 解析相对路径为绝对 URL(相对于 JSON 文件位置)
*/
function resolveRelativePath(path: string, jsonFileUrl: string): string {
// 已经是绝对 URL,直接返回
if (path.startsWith('http://') || path.startsWith('https://')) {
return path
}
// 解析相对路径
return new URL(path, jsonFileUrl).toString()
}

/**
* 解析配置中的 icon 和 tokenIconBase 相对路径
*/
function resolveIconPaths(
config: { icon?: string | undefined; tokenIconBase?: string[] | undefined },
jsonFileUrl: string
): { icon?: string; tokenIconBase?: string[] } {
const result: { icon?: string; tokenIconBase?: string[] } = {}

if (config.icon !== undefined) {
result.icon = resolveRelativePath(config.icon, jsonFileUrl)
}

if (config.tokenIconBase !== undefined) {
result.tokenIconBase = config.tokenIconBase.map((base) =>
resolveRelativePath(base, jsonFileUrl)
)
}

return result
}

function parseConfigs(input: unknown, source: ChainConfigSource, jsonFileUrl?: string): ChainConfig[] {
const normalized: unknown = Array.isArray(input) ? input.map(normalizeUnknownType) : normalizeUnknownType(input)

const parsed = Array.isArray(normalized)
? ChainConfigListSchema.parse(normalized)
: [ChainConfigSchema.parse(normalized)]

return parsed.map((config) => ({
...config,
source,
enabled: true,
}))
return parsed.map((config) => {
const resolvedPaths = jsonFileUrl ? resolveIconPaths(config, jsonFileUrl) : {}
return {
...config,
...resolvedPaths,
source,
enabled: true,
}
})
}

function mergeBySource(options: {
Expand Down