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

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

@@ -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;