From fa8e21553895ccf4528cf0a3b6507d316332b8a4 Mon Sep 17 00:00:00 2001 From: clear Date: Wed, 27 Aug 2025 16:55:21 +0800 Subject: [PATCH] init --- src/App.vue | 80 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/src/App.vue b/src/App.vue index 9f60cc7..a1b5290 100644 --- a/src/App.vue +++ b/src/App.vue @@ -11,6 +11,7 @@ const progressText = ref('') const hasInjectedProvider = ref(false) const latestBlock = ref(null) const selectedPreset = ref(50000) +const showAllSafes = ref(false) const allSafes = ref([]) // BSC Testnet RPC 与 Safe 工厂地址(SafeProxyFactory 1.3.x,CREATE2 地址) @@ -24,6 +25,7 @@ const toBlockInput = ref('') const SAFE_ABI = [ 'function getOwners() view returns (address[])', + 'function getThreshold() view returns (uint256)', ] function isValidAddress(address) { @@ -156,6 +158,58 @@ async function getFactoryLogsChunked(provider, address, topic0, fromBlock, toBlo return all } +// 通过 WS 扫描并筛选“与我相关”的 Safe(按 getOwners 包含目标地址) +async function fetchMySafesViaWs() { + errorMessage.value = '' + safes.value = [] + progressText.value = '' + const targetOwner = (ownerAddress.value || connectedAddress.value || '').trim() + if (!isValidAddress(targetOwner)) { + errorMessage.value = '请输入有效地址或先连接钱包' + return + } + isLoading.value = true + try { + const provider = new ethers.WebSocketProvider(RPC_WS_URL, 97) + const latest = await provider.getBlockNumber() + const fromBlock = fromBlockInput.value ? Number(fromBlockInput.value) : Math.max(1, latest - (Number(selectedPreset.value) || 50000)) + const toBlock = toBlockInput.value ? Number(toBlockInput.value) : latest + const factoryTopic = ethers.id('ProxyCreation(address,address)') + const logs = await getFactoryLogsChunked(provider, SAFE_PROXY_FACTORY, factoryTopic, fromBlock, toBlock, 50000, (processedTo) => { + progressText.value = `通过 WS 扫描(与我相关)... 已至区块 ${processedTo}` + }) + const eventIface = new ethers.Interface(['event ProxyCreation(address proxy,address singleton)']) + const safeIface = new ethers.Interface(SAFE_ABI) + const resultSet = new Set() + for (const log of logs) { + try { + const decoded = eventIface.decodeEventLog('ProxyCreation', log.data, log.topics) + const proxy = decoded?.proxy || decoded?.[0] + if (!proxy) continue + // 校验为 Safe:threshold > 0 + const thresholdData = safeIface.encodeFunctionData('getThreshold', []) + const thresholdRes = await provider.call({ to: proxy, data: thresholdData }) + const threshold = Number(safeIface.decodeFunctionResult('getThreshold', thresholdRes)[0]) + if (!Number.isFinite(threshold) || threshold <= 0) continue + // 校验 owners 是否包含 + const ownersData = safeIface.encodeFunctionData('getOwners', []) + const ownersRes = await provider.call({ to: proxy, data: ownersData }) + const owners = safeIface.decodeFunctionResult('getOwners', ownersRes)[0] + if (owners.map((o) => ethers.getAddress(o)).includes(ethers.getAddress(targetOwner))) { + resultSet.add(ethers.getAddress(proxy)) + } + } catch (_) {} + } + safes.value = Array.from(resultSet) + if (safes.value.length === 0) errorMessage.value = '未找到与您相关的 Safe(WS 历史扫描)' + } catch (e) { + errorMessage.value = e?.message || 'WS 查询失败' + } finally { + isLoading.value = false + progressText.value = '' + } +} + // 通过 WS provider 扫描历史所有 Safe 地址 async function fetchAllSafesViaWs() { errorMessage.value = '' @@ -178,15 +232,16 @@ async function fetchAllSafesViaWs() { progressText.value = `通过 WS 扫描... 已至区块 ${processedTo}` }) const iface = new ethers.Interface(['event ProxyCreation(address proxy,address singleton)']) - const set = new Set(allSafes.value) + const set = new Set(showAllSafes.value ? allSafes.value : []) for (const log of logs) { try { const decoded = iface.decodeEventLog('ProxyCreation', log.data, log.topics) const proxy = decoded?.proxy || decoded?.[0] - if (proxy) set.add(ethers.getAddress(proxy)) + if (!proxy) continue + if (showAllSafes.value) set.add(ethers.getAddress(proxy)) } catch (_) {} } - allSafes.value = Array.from(set) + if (showAllSafes.value) allSafes.value = Array.from(set) } catch (e) { errorMessage.value = e?.message || '通过 WS 获取失败' } finally { @@ -234,11 +289,18 @@ function startWsSubscription() { const decoded = iface.decodeEventLog('ProxyCreation', log.data, log.topics) const proxy = decoded?.proxy || decoded?.[0] if (!proxy) return - // 记录所有 Safe(实时新建) - const normalized = ethers.getAddress(proxy) - if (!allSafes.value.includes(normalized)) allSafes.value = [normalized, ...allSafes.value] const provider = new ethers.JsonRpcProvider(RPC_HTTP_URL, 97) const safeIface = new ethers.Interface(SAFE_ABI) + // 仅保留有效 Safe:threshold > 0 + const thresholdData = safeIface.encodeFunctionData('getThreshold', []) + const thresholdRes = await provider.call({ to: proxy, data: thresholdData }) + const threshold = Number(safeIface.decodeFunctionResult('getThreshold', thresholdRes)[0]) + if (!Number.isFinite(threshold) || threshold <= 0) return + // 按需记录所有 Safe + if (showAllSafes.value) { + const normalized = ethers.getAddress(proxy) + if (!allSafes.value.includes(normalized)) allSafes.value = [normalized, ...allSafes.value] + } const data = safeIface.encodeFunctionData('getOwners', []) const result = await provider.call({ to: proxy, data }) const owners = safeIface.decodeFunctionResult('getOwners', result)[0] @@ -295,7 +357,8 @@ onBeforeUnmount(() => { placeholder="输入钱包地址(0x...)" spellcheck="false" /> - + +
@@ -330,7 +393,8 @@ onBeforeUnmount(() => {
- + + 共 {{ allSafes.length }} 个