ваше сообщение коммита

This commit is contained in:
2025-09-04 09:43:35 +03:00
parent 3d9e797d7b
commit 0a90bd91ba
4 changed files with 848 additions and 79 deletions

View File

@@ -162,6 +162,109 @@ router.post('/get-all-modules', async (req, res) => {
console.log(`[DLE Modules] Получение всех модулей для DLE: ${dleAddress}`); console.log(`[DLE Modules] Получение всех модулей для DLE: ${dleAddress}`);
// Сначала пытаемся получить модули из файлов деплоя
console.log(`[DLE Modules] Вызываем getDeployedModulesInfo...`);
const modulesInfo = await getDeployedModulesInfo(dleAddress);
console.log(`[DLE Modules] Результат getDeployedModulesInfo:`, modulesInfo);
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); const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
if (!rpcUrl) { if (!rpcUrl) {
return res.status(500).json({ return res.status(500).json({
@@ -174,47 +277,86 @@ router.post('/get-all-modules', async (req, res) => {
const dleAbi = [ const dleAbi = [
"function isModuleActive(bytes32 _moduleId) external view returns (bool)", "function isModuleActive(bytes32 _moduleId) external view returns (bool)",
"function getModuleAddress(bytes32 _moduleId) external view returns (address)" "function getModuleAddress(bytes32 _moduleId) external view returns (address)",
"function modulesInitialized() external view returns (bool)"
]; ];
const dle = new ethers.Contract(dleAddress, dleAbi, provider); const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Список известных модулей для проверки // Проверяем инициализацию модулей
const knownModules = [ let modulesInitialized = false;
try {
modulesInitialized = await dle.modulesInitialized();
} catch (error) {
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" "0x7472656173757279000000000000000000000000000000000000000000000000", // "treasury"
"0x6d756c7469736967000000000000000000000000000000000000000000000000", // "multisig" "0x74696d656c6f636b000000000000000000000000000000000000000000000000", // "timelock"
"0x646561637469766174696f6e0000000000000000000000000000000000000000", // "deactivation" "0x6d696e7400000000000000000000000000000000000000000000000000000000", // "mint"
"0x616e616c79746963730000000000000000000000000000000000000000000000", // "analytics" "0x6275726e00000000000000000000000000000000000000000000000000000000", // "burn"
"0x6e6f74696669636174696f6e7300000000000000000000000000000000000000" // "notifications" "0x6f7261636c650000000000000000000000000000000000000000000000000000", // "oracle"
"0x696e6865726974616e6365000000000000000000000000000000000000000000", // "inheritance"
"0x636f6d6d756e69636174696f6e00000000000000000000000000000000000000", // "communication"
"0x6170706c69636174696f6e000000000000000000000000000000000000000000" // "application"
]; ];
const modules = []; const modules = [];
// Проверяем активность известных модулей // Проверяем каждый модуль
for (const moduleId of knownModules) { for (const moduleId of standardModuleIds) {
try { try {
const isActive = await dle.isModuleActive(moduleId); const isActive = await dle.isModuleActive(moduleId);
if (isActive) { if (isActive) {
const address = await dle.getModuleAddress(moduleId); const moduleAddress = await dle.getModuleAddress(moduleId);
// Получаем человекочитаемое название модуля
const moduleName = getModuleName(moduleId);
const moduleDescription = getModuleDescription(moduleId);
modules.push({ modules.push({
id: moduleId, moduleId: moduleId,
address: address, moduleName: moduleName,
isActive: isActive moduleDescription: moduleDescription,
moduleAddress: moduleAddress,
isActive: isActive,
deployedAt: new Date().toISOString() // В реальности нужно брать из событий
}); });
console.log(`[DLE Modules] Найден активный модуль: ${moduleName} по адресу ${moduleAddress}`);
} }
} catch (error) { } catch (error) {
console.log(`[DLE Modules] Ошибка при проверке модуля ${moduleId}:`, error.message); console.log(`[DLE Modules] Ошибка при проверке модуля ${getModuleName(moduleId)}:`, error.message);
} }
} }
console.log(`[DLE Modules] Найдено активных модулей: ${modules.length}`); console.log(`[DLE Modules] Всего найдено активных модулей в блокчейне: ${modules.length}`);
res.json({ res.json({
success: true, success: true,
data: { data: {
modules: modules modules: modules,
modulesInitialized: modulesInitialized,
totalModules: standardModuleIds.length,
activeModules: modules.length
} }
}); });
}
} catch (error) { } catch (error) {
console.error('[DLE Modules] Ошибка при получении всех модулей:', error); console.error('[DLE Modules] Ошибка при получении всех модулей:', error);
@@ -337,15 +479,10 @@ async function getDeployedModulesInfo(dleAddress) {
const path = require('path'); const path = require('path');
try { try {
// Ищем файл модулей для конкретного DLE // Ищем файл модулей в правильной директории
const deployDir = path.join(__dirname, '../temp'); const tempDir = path.join(__dirname, '../scripts/temp');
if (!fs.existsSync(deployDir)) {
return { modules: [], verification: {} };
}
// Ищем файл по адресу DLE
const modulesFileName = `modules-${dleAddress.toLowerCase()}.json`; const modulesFileName = `modules-${dleAddress.toLowerCase()}.json`;
const modulesFilePath = path.join(deployDir, modulesFileName); const modulesFilePath = path.join(tempDir, modulesFileName);
if (!fs.existsSync(modulesFilePath)) { if (!fs.existsSync(modulesFilePath)) {
console.log(`[DLE Modules] Файл модулей не найден: ${modulesFileName}`); console.log(`[DLE Modules] Файл модулей не найден: ${modulesFileName}`);
@@ -356,10 +493,26 @@ async function getDeployedModulesInfo(dleAddress) {
const data = JSON.parse(fs.readFileSync(modulesFilePath, 'utf8')); const data = JSON.parse(fs.readFileSync(modulesFilePath, 'utf8'));
console.log(`[DLE Modules] Загружена информация о модулях для DLE: ${dleAddress}`); 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 { return {
modules: data.modules || [], modules: data.modules || [],
verification: data.verification || {}, verification: data.verification || {},
deployTimestamp: data.deployTimestamp deployTimestamp: data.deployTimestamp,
networks: data.networks || [], // ✅ Добавляем поле networks
modulesWithVerification: modulesWithVerification
}; };
} catch (error) { } catch (error) {
console.error(`Ошибка при чтении файла ${modulesFileName}:`, 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; module.exports = router;

View File

@@ -555,13 +555,52 @@ async function main() {
console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(finalResults)); console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(finalResults));
// Сохраняем информацию о модулях в отдельный файл для каждого DLE // Сохраняем информацию о модулях в отдельный файл для каждого DLE
// Добавляем информацию о сетях (chainId, rpcUrl)
const modulesInfo = { const modulesInfo = {
dleAddress: uniqueAddresses[0], dleAddress: uniqueAddresses[0],
networks: networks.map((rpcUrl, index) => ({
rpcUrl: rpcUrl,
chainId: null, // Будет заполнено ниже
networkName: null // Будет заполнено ниже
})),
modules: moduleResults, modules: moduleResults,
verification: verificationResults, verification: verificationResults,
deployTimestamp: new Date().toISOString() 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 если её нет // Создаем директорию temp если её нет
const tempDir = path.join(__dirname, '../temp'); const tempDir = path.join(__dirname, '../temp');
if (!fs.existsSync(tempDir)) { if (!fs.existsSync(tempDir)) {

View File

@@ -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) { if (messageId) {
try { try {
// Проверяем, есть ли уже ответ от AI для этого письма // Проверяем, есть ли уже ответ от AI для этого письма

View File

@@ -44,6 +44,9 @@
<div class="info-item"> <div class="info-item">
<strong>Неактивных модулей:</strong> {{ inactiveModulesCount }} <strong>Неактивных модулей:</strong> {{ inactiveModulesCount }}
</div> </div>
<div class="info-item" v-if="modules.length > 0">
<strong>Последнее обновление:</strong> {{ lastUpdateTime }}
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -484,23 +487,50 @@
:class="{ 'active': module.isActive, 'inactive': !module.isActive }" :class="{ 'active': module.isActive, 'inactive': !module.isActive }"
> >
<div class="module-header"> <div class="module-header">
<h5>{{ module.moduleId }}</h5> <h5>{{ module.moduleName || 'Неизвестный модуль' }}</h5>
<span class="module-status" :class="{ 'active': module.isActive, 'inactive': !module.isActive }"> <span class="module-status" :class="{ 'active': module.isActive, 'inactive': !module.isActive }">
{{ module.isActive ? 'Активен' : 'Неактивен' }} {{ module.isActive ? 'Активен' : 'Неактивен' }}
</span> </span>
</div> </div>
<div class="module-details"> <div class="module-details">
<div class="detail-item" v-if="module.moduleDescription">
<strong>Описание:</strong>
<span>{{ module.moduleDescription }}</span>
</div>
<!-- Адреса модуля в разных сетях -->
<div class="detail-item"> <div class="detail-item">
<strong>Адрес:</strong> <strong>Адреса в сетях:</strong>
<div class="addresses-list">
<div
v-for="addr in module.addresses"
:key="`${module.moduleId}-${addr.networkIndex}`"
class="address-item"
>
<span class="network-badge">{{ addr.networkName }}</span>
<a <a
:href="`https://sepolia.etherscan.io/address/${module.moduleAddress}`" :href="getEtherscanUrl(addr.address, addr.networkIndex, addr.chainId)"
target="_blank" target="_blank"
class="address-link" class="address-link"
> >
{{ shortenAddress(module.moduleAddress) }} {{ shortenAddress(addr.address) }}
<i class="fas fa-external-link-alt"></i> <i class="fas fa-external-link-alt"></i>
</a> </a>
<span class="verification-status" :class="addr.verificationStatus">
<i class="fas fa-check-circle" v-if="addr.verificationStatus === 'success'"></i>
<i class="fas fa-times-circle" v-else-if="addr.verificationStatus === 'failed'"></i>
<i class="fas fa-clock" v-else></i>
{{ addr.verificationStatus === 'success' ? 'Верифицирован' :
addr.verificationStatus === 'failed' ? 'Ошибка' : 'Ожидает' }}
</span>
</div>
</div>
</div>
<div class="detail-item" v-if="module.deployedAt">
<strong>Дата деплоя:</strong>
<span>{{ formatDate(module.deployedAt) }}</span>
</div> </div>
</div> </div>
@@ -523,6 +553,25 @@
<i class="fas fa-check"></i> <i class="fas fa-check"></i>
{{ isActivating === module.moduleId ? 'Активация...' : 'Активировать' }} {{ isActivating === module.moduleId ? 'Активация...' : 'Активировать' }}
</button> </button>
<!-- Кнопки верификации для каждой сети -->
<div class="verification-buttons">
<button
v-for="addr in module.addresses"
:key="`verify-${module.moduleId}-${addr.networkIndex}`"
class="btn btn-sm btn-info verification-btn"
@click="verifyModule(module, addr)"
:disabled="isVerifying === `${module.moduleId}-${addr.networkIndex}`"
:title="getVerificationButtonTitle(addr.verificationStatus)"
>
<i class="fas fa-check-circle" v-if="addr.verificationStatus === 'success'"></i>
<i class="fas fa-times-circle" v-else-if="addr.verificationStatus === 'failed'"></i>
<i class="fas fa-spinner fa-spin" v-else-if="isVerifying === `${module.moduleId}-${addr.networkIndex}`"></i>
<i class="fas fa-shield-alt" v-else></i>
{{ getVerificationButtonText(addr.verificationStatus) }}
<span class="network-indicator">{{ addr.networkName }}</span>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -567,6 +616,8 @@ const isLoadingModules = ref(false);
const isCreating = ref(false); const isCreating = ref(false);
const isRemoving = ref(null); const isRemoving = ref(null);
const isActivating = ref(null); const isActivating = ref(null);
const isVerifying = ref(null);
const lastUpdateTime = ref('');
// Форма нового модуля // Форма нового модуля
const newModule = ref({ const newModule = ref({
@@ -628,7 +679,8 @@ async function loadModules() {
const dleAddress = route.query.address; const dleAddress = route.query.address;
if (!dleAddress) { if (!dleAddress) {
console.error('Адрес DLE не указан'); console.error('[ModulesView] Адрес DLE не указан');
modules.value = [];
return; return;
} }
@@ -637,9 +689,30 @@ async function loadModules() {
// Загружаем модули через modulesService // Загружаем модули через modulesService
const modulesResponse = await getAllModules(dleAddress); const modulesResponse = await getAllModules(dleAddress);
console.log('[ModulesView] Ответ от API:', modulesResponse);
if (modulesResponse.success) { if (modulesResponse.success) {
modules.value = modulesResponse.data.modules || []; 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 { } else {
console.error('[ModulesView] Ошибка загрузки модулей:', modulesResponse.error); console.error('[ModulesView] Ошибка загрузки модулей:', modulesResponse.error);
modules.value = []; modules.value = [];
@@ -647,6 +720,11 @@ async function loadModules() {
} catch (error) { } catch (error) {
console.error('[ModulesView] Ошибка загрузки модулей:', error); console.error('[ModulesView] Ошибка загрузки модулей:', error);
console.error('[ModulesView] Детали ошибки:', {
message: error.message,
response: error.response?.data,
status: error.response?.status
});
modules.value = []; modules.value = [];
} finally { } finally {
isLoadingModules.value = false; 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) { function shortenAddress(address) {
if (!address) return ''; if (!address) return '';
return `${address.slice(0, 6)}...${address.slice(-4)}`; 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(() => { onMounted(() => {
loadDleData(); loadDleData();
@@ -1126,6 +1304,15 @@ onMounted(() => {
text-decoration: underline; 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 { .module-actions {
display: flex; display: flex;
gap: 10px; gap: 10px;