From cbcebf6e28626469a4b095ca010a176332e4e568 Mon Sep 17 00:00:00 2001 From: WrBug Date: Wed, 18 Feb 2026 03:17:40 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E8=B4=A6=E6=88=B7=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=B8=8E=E6=9D=82=E9=A1=B9=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BlockchainService: 新增 isProxyDeployed、getUsdcAllowance(账户设置检查/授权用) - RetrofitFactory: 移除 RPC URL 替换 debug 日志 - AccountImportForm: 修复 checkSetupStatus/onSuccess 参数类型(使用 newAccount.id) - AccountSetupStatusBlock: 移除未使用的 isMobile 与 useMediaQuery 引用 Co-authored-by: Cursor --- .../service/common/BlockchainService.kt | 56 +++++++++++++++++++ .../polymarketbot/util/RetrofitFactory.kt | 4 -- frontend/src/components/AccountImportForm.tsx | 4 +- .../components/AccountSetupStatusBlock.tsx | 2 - 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/BlockchainService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/BlockchainService.kt index f7b0101..c94effc 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/BlockchainService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/BlockchainService.kt @@ -247,6 +247,62 @@ class BlockchainService( false } } + + /** + * 检查代理钱包是否已部署(链上有合约代码) + * @param proxyAddress 代理钱包地址 + * @return 已部署返回 true + */ + suspend fun isProxyDeployed(proxyAddress: String): Boolean { + if (proxyAddress.isBlank() || !proxyAddress.startsWith("0x") || proxyAddress.length != 42) { + return false + } + return isContract(proxyAddress) + } + + /** + * 查询 ERC20 USDC 授权额度 allowance(owner, spender) + * @param owner 代币持有者地址(代理钱包地址) + * @param spender 被授权方地址(如 CTF Exchange) + * @return 授权额度(原始值,USDC 为 6 位小数,需除以 1e6 为显示值) + */ + suspend fun getUsdcAllowance(owner: String, spender: String): Result { + return try { + if (owner.isBlank() || spender.isBlank()) { + return Result.failure(IllegalArgumentException("owner 或 spender 不能为空")) + } + val rpcApi = polygonRpcApi + // ERC20 allowance(address owner, address spender) 选择器 + val functionSelector = "0xdd62ed3e" + val ownerEncoded = EthereumUtils.encodeAddress(owner) + val spenderEncoded = EthereumUtils.encodeAddress(spender) + val data = functionSelector + ownerEncoded + spenderEncoded + val rpcRequest = JsonRpcRequest( + method = "eth_call", + params = listOf( + mapOf( + "to" to usdcContractAddress, + "data" to data + ), + "latest" + ) + ) + val response = rpcApi.call(rpcRequest) + if (!response.isSuccessful || response.body() == null) { + return Result.failure(Exception("RPC 请求失败: ${response.code()} ${response.message()}")) + } + val rpcResponse = response.body()!! + if (rpcResponse.error != null) { + return Result.failure(Exception("RPC 错误: ${rpcResponse.error.message}")) + } + val hexResult = rpcResponse.result?.asString ?: return Result.failure(Exception("RPC 响应 result 为空")) + val allowance = EthereumUtils.decodeUint256(hexResult) + Result.success(allowance) + } catch (e: Exception) { + logger.warn("查询 USDC 授权额度失败: ${e.message}") + Result.failure(e) + } + } /** * 查询账户 USDC 余额 diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/util/RetrofitFactory.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/util/RetrofitFactory.kt index cbe587a..c751d0e 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/util/RetrofitFactory.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/util/RetrofitFactory.kt @@ -400,8 +400,6 @@ class RpcUrlReplaceInterceptor( private val fixedBaseUrl: String, private val actualRpcUrl: String ) : Interceptor { - private val logger = LoggerFactory.getLogger(RpcUrlReplaceInterceptor::class.java) - @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() @@ -415,8 +413,6 @@ class RpcUrlReplaceInterceptor( val newUrl = newUrlString.toHttpUrlOrNull() ?: throw IllegalArgumentException("无效的 RPC URL: $newUrlString") - logger.debug("RPC URL 替换: $originalUrlString -> $newUrlString") - val newRequest = originalRequest.newBuilder() .url(newUrl) .build() diff --git a/frontend/src/components/AccountImportForm.tsx b/frontend/src/components/AccountImportForm.tsx index 8c7a896..265283a 100644 --- a/frontend/src/components/AccountImportForm.tsx +++ b/frontend/src/components/AccountImportForm.tsx @@ -269,7 +269,7 @@ const AccountImportForm: React.FC = ({ // 检查账户设置状态 let willShowSetupModal = false try { - const setupResponse = await apiService.accounts.checkSetupStatus(accountId) + const setupResponse = await apiService.accounts.checkSetupStatus(newAccount.id) if (setupResponse.data.code === 0 && setupResponse.data.data) { const status = setupResponse.data.data setSetupStatus(status) @@ -284,7 +284,7 @@ const AccountImportForm: React.FC = ({ } // 未展示设置弹窗时才调用 onSuccess,避免父组件关闭导入弹窗导致设置弹窗被卸载 if (!willShowSetupModal && onSuccess) { - onSuccess(accountId) + onSuccess(newAccount.id) } } else if (onSuccess) { onSuccess(0) diff --git a/frontend/src/components/AccountSetupStatusBlock.tsx b/frontend/src/components/AccountSetupStatusBlock.tsx index b24f90f..49b92dd 100644 --- a/frontend/src/components/AccountSetupStatusBlock.tsx +++ b/frontend/src/components/AccountSetupStatusBlock.tsx @@ -10,7 +10,6 @@ import { ReloadOutlined } from '@ant-design/icons' import { useTranslation } from 'react-i18next' -import { useMediaQuery } from 'react-responsive' import { apiService } from '../services/api' const { Paragraph, Text } = Typography @@ -47,7 +46,6 @@ const AccountSetupStatusBlock: React.FC = ({ embedded = false }) => { const { t } = useTranslation() - const isMobile = useMediaQuery({ maxWidth: 768 }) const [setupStatus, setSetupStatus] = useState(null) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false)