ваше сообщение коммита
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] Получение всех модулей для 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;
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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 для этого письма
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user