From 77c648d6e88051f226a21ef4c6217ec25132520b Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 29 Jan 2026 21:12:31 +0800 Subject: [PATCH 1/2] fix(biobridge): keep tron contract hex for token info --- miniapps/biobridge/src/api/recharge.ts | 53 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/miniapps/biobridge/src/api/recharge.ts b/miniapps/biobridge/src/api/recharge.ts index 1e31bf17c..c1ffd623a 100644 --- a/miniapps/biobridge/src/api/recharge.ts +++ b/miniapps/biobridge/src/api/recharge.ts @@ -27,8 +27,12 @@ import type { ContractTokenInfo, } from './types' +const tokenInfoCache = new Map() +const tokenInfoInflight = new Map>() + /** - * Convert TRON hex addresses in ExternalAssetInfoItem to Base58 format + * Convert TRON hex deposit addresses in ExternalAssetInfoItem to Base58 format. + * Contract addresses must keep their original hex format for token info API. */ async function convertTronAddresses(item: ExternalAssetInfoItem): Promise { const result = { ...item } @@ -38,16 +42,11 @@ async function convertTronAddresses(item: ExternalAssetInfoItem): Promise { const recharge = { ...response.recharge } @@ -77,7 +76,7 @@ async function transformSupportResponse(response: RechargeSupportResDto): Promis } export const rechargeApi = { - /** 获取支持的充值配置 (TRON addresses converted to Base58) */ + /** 获取支持的充值配置 (TRON depositAddress converted to Base58) */ async getSupport(): Promise { const raw = await apiClient.get(API_ENDPOINTS.RECHARGE_SUPPORT) const parsed = rechargeSupportSchema.safeParse(raw) @@ -97,17 +96,37 @@ export const rechargeApi = { return parsed.data }, - /** 获取合约代币信息(精度/图标等) */ + /** 获取合约代币信息(精度/图标等),按 chainName+contractAddress 缓存 */ async getTokenInfo(params: { contractAddress: string; chainName: string }): Promise { - const raw = await apiClient.get(API_ENDPOINTS.CONTRACT_TOKEN_INFO, { - contractAddress: params.contractAddress, - chainName: params.chainName, - }) - const parsed = contractTokenInfoSchema.safeParse(raw) - if (!parsed.success) { - throw new ApiError('Invalid contract token info response', 0, parsed.error.flatten()) + const contractAddress = params.contractAddress.trim() + const chainName = params.chainName.trim() + const key = `${chainName}:${contractAddress}`.toLowerCase() + if (tokenInfoCache.has(key)) { + return tokenInfoCache.get(key) as ContractTokenInfo + } + if (tokenInfoInflight.has(key)) { + return tokenInfoInflight.get(key) as Promise + } + + const task = (async () => { + const raw = await apiClient.get(API_ENDPOINTS.CONTRACT_TOKEN_INFO, { + contractAddress, + chainName, + }) + const parsed = contractTokenInfoSchema.safeParse(raw) + if (!parsed.success) { + throw new ApiError('Invalid contract token info response', 0, parsed.error.flatten()) + } + tokenInfoCache.set(key, parsed.data) + return parsed.data + })() + + tokenInfoInflight.set(key, task) + try { + return await task + } finally { + tokenInfoInflight.delete(key) } - return parsed.data }, /** 获取合约池信息 */ From 31c4473233a3e5776b3982ef99b176832af981ac Mon Sep 17 00:00:00 2001 From: Gaubee Date: Thu, 29 Jan 2026 21:32:41 +0800 Subject: [PATCH 2/2] fix(biobridge): avoid decimals loading when already known --- miniapps/biobridge/src/App.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/miniapps/biobridge/src/App.tsx b/miniapps/biobridge/src/App.tsx index ffb68abe3..1dfc1954f 100644 --- a/miniapps/biobridge/src/App.tsx +++ b/miniapps/biobridge/src/App.tsx @@ -249,10 +249,12 @@ export default function App() { : undefined; const tokenInfoTargets = useMemo( () => - forgeOptions.map((option) => ({ - chain: option.externalChain, - address: option.externalInfo.contract, - })), + forgeOptions + .filter((option) => option.externalInfo.decimals === undefined) + .map((option) => ({ + chain: option.externalChain, + address: option.externalInfo.contract, + })), [forgeOptions], ); const { tokenInfoMap, loadingMap } = useTokenInfoMap(tokenInfoTargets); @@ -262,7 +264,8 @@ export default function App() { ? getTokenInfoKey(selectedOption.externalChain, externalTokenAddress) : undefined; const tokenInfo = tokenInfoKey ? tokenInfoMap[tokenInfoKey] : undefined; - const tokenInfoLoading = tokenInfoKey ? Boolean(loadingMap[tokenInfoKey]) : false; + const needsTokenInfo = Boolean(externalTokenAddress && externalDecimals === undefined); + const tokenInfoLoading = needsTokenInfo && tokenInfoKey ? Boolean(loadingMap[tokenInfoKey]) : false; const resolvedExternalDecimals = tokenInfo?.decimals ?? externalDecimals; const usingRemoteDecimals = Boolean(externalTokenAddress) && tokenInfo?.decimals !== undefined; const resolvedExternalSymbol = tokenInfo?.symbol ?? selectedOption?.externalAsset ?? ''; @@ -271,7 +274,7 @@ export default function App() { const handleConfirm = useCallback(async () => { if (!externalAccount || !internalAccount || !selectedOption) return; const tokenAddress = selectedOption.externalInfo.contract?.trim(); - if (tokenAddress && tokenInfoLoading) { + if (needsTokenInfo && tokenInfoLoading) { setError(t('error.decimalsLoading')); return; } @@ -310,7 +313,7 @@ export default function App() { internalAsset: selectedOption.internalAsset, internalAccount, }); - }, [externalAccount, internalAccount, selectedOption, amount, forgeHook, resolvedExternalDecimals, t]); + }, [externalAccount, internalAccount, selectedOption, amount, forgeHook, resolvedExternalDecimals, needsTokenInfo, t]); const handleReset = useCallback(() => { setRechargeStep('swap');