ваше сообщение коммита
This commit is contained in:
@@ -162,6 +162,109 @@ router.post('/get-all-modules', async (req, res) => {
|
||||
|
||||
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);
|
||||
if (!rpcUrl) {
|
||||
return res.status(500).json({
|
||||
@@ -174,47 +277,86 @@ router.post('/get-all-modules', async (req, res) => {
|
||||
|
||||
const dleAbi = [
|
||||
"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 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"
|
||||
"0x6d756c7469736967000000000000000000000000000000000000000000000000", // "multisig"
|
||||
"0x646561637469766174696f6e0000000000000000000000000000000000000000", // "deactivation"
|
||||
"0x616e616c79746963730000000000000000000000000000000000000000000000", // "analytics"
|
||||
"0x6e6f74696669636174696f6e7300000000000000000000000000000000000000" // "notifications"
|
||||
"0x74696d656c6f636b000000000000000000000000000000000000000000000000", // "timelock"
|
||||
"0x6d696e7400000000000000000000000000000000000000000000000000000000", // "mint"
|
||||
"0x6275726e00000000000000000000000000000000000000000000000000000000", // "burn"
|
||||
"0x6f7261636c650000000000000000000000000000000000000000000000000000", // "oracle"
|
||||
"0x696e6865726974616e6365000000000000000000000000000000000000000000", // "inheritance"
|
||||
"0x636f6d6d756e69636174696f6e00000000000000000000000000000000000000", // "communication"
|
||||
"0x6170706c69636174696f6e000000000000000000000000000000000000000000" // "application"
|
||||
];
|
||||
|
||||
const modules = [];
|
||||
|
||||
// Проверяем активность известных модулей
|
||||
for (const moduleId of knownModules) {
|
||||
// Проверяем каждый модуль
|
||||
for (const moduleId of standardModuleIds) {
|
||||
try {
|
||||
const isActive = await dle.isModuleActive(moduleId);
|
||||
if (isActive) {
|
||||
const address = await dle.getModuleAddress(moduleId);
|
||||
const moduleAddress = await dle.getModuleAddress(moduleId);
|
||||
|
||||
// Получаем человекочитаемое название модуля
|
||||
const moduleName = getModuleName(moduleId);
|
||||
const moduleDescription = getModuleDescription(moduleId);
|
||||
|
||||
modules.push({
|
||||
id: moduleId,
|
||||
address: address,
|
||||
isActive: isActive
|
||||
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] Ошибка при проверке модуля ${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({
|
||||
success: true,
|
||||
data: {
|
||||
modules: modules
|
||||
modules: modules,
|
||||
modulesInitialized: modulesInitialized,
|
||||
totalModules: standardModuleIds.length,
|
||||
activeModules: modules.length
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Modules] Ошибка при получении всех модулей:', error);
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 для этого письма
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
<div class="info-item">
|
||||
<strong>Неактивных модулей:</strong> {{ inactiveModulesCount }}
|
||||
</div>
|
||||
<div class="info-item" v-if="modules.length > 0">
|
||||
<strong>Последнее обновление:</strong> {{ lastUpdateTime }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -484,23 +487,50 @@
|
||||
:class="{ 'active': module.isActive, 'inactive': !module.isActive }"
|
||||
>
|
||||
<div class="module-header">
|
||||
<h5>{{ module.moduleId }}</h5>
|
||||
<h5>{{ module.moduleName || 'Неизвестный модуль' }}</h5>
|
||||
<span class="module-status" :class="{ 'active': module.isActive, 'inactive': !module.isActive }">
|
||||
{{ module.isActive ? 'Активен' : 'Неактивен' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="module-details">
|
||||
<div class="detail-item" v-if="module.moduleDescription">
|
||||
<strong>Описание:</strong>
|
||||
<span>{{ module.moduleDescription }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Адреса модуля в разных сетях -->
|
||||
<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
|
||||
:href="`https://sepolia.etherscan.io/address/${module.moduleAddress}`"
|
||||
:href="getEtherscanUrl(addr.address, addr.networkIndex, addr.chainId)"
|
||||
target="_blank"
|
||||
class="address-link"
|
||||
>
|
||||
{{ shortenAddress(module.moduleAddress) }}
|
||||
{{ shortenAddress(addr.address) }}
|
||||
<i class="fas fa-external-link-alt"></i>
|
||||
</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>
|
||||
|
||||
@@ -523,6 +553,25 @@
|
||||
<i class="fas fa-check"></i>
|
||||
{{ isActivating === module.moduleId ? 'Активация...' : 'Активировать' }}
|
||||
</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>
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user