diff --git a/backend/routes/dleModules.js b/backend/routes/dleModules.js index 01d84c5..27abf61 100644 --- a/backend/routes/dleModules.js +++ b/backend/routes/dleModules.js @@ -160,62 +160,204 @@ router.post('/get-all-modules', async (req, res) => { }); } - console.log(`[DLE Modules] Получение всех модулей для DLE: ${dleAddress}`); + console.log(`[DLE Modules] Получение всех модулей для DLE: ${dleAddress}`); - const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111); - if (!rpcUrl) { - return res.status(500).json({ - success: false, - error: 'RPC URL для Sepolia не найден' - }); - } - - const provider = new ethers.JsonRpcProvider(rpcUrl); + // Сначала пытаемся получить модули из файлов деплоя + console.log(`[DLE Modules] Вызываем getDeployedModulesInfo...`); + const modulesInfo = await getDeployedModulesInfo(dleAddress); + console.log(`[DLE Modules] Результат getDeployedModulesInfo:`, modulesInfo); - const dleAbi = [ - "function isModuleActive(bytes32 _moduleId) external view returns (bool)", - "function getModuleAddress(bytes32 _moduleId) external view returns (address)" - ]; - - const dle = new ethers.Contract(dleAddress, dleAbi, provider); - - // Список известных модулей для проверки - const knownModules = [ - "0x7472656173757279000000000000000000000000000000000000000000000000", // "treasury" - "0x6d756c7469736967000000000000000000000000000000000000000000000000", // "multisig" - "0x646561637469766174696f6e0000000000000000000000000000000000000000", // "deactivation" - "0x616e616c79746963730000000000000000000000000000000000000000000000", // "analytics" - "0x6e6f74696669636174696f6e7300000000000000000000000000000000000000" // "notifications" - ]; - - const modules = []; - - // Проверяем активность известных модулей - for (const moduleId of knownModules) { - try { - const isActive = await dle.isModuleActive(moduleId); - if (isActive) { - const address = await dle.getModuleAddress(moduleId); - modules.push({ - id: moduleId, - address: address, - isActive: isActive + if (modulesInfo.modules && modulesInfo.modules.length > 0) { + console.log(`[DLE Modules] Модули найдены в файлах, количество: ${modulesInfo.modules.length}`); + + // Преобразуем данные в нужный формат для frontend + // Группируем модули по типам, а не по сетям + const moduleGroups = { + treasury: { + moduleId: "0x747265617375727900000000000000000000000000000000000000000000000000", + moduleName: "TREASURY", + moduleDescription: "Казначейство DLE - управление финансами, депозиты, выводы, дивиденды", + addresses: [], + isActive: true, + deployedAt: modulesInfo.deployTimestamp + }, + timelock: { + moduleId: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", + moduleName: "TIMELOCK", + moduleDescription: "Модуль задержек исполнения - безопасность критических операций через таймлоки", + addresses: [], + isActive: true, + deployedAt: modulesInfo.deployTimestamp + }, + reader: { + moduleId: "0x726561646572000000000000000000000000000000000000000000000000000000", + moduleName: "READER", + moduleDescription: "Модуль чтения данных DLE - получение информации о контракте", + addresses: [], + isActive: true, + deployedAt: modulesInfo.deployTimestamp + } + }; + + // Собираем адреса для каждого типа модуля из всех сетей + // Определяем реальные chainId для каждого адреса + for (let networkIndex = 0; networkIndex < modulesInfo.modules.length; networkIndex++) { + const networkModules = modulesInfo.modules[networkIndex]; + + console.log(`[DLE Modules] Обрабатываем сеть ${networkIndex + 1}:`, networkModules); + + // Получаем информацию о сети из файла деплоя + let chainId = null; + let networkName = `Сеть ${networkIndex + 1}`; + + // Если в файле есть информация о сетях, используем её + if (modulesInfo.networks && modulesInfo.networks[networkIndex]) { + chainId = modulesInfo.networks[networkIndex].chainId; + networkName = modulesInfo.networks[networkIndex].networkName || `Сеть ${networkIndex + 1}`; + } + + if (networkModules.treasuryModule) { + moduleGroups.treasury.addresses.push({ + address: networkModules.treasuryModule, + networkName: networkName, + networkIndex: networkIndex, + chainId: chainId, + verificationStatus: modulesInfo.verification?.[networkIndex]?.treasuryModule || 'pending' }); } + + if (networkModules.timelockModule) { + moduleGroups.timelock.addresses.push({ + address: networkModules.timelockModule, + networkName: networkName, + networkIndex: networkIndex, + chainId: chainId, + verificationStatus: modulesInfo.verification?.[networkIndex]?.timelockModule || 'pending' + }); + } + + if (networkModules.dleReader) { + moduleGroups.reader.addresses.push({ + address: networkModules.dleReader, + networkName: networkName, + networkIndex: networkIndex, + chainId: chainId, + verificationStatus: modulesInfo.verification?.[networkIndex]?.dleReader || 'pending' + }); + } + } + + // Преобразуем в массив модулей + const formattedModules = Object.values(moduleGroups).filter(module => module.addresses.length > 0); + + console.log(`[DLE Modules] Сгруппированные модули:`, formattedModules); + console.log(`[DLE Modules] Найдено типов модулей: ${formattedModules.length}`); + + res.json({ + success: true, + data: { + modules: formattedModules, + modulesInitialized: true, + totalModules: formattedModules.length, + activeModules: formattedModules.length + } + }); + } else { + console.log(`[DLE Modules] Файлы модулей не найдены или пусты, проверяем блокчейн для DLE: ${dleAddress}`); + + const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111); + if (!rpcUrl) { + return res.status(500).json({ + success: false, + error: 'RPC URL для Sepolia не найден' + }); + } + + const provider = new ethers.JsonRpcProvider(rpcUrl); + + const dleAbi = [ + "function isModuleActive(bytes32 _moduleId) external view returns (bool)", + "function getModuleAddress(bytes32 _moduleId) external view returns (address)", + "function modulesInitialized() external view returns (bool)" + ]; + + const dle = new ethers.Contract(dleAddress, dleAbi, provider); + + // Проверяем инициализацию модулей + let modulesInitialized = false; + try { + modulesInitialized = await dle.modulesInitialized(); } catch (error) { - console.log(`[DLE Modules] Ошибка при проверке модуля ${moduleId}:`, error.message); + console.log(`[DLE Modules] Ошибка при проверке инициализации модулей:`, error.message); } + + if (!modulesInitialized) { + console.log(`[DLE Modules] Модули для DLE ${dleAddress} не инициализированы`); + return res.json({ + success: true, + data: { + modules: [], + modulesInitialized: false, + totalModules: 0, + activeModules: 0 + } + }); + } + + // Список стандартных модулей DLE + const standardModuleIds = [ + "0x7472656173757279000000000000000000000000000000000000000000000000", // "treasury" + "0x74696d656c6f636b000000000000000000000000000000000000000000000000", // "timelock" + "0x6d696e7400000000000000000000000000000000000000000000000000000000", // "mint" + "0x6275726e00000000000000000000000000000000000000000000000000000000", // "burn" + "0x6f7261636c650000000000000000000000000000000000000000000000000000", // "oracle" + "0x696e6865726974616e6365000000000000000000000000000000000000000000", // "inheritance" + "0x636f6d6d756e69636174696f6e00000000000000000000000000000000000000", // "communication" + "0x6170706c69636174696f6e000000000000000000000000000000000000000000" // "application" + ]; + + const modules = []; + + // Проверяем каждый модуль + for (const moduleId of standardModuleIds) { + try { + const isActive = await dle.isModuleActive(moduleId); + if (isActive) { + const moduleAddress = await dle.getModuleAddress(moduleId); + + // Получаем человекочитаемое название модуля + const moduleName = getModuleName(moduleId); + const moduleDescription = getModuleDescription(moduleId); + + modules.push({ + moduleId: moduleId, + moduleName: moduleName, + moduleDescription: moduleDescription, + moduleAddress: moduleAddress, + isActive: isActive, + deployedAt: new Date().toISOString() // В реальности нужно брать из событий + }); + + console.log(`[DLE Modules] Найден активный модуль: ${moduleName} по адресу ${moduleAddress}`); + } + } catch (error) { + console.log(`[DLE Modules] Ошибка при проверке модуля ${getModuleName(moduleId)}:`, error.message); + } + } + + console.log(`[DLE Modules] Всего найдено активных модулей в блокчейне: ${modules.length}`); + + res.json({ + success: true, + data: { + modules: modules, + modulesInitialized: modulesInitialized, + totalModules: standardModuleIds.length, + activeModules: modules.length + } + }); } - console.log(`[DLE Modules] Найдено активных модулей: ${modules.length}`); - - res.json({ - success: true, - data: { - modules: modules - } - }); - } catch (error) { console.error('[DLE Modules] Ошибка при получении всех модулей:', error); res.status(500).json({ @@ -337,15 +479,10 @@ async function getDeployedModulesInfo(dleAddress) { const path = require('path'); try { - // Ищем файл модулей для конкретного DLE - const deployDir = path.join(__dirname, '../temp'); - if (!fs.existsSync(deployDir)) { - return { modules: [], verification: {} }; - } - - // Ищем файл по адресу DLE + // Ищем файл модулей в правильной директории + const tempDir = path.join(__dirname, '../scripts/temp'); const modulesFileName = `modules-${dleAddress.toLowerCase()}.json`; - const modulesFilePath = path.join(deployDir, modulesFileName); + const modulesFilePath = path.join(tempDir, modulesFileName); if (!fs.existsSync(modulesFilePath)) { console.log(`[DLE Modules] Файл модулей не найден: ${modulesFileName}`); @@ -356,10 +493,26 @@ async function getDeployedModulesInfo(dleAddress) { const data = JSON.parse(fs.readFileSync(modulesFilePath, 'utf8')); console.log(`[DLE Modules] Загружена информация о модулях для DLE: ${dleAddress}`); + // Получаем статус верификации для каждого модуля + const modulesWithVerification = []; + + if (data.modules && Array.isArray(data.modules)) { + for (const networkModules of data.modules) { + if (networkModules.treasuryModule) { + modulesWithVerification.push({ + ...networkModules, + verificationStatus: data.verification?.[0]?.treasuryModule || 'pending' + }); + } + } + } + return { modules: data.modules || [], verification: data.verification || {}, - deployTimestamp: data.deployTimestamp + deployTimestamp: data.deployTimestamp, + networks: data.networks || [], // ✅ Добавляем поле networks + modulesWithVerification: modulesWithVerification }; } catch (error) { console.error(`Ошибка при чтении файла ${modulesFileName}:`, error); @@ -372,4 +525,404 @@ async function getDeployedModulesInfo(dleAddress) { } } +// Функция для определения названия сети по chainId +function getNetworkNameByChainId(chainId) { + const networkNames = { + 1: 'Ethereum Mainnet', + 5: 'Goerli', + 11155111: 'Sepolia', + 137: 'Polygon Mainnet', + 80001: 'Mumbai', + 56: 'BSC Mainnet', + 97: 'BSC Testnet', + 42161: 'Arbitrum One', + 421614: 'Arbitrum Sepolia', + 10: 'Optimism', + 11155420: 'Optimism Sepolia', + 8453: 'Base', + 84532: 'Base Sepolia' + }; + + return networkNames[chainId] || `Chain ID ${chainId}`; +} + +// Функция для получения модулей из файлов +async function getModulesFromFiles(dleAddress) { + const fs = require('fs'); + const path = require('path'); + + try { + // Ищем файл модулей в правильной директории + const tempDir = path.join(__dirname, '../scripts/temp'); + const modulesFileName = `modules-${dleAddress.toLowerCase()}.json`; + const modulesFilePath = path.join(tempDir, modulesFileName); + + if (!fs.existsSync(modulesFilePath)) { + console.log(`[DLE Modules] Файл модулей не найден: ${modulesFileName}`); + return []; + } + + try { + const data = JSON.parse(fs.readFileSync(modulesFilePath, 'utf8')); + console.log(`[DLE Modules] Загружена информация о модулях для DLE: ${dleAddress}`); + + const modules = []; + + // Обрабатываем модули из файла + if (data.modules && Array.isArray(data.modules)) { + for (const networkModules of data.modules) { + if (networkModules.treasuryModule) { + modules.push({ + moduleId: "0x7472656173757279000000000000000000000000000000000000000000000000", + moduleName: "TREASURY", + moduleDescription: "Казначейство DLE - управление финансами, депозиты, выводы, дивиденды", + moduleAddress: networkModules.treasuryModule, + isActive: true, + deployedAt: data.deployTimestamp + }); + } + + if (networkModules.timelockModule) { + modules.push({ + moduleId: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", + moduleName: "TIMELOCK", + moduleDescription: "Модуль задержек исполнения - безопасность критических операций через таймлоки", + moduleAddress: networkModules.timelockModule, + isActive: true, + deployedAt: data.deployTimestamp + }); + } + + if (networkModules.dleReader) { + modules.push({ + moduleId: "0x726561646572000000000000000000000000000000000000000000000000000000", + moduleName: "READER", + moduleDescription: "Модуль чтения данных DLE - получение информации о контракте", + moduleAddress: networkModules.dleReader, + isActive: true, + deployedAt: data.deployTimestamp + }); + } + } + } + + // Убираем дубликаты (модули могут быть в нескольких сетях) + const uniqueModules = modules.filter((module, index, self) => + index === self.findIndex(m => m.moduleId === module.moduleId) + ); + + return uniqueModules; + + } catch (error) { + console.error(`Ошибка при чтении файла ${modulesFileName}:`, error); + return []; + } + + } catch (error) { + console.error('Ошибка при получении информации о модулях из файлов:', error); + return []; + } +} + +// Вспомогательные функции для получения названий и описаний модулей +function getModuleName(moduleId) { + const moduleNames = { + "0x7472656173757279000000000000000000000000000000000000000000000000": "TREASURY", + "0x74696d656c6f636b000000000000000000000000000000000000000000000000": "TIMELOCK", + "0x6d696e7400000000000000000000000000000000000000000000000000000000": "MINT", + "0x6275726e00000000000000000000000000000000000000000000000000000000": "BURN", + "0x6f7261636c650000000000000000000000000000000000000000000000000000": "ORACLE", + "0x696e6865726974616e6365000000000000000000000000000000000000000000": "INHERITANCE", + "0x636f6d6d756e69636174696f6e00000000000000000000000000000000000000": "COMMUNICATION", + "0x6170706c69636174696f6e000000000000000000000000000000000000000000": "APPLICATION" + }; + return moduleNames[moduleId] || "UNKNOWN"; +} + +function getModuleDescription(moduleId) { + const moduleDescriptions = { + "0x7472656173757279000000000000000000000000000000000000000000000000": "Казначейство DLE - управление финансами, депозиты, выводы, дивиденды", + "0x74696d656c6f636b000000000000000000000000000000000000000000000000": "Модуль задержек исполнения - безопасность критических операций через таймлоки", + "0x6d696e7400000000000000000000000000000000000000000000000000000000": "Модуль выпуска токенов - создание дополнительных токенов DLE через governance", + "0x6275726e00000000000000000000000000000000000000000000000000000000": "Модуль сжигания токенов - уменьшение общего предложения токенов DLE", + "0x6f7261636c650000000000000000000000000000000000000000000000000000": "Модуль оракулов - получение внешних данных для автоматизации DLE", + "0x696e6865726974616e6365000000000000000000000000000000000000000000": "Модуль наследования - передача прав и токенов между участниками DLE", + "0x636f6d6d756e69636174696f6e00000000000000000000000000000000000000": "Модуль коммуникации - уведомления и взаимодействие между участниками DLE", + "0x6170706c69636174696f6e000000000000000000000000000000000000000000": "Модуль заявок - обработка предложений и заявок участников DLE" + }; + return moduleDescriptions[moduleId] || "Описание не найдено"; +} + +// Верификация модуля на Etherscan +router.post('/verify-module', async (req, res) => { + try { + const { dleAddress, moduleId, moduleAddress, moduleName } = req.body; + + if (!dleAddress || !moduleId || !moduleAddress || !moduleName) { + return res.status(400).json({ + success: false, + error: 'Все параметры обязательны: dleAddress, moduleId, moduleAddress, moduleName' + }); + } + + console.log(`[DLE Modules] Верификация модуля ${moduleName} по адресу ${moduleAddress}`); + + // Получаем API ключ Etherscan из секретов + const { getSecret } = require('../services/secretStore'); + let apiKey = await getSecret('ETHERSCAN_V2_API_KEY'); + + if (!apiKey) { + return res.status(500).json({ + success: false, + error: 'API ключ Etherscan не найден в секретах' + }); + } + + // Определяем chainId (Sepolia) + const chainId = 11155111; // Sepolia testnet + + // Определяем имя контракта на основе moduleId + let contractName; + if (moduleId === "0x7472656173757279000000000000000000000000000000000000000000000000") { + contractName = "TreasuryModule"; + } else if (moduleId === "0x74696d656c6f636b000000000000000000000000000000000000000000000000") { + contractName = "TimelockModule"; + } else if (moduleId === "0x726561646572000000000000000000000000000000000000000000000000000000") { + contractName = "DLEReader"; + } else { + contractName = "UnknownModule"; + } + + console.log(`[DLE Modules] Верификация ${contractName} на Etherscan...`); + + // Импортируем сервис верификации + const etherscanV2 = require('../services/etherscanV2VerificationService'); + + // Получаем RPC URL для Sepolia + const rpcProviderService = require('../services/rpcProviderService'); + const rpcUrl = await rpcProviderService.getRpcUrlByChainId(chainId); + + if (!rpcUrl) { + return res.status(500).json({ + success: false, + error: 'RPC URL для Sepolia не найден' + }); + } + + // Получаем ABI и bytecode для модуля + const { ethers } = require('ethers'); + const provider = new ethers.JsonRpcProvider(rpcUrl); + + // Получаем код контракта для проверки существования + const code = await provider.getCode(moduleAddress); + + if (code === '0x') { + return res.status(400).json({ + success: false, + error: 'По указанному адресу нет контракта' + }); + } + + // Создаем стандартный JSON input для компиляции + const standardJsonInput = await createStandardJsonInput(contractName, moduleAddress, dleAddress, chainId); + + // Отправляем верификацию на Etherscan + const verificationResult = await etherscanV2.submitVerification({ + chainId: chainId, + contractAddress: moduleAddress, + contractName: `${contractName}.sol:${contractName}`, // Формат: filename.sol:contractname + compilerVersion: "v0.8.20+commit.a1b79de6", // Версия компилятора (точно как в основном контракте) + standardJsonInput: standardJsonInput.standardJsonInput, + constructorArgsHex: standardJsonInput.constructorArgsHex, + apiKey: apiKey + }); + + console.log(`[DLE Modules] Результат верификации:`, verificationResult); + + // Обновляем статус верификации в файле модулей + await updateModuleVerificationStatus(dleAddress, moduleId, 'success'); + + res.json({ + success: true, + data: { + message: `Модуль ${moduleName} успешно верифицирован`, + verificationResult: verificationResult, + contractName: contractName, + moduleAddress: moduleAddress + } + }); + + } catch (error) { + console.error('[DLE Modules] Ошибка верификации модуля:', error); + + // Обновляем статус верификации в файле модулей + if (req.body.dleAddress && req.body.moduleId) { + await updateModuleVerificationStatus(req.body.dleAddress, req.body.moduleId, 'failed'); + } + + res.status(500).json({ + success: false, + error: 'Ошибка верификации модуля: ' + error.message + }); + } +}); + +// Функция для обновления статуса верификации в файле модулей +async function updateModuleVerificationStatus(dleAddress, moduleId, status) { + try { + const fs = require('fs'); + const path = require('path'); + + const tempDir = path.join(__dirname, '../scripts/temp'); + const modulesFileName = `modules-${dleAddress.toLowerCase()}.json`; + const modulesFilePath = path.join(tempDir, modulesFileName); + + if (!fs.existsSync(modulesFilePath)) { + console.log(`[DLE Modules] Файл модулей не найден для обновления статуса: ${modulesFileName}`); + return; + } + + const data = JSON.parse(fs.readFileSync(modulesFilePath, 'utf8')); + + // Обновляем статус верификации для всех сетей + if (data.verification && Array.isArray(data.verification)) { + for (let i = 0; i < data.verification.length; i++) { + if (moduleId === "0x7472656173757279000000000000000000000000000000000000000000000000") { + data.verification[i].treasuryModule = status; + } else if (moduleId === "0x74696d656c6f636b000000000000000000000000000000000000000000000000") { + data.verification[i].timelockModule = status; + } else if (moduleId === "0x726561646572000000000000000000000000000000000000000000000000000000") { + data.verification[i].dleReader = status; + } + } + } + + // Записываем обновленные данные обратно в файл + fs.writeFileSync(modulesFilePath, JSON.stringify(data, null, 2)); + console.log(`[DLE Modules] Статус верификации обновлен для модуля ${moduleId}: ${status}`); + + } catch (error) { + console.error('[DLE Modules] Ошибка обновления статуса верификации:', error); + } +} + +// Функция для создания стандартного JSON input для верификации +async function createStandardJsonInput(contractName, moduleAddress, dleAddress, chainId) { + const fs = require('fs'); + const path = require('path'); + + try { + // Читаем исходный код контракта + let contractSource = ''; + let contractPath = ''; + + if (contractName === 'TreasuryModule') { + contractPath = path.join(__dirname, '../contracts/TreasuryModule.sol'); + } else if (contractName === 'TimelockModule') { + contractPath = path.join(__dirname, '../contracts/TimelockModule.sol'); + } else if (contractName === 'DLEReader') { + contractPath = path.join(__dirname, '../contracts/DLEReader.sol'); + } else { + throw new Error(`Неизвестный контракт: ${contractName}`); + } + + if (!fs.existsSync(contractPath)) { + throw new Error(`Файл контракта не найден: ${contractPath}`); + } + + contractSource = fs.readFileSync(contractPath, 'utf8'); + + // Определяем конструктор аргументы на основе типа контракта + let constructorArgs = []; + + if (contractName === 'TreasuryModule') { + // TreasuryModule: [dleAddress, chainId, emergencyAdmin] + // Нужно получить реальный emergencyAdmin из транзакции деплоя + let emergencyAdmin = dleAddress; // fallback + + try { + // Получаем emergencyAdmin из транзакции деплоя + const provider = new ethers.JsonRpcProvider(await require('../services/rpcProviderService').getRpcUrlByChainId(chainId)); + const history = await provider.getHistory(moduleAddress); + if (history.length > 0) { + const deployTx = history[0]; // Первая транзакция - это деплой + if (deployTx.from) { + emergencyAdmin = deployTx.from; // deployer становится emergencyAdmin + console.log(`[DLE Modules] Найден emergencyAdmin из транзакции деплоя: ${emergencyAdmin}`); + } + } + } catch (error) { + console.log(`[DLE Modules] Не удалось получить emergencyAdmin из транзакции: ${error.message}`); + } + + constructorArgs = [dleAddress, chainId.toString(), emergencyAdmin]; + } else if (contractName === 'TimelockModule') { + // TimelockModule: [dleAddress] + constructorArgs = [dleAddress]; + } else if (contractName === 'DLEReader') { + // DLEReader: [dleAddress] + constructorArgs = [dleAddress]; + } + + // Кодируем конструктор аргументы + const { ethers } = require('ethers'); + const abiCoder = new ethers.AbiCoder(); + + let constructorArgsHex = '0x'; + if (constructorArgs.length > 0) { + // Определяем типы параметров для каждого контракта + let paramTypes = []; + if (contractName === 'TreasuryModule') { + paramTypes = ['address', 'uint256', 'address']; + } else if (contractName === 'TimelockModule' || contractName === 'DLEReader') { + paramTypes = ['address']; + } + + constructorArgsHex = abiCoder.encode(paramTypes, constructorArgs); + } + + // Создаем стандартный JSON input + const standardJsonInput = { + language: "Solidity", + sources: { + [contractName + '.sol']: { + content: contractSource + } + }, + settings: { + optimizer: { + enabled: true, + runs: 200 + }, + evmVersion: "paris", + outputSelection: { + "*": { + "*": ["*"] + } + }, + libraries: {}, + remappings: [ + "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/" + ] + } + }; + + console.log(`[DLE Modules] Создан standardJsonInput для ${contractName}:`, { + contractPath, + constructorArgs, + constructorArgsHex, + sourceLength: contractSource.length + }); + + return { + standardJsonInput: JSON.stringify(standardJsonInput), + constructorArgsHex: constructorArgsHex + }; + + } catch (error) { + console.error(`[DLE Modules] Ошибка создания standardJsonInput для ${contractName}:`, error); + throw error; + } +} + module.exports = router; diff --git a/backend/scripts/deploy/deploy-multichain.js b/backend/scripts/deploy/deploy-multichain.js index d0fde32..a032b8f 100644 --- a/backend/scripts/deploy/deploy-multichain.js +++ b/backend/scripts/deploy/deploy-multichain.js @@ -555,13 +555,52 @@ async function main() { console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(finalResults)); // Сохраняем информацию о модулях в отдельный файл для каждого DLE + // Добавляем информацию о сетях (chainId, rpcUrl) const modulesInfo = { dleAddress: uniqueAddresses[0], + networks: networks.map((rpcUrl, index) => ({ + rpcUrl: rpcUrl, + chainId: null, // Будет заполнено ниже + networkName: null // Будет заполнено ниже + })), modules: moduleResults, verification: verificationResults, deployTimestamp: new Date().toISOString() }; + // Получаем chainId для каждой сети + for (let i = 0; i < networks.length; i++) { + try { + const provider = new hre.ethers.JsonRpcProvider(networks[i]); + const network = await provider.getNetwork(); + modulesInfo.networks[i].chainId = Number(network.chainId); + + // Определяем название сети по chainId + const networkNames = { + 1: 'Ethereum Mainnet', + 5: 'Goerli', + 11155111: 'Sepolia', + 137: 'Polygon Mainnet', + 80001: 'Mumbai', + 56: 'BSC Mainnet', + 97: 'BSC Testnet', + 42161: 'Arbitrum One', + 421614: 'Arbitrum Sepolia', + 10: 'Optimism', + 11155420: 'Optimism Sepolia', + 8453: 'Base', + 84532: 'Base Sepolia' + }; + modulesInfo.networks[i].networkName = networkNames[Number(network.chainId)] || `Chain ID ${Number(network.chainId)}`; + + console.log(`[MULTI_DBG] Сеть ${i + 1}: chainId=${Number(network.chainId)}, name=${modulesInfo.networks[i].networkName}`); + } catch (error) { + console.error(`[MULTI_DBG] Ошибка получения chainId для сети ${i + 1}:`, error.message); + modulesInfo.networks[i].chainId = null; + modulesInfo.networks[i].networkName = `Сеть ${i + 1}`; + } + } + // Создаем директорию temp если её нет const tempDir = path.join(__dirname, '../temp'); if (!fs.existsSync(tempDir)) { diff --git a/backend/services/emailBot.js b/backend/services/emailBot.js index aa8fd51..fc53557 100644 --- a/backend/services/emailBot.js +++ b/backend/services/emailBot.js @@ -352,16 +352,6 @@ class EmailBotService { } // Проверяем, не обрабатывали ли мы уже это письмо - const existingResponse = await encryptedDb.getData('ai_responses', { - message_id: messageId - }); - - if (existingResponse.length > 0) { - // logger.info(`[EmailBot] Игнорируем дубликат письма от ${fromEmail} (Message-ID: ${messageId})`); // Убрано логирование email адреса - return; - } - - // Проверяем, не обрабатывали ли мы уже это письмо (улучшенная проверка) if (messageId) { try { // Проверяем, есть ли уже ответ от AI для этого письма diff --git a/frontend/src/views/smartcontracts/ModulesView.vue b/frontend/src/views/smartcontracts/ModulesView.vue index 3120dc3..ee31695 100644 --- a/frontend/src/views/smartcontracts/ModulesView.vue +++ b/frontend/src/views/smartcontracts/ModulesView.vue @@ -44,6 +44,9 @@
Неактивных модулей: {{ inactiveModulesCount }}
+
+ Последнее обновление: {{ lastUpdateTime }} +
@@ -484,23 +487,50 @@ :class="{ 'active': module.isActive, 'inactive': !module.isActive }" >
-
{{ module.moduleId }}
+
{{ module.moduleName || 'Неизвестный модуль' }}
{{ module.isActive ? 'Активен' : 'Неактивен' }}
+
+ Описание: + {{ module.moduleDescription }} +
+ +
- Адрес: - - {{ shortenAddress(module.moduleAddress) }} - - + Адреса в сетях: +
+
+ {{ addr.networkName }} + + {{ shortenAddress(addr.address) }} + + + + + + + {{ addr.verificationStatus === 'success' ? 'Верифицирован' : + addr.verificationStatus === 'failed' ? 'Ошибка' : 'Ожидает' }} + +
+
+
+ +
+ Дата деплоя: + {{ formatDate(module.deployedAt) }}
@@ -523,6 +553,25 @@ {{ isActivating === module.moduleId ? 'Активация...' : 'Активировать' }} + + +
+ +
@@ -567,6 +616,8 @@ const isLoadingModules = ref(false); const isCreating = ref(false); const isRemoving = ref(null); const isActivating = ref(null); +const isVerifying = ref(null); +const lastUpdateTime = ref(''); // Форма нового модуля const newModule = ref({ @@ -628,7 +679,8 @@ async function loadModules() { const dleAddress = route.query.address; if (!dleAddress) { - console.error('Адрес DLE не указан'); + console.error('[ModulesView] Адрес DLE не указан'); + modules.value = []; return; } @@ -637,9 +689,30 @@ async function loadModules() { // Загружаем модули через modulesService const modulesResponse = await getAllModules(dleAddress); + console.log('[ModulesView] Ответ от API:', modulesResponse); + if (modulesResponse.success) { modules.value = modulesResponse.data.modules || []; - console.log('[ModulesView] Модули загружены:', modules.value); + console.log('[ModulesView] Модули загружены успешно:', { + count: modules.value.length, + modules: modules.value.map(m => ({ + name: m.moduleName, + address: m.moduleAddress, + active: m.isActive, + id: m.moduleId + })), + modulesInitialized: modulesResponse.data.modulesInitialized, + totalModules: modulesResponse.data.totalModules, + activeModules: modulesResponse.data.activeModules + }); + + // Обновляем счетчики + if (modulesResponse.data.modulesInitialized === false) { + console.log('[ModulesView] Модули для DLE не инициализированы'); + } + + // Обновляем время последнего обновления + lastUpdateTime.value = new Date().toLocaleTimeString('ru-RU'); } else { console.error('[ModulesView] Ошибка загрузки модулей:', modulesResponse.error); modules.value = []; @@ -647,6 +720,11 @@ async function loadModules() { } catch (error) { console.error('[ModulesView] Ошибка загрузки модулей:', error); + console.error('[ModulesView] Детали ошибки:', { + message: error.message, + response: error.response?.data, + status: error.response?.status + }); modules.value = []; } finally { isLoadingModules.value = false; @@ -758,12 +836,112 @@ async function activateModule(moduleId) { } } +// Верификация модуля в конкретной сети +async function verifyModule(module, addressInfo) { + try { + const verificationKey = `${module.moduleId}-${addressInfo.networkIndex}`; + isVerifying.value = verificationKey; + console.log('[ModulesView] Верификация модуля в сети:', { module, addressInfo }); + + const dleAddress = route.query.address; + if (!dleAddress) { + alert('Адрес DLE не указан'); + return; + } + + // Вызываем API для верификации модуля + const response = await api.post('/dle-modules/verify-module', { + dleAddress: dleAddress, + moduleId: module.moduleId, + moduleAddress: addressInfo.address, + moduleName: module.moduleName + }); + + if (response.data.success) { + console.log('[ModulesView] Модуль верифицирован:', response.data); + alert(`✅ Модуль ${module.moduleName} успешно верифицирован в сети ${addressInfo.networkName}!`); + + // Перезагружаем модули для обновления данных + await loadModules(); + } else { + console.error('[ModulesView] Ошибка верификации:', response.data.error); + alert('❌ Ошибка верификации: ' + response.data.error); + } + + } catch (error) { + console.error('[ModulesView] Ошибка верификации модуля:', error); + alert('❌ Ошибка верификации: ' + error.message); + } finally { + isVerifying.value = null; + } +} + +function getVerificationButtonText(verificationStatus) { + if (verificationStatus === 'success') { + return 'Верифицирован'; + } else if (verificationStatus === 'failed') { + return 'Ошибка'; + } else { + return 'Верифицировать'; + } +} + +function getVerificationButtonTitle(verificationStatus) { + if (verificationStatus === 'success') { + return 'Модуль уже верифицирован'; + } else if (verificationStatus === 'failed') { + return 'Попробовать верификацию снова'; + } else { + return 'Верифицировать модуль на Etherscan'; + } +} + // Утилиты +function getEtherscanUrl(address, networkIndex, chainId) { + // Если есть chainId, используем его для определения правильного URL + if (chainId) { + const networkUrls = { + 11155111: `https://sepolia.etherscan.io/address/${address}`, // Sepolia + 17000: `https://holesky.etherscan.io/address/${address}`, // Holesky + 421614: `https://sepolia.arbiscan.io/address/${address}`, // Arbitrum Sepolia + 84532: `https://sepolia.basescan.org/address/${address}` // Base Sepolia + }; + + return networkUrls[chainId] || `https://etherscan.io/address/${address}`; + } + + // Fallback на старую логику по networkIndex (для обратной совместимости) + const networkUrls = { + 0: `https://sepolia.etherscan.io/address/${address}`, // Sepolia + 1: `https://mumbai.polygonscan.com/address/${address}`, // Mumbai + 2: `https://testnet.bscscan.com/address/${address}`, // BSC Testnet + 3: `https://sepolia.arbiscan.io/address/${address}` // Arbitrum Sepolia + }; + + return networkUrls[networkIndex] || networkUrls[0]; // fallback на Sepolia +} + function shortenAddress(address) { if (!address) return ''; return `${address.slice(0, 6)}...${address.slice(-4)}`; } +function formatDate(dateString) { + if (!dateString) return ''; + try { + const date = new Date(dateString); + return date.toLocaleDateString('ru-RU', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + } catch (error) { + return dateString; + } +} + // Инициализация onMounted(() => { loadDleData(); @@ -1126,6 +1304,15 @@ onMounted(() => { text-decoration: underline; } +.network-badge { + background: var(--color-primary); + color: white; + padding: 4px 8px; + border-radius: var(--radius-sm); + font-size: 12px; + font-weight: 500; +} + .module-actions { display: flex; gap: 10px;