diff --git a/backend/app.js b/backend/app.js index 217ad5e..bcebc94 100644 --- a/backend/app.js +++ b/backend/app.js @@ -95,7 +95,7 @@ const dleTokensRoutes = require('./routes/dleTokens'); // Функции ток const dleAnalyticsRoutes = require('./routes/dleAnalytics'); // Аналитика и история const compileRoutes = require('./routes/compile'); // Компиляция контрактов const dleMultichainRoutes = require('./routes/dleMultichain'); // Мультичейн функции -const dleHistoryRoutes = require('./routes/dleHistory'); // Расширенная история +const { router: dleHistoryRoutes } = require('./routes/dleHistory'); // Расширенная история const systemRoutes = require('./routes/system'); // Добавляем импорт маршрутов системного мониторинга const app = express(); diff --git a/backend/constants/moduleIds.js b/backend/constants/moduleIds.js new file mode 100644 index 0000000..97ad2d4 --- /dev/null +++ b/backend/constants/moduleIds.js @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2024-2025 Тарабанов Александр Викторович + * All rights reserved. + * + * This software is proprietary and confidential. + * Unauthorized copying, modification, or distribution is prohibited. + * + * For licensing inquiries: info@hb3-accelerator.com + * Website: https://hb3-accelerator.com + * GitHub: https://github.com/HB3-ACCELERATOR + */ + +/** + * Стандартные ID модулей DLE + * Эти ID используются для идентификации модулей в смарт-контракте DLE + * + * Формат: ASCII-коды названий модулей, дополненные нулями до 32 байт + * Это не стандартные keccak256 хеши, а просто padded ASCII строки + */ +const MODULE_IDS = { + // Treasury Module - модуль для управления казной + TREASURY: '0x7472656173757279000000000000000000000000000000000000000000000000', + + // Timelock Module - модуль для задержки выполнения операций + TIMELOCK: '0x74696d656c6f636b000000000000000000000000000000000000000000000000', + + // Reader Module - модуль для чтения данных DLE + READER: '0x7265616465720000000000000000000000000000000000000000000000000000' +}; + +/** + * Маппинг типов модулей на их ID + * Используется для удобства работы с модулями в API + */ +const MODULE_TYPE_TO_ID = { + treasury: MODULE_IDS.TREASURY, + timelock: MODULE_IDS.TIMELOCK, + reader: MODULE_IDS.READER +}; + +/** + * Маппинг ID модулей на их типы + * Обратный маппинг для удобства + */ +const MODULE_ID_TO_TYPE = { + [MODULE_IDS.TREASURY]: 'treasury', + [MODULE_IDS.TIMELOCK]: 'timelock', + [MODULE_IDS.READER]: 'reader' +}; + +/** + * Названия модулей для отображения + */ +const MODULE_NAMES = { + treasury: 'Treasury Module', + timelock: 'Timelock Module', + reader: 'Reader Module' +}; + +/** + * Описания модулей + */ +const MODULE_DESCRIPTIONS = { + treasury: 'Модуль для управления казной и финансовыми операциями DLE', + timelock: 'Модуль для задержки выполнения критических операций', + reader: 'Модуль для чтения и получения данных о состоянии DLE' +}; + +module.exports = { + MODULE_IDS, + MODULE_TYPE_TO_ID, + MODULE_ID_TO_TYPE, + MODULE_NAMES, + MODULE_DESCRIPTIONS +}; diff --git a/backend/contracts/DLE.sol b/backend/contracts/DLE.sol index 19eebf4..d098e1a 100644 --- a/backend/contracts/DLE.sol +++ b/backend/contracts/DLE.sol @@ -16,10 +16,30 @@ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + interface IERC1271 { function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue); } +/** + * @dev Интерфейс для мультичейн метаданных (EIP-3668 inspired) + */ +interface IMultichainMetadata { + /** + * @dev Возвращает информацию о мультичейн развертывании + * @return supportedChainIds Массив всех поддерживаемых chain ID + * @return defaultVotingChain ID сети по умолчанию для голосования (может быть любая из поддерживаемых) + */ + function getMultichainInfo() external view returns (uint256[] memory supportedChainIds, uint256 defaultVotingChain); + + /** + * @dev Возвращает адреса контракта в других сетях + * @return chainIds Массив chain ID где развернут контракт + * @return addresses Массив адресов контракта в соответствующих сетях + */ + function getMultichainAddresses() external view returns (uint256[] memory chainIds, address[] memory addresses); +} + /** * @title DLE (Digital Legal Entity) * @dev Основной контракт DLE с модульной архитектурой, Single-Chain Governance @@ -31,7 +51,7 @@ interface IERC1271 { * - Токены служат только для голосования и управления DLE * - Все операции с токенами требуют коллективного решения */ -contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard { +contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMetadata { using ECDSA for bytes32; struct DLEInfo { string name; @@ -88,8 +108,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard { // Модули mapping(bytes32 => address) public modules; mapping(bytes32 => bool) public activeModules; - bool public modulesInitialized; // Флаг инициализации базовых модулей - address public immutable initializer; // Адрес, имеющий право на однократную инициализацию модулей + address public immutable initializer; // Адрес, имеющий право на однократную инициализацию логотипа // Предложения mapping(uint256 => Proposal) public proposals; @@ -709,41 +728,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard { - /** - * @dev Инициализировать базовые модули (вызывается только один раз при деплое) - * @param _treasuryAddress Адрес Treasury модуля - * @param _timelockAddress Адрес Timelock модуля - * @param _readerAddress Адрес Reader модуля - */ - function initializeBaseModules( - address _treasuryAddress, - address _timelockAddress, - address _readerAddress - ) external { - if (modulesInitialized) revert ErrProposalExecuted(); // keep existing error to avoid new identifier - if (msg.sender != initializer) revert ErrOnlyInitializer(); - if (_treasuryAddress == address(0) || _timelockAddress == address(0) || _readerAddress == address(0)) revert ErrZeroAddress(); - - // Добавляем базовые модули без голосования (только при инициализации) - bytes32 treasuryId = keccak256("TREASURY"); - bytes32 timelockId = keccak256("TIMELOCK"); - bytes32 readerId = keccak256("READER"); - - modules[treasuryId] = _treasuryAddress; - activeModules[treasuryId] = true; - - modules[timelockId] = _timelockAddress; - activeModules[timelockId] = true; - - modules[readerId] = _readerAddress; - activeModules[readerId] = true; - - modulesInitialized = true; - - emit ModuleAdded(treasuryId, _treasuryAddress); - emit ModuleAdded(timelockId, _timelockAddress); - emit ModuleAdded(readerId, _readerAddress); - } /** * @dev Создать предложение о добавлении модуля @@ -897,6 +881,150 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard { return currentChainId; } + /** + * @dev Получить URI логотипа токена (стандартная функция для блокчейн-сканеров) + * @return URI логотипа или пустую строку если не установлен + */ + function tokenURI() external view returns (string memory) { + return logoURI; + } + + /** + * @dev Получить URI логотипа токена (альтернативная функция для блокчейн-сканеров) + * @return URI логотипа или пустую строку если не установлен + */ + function logo() external view returns (string memory) { + return logoURI; + } + + /** + * @dev Получить информацию о мультичейн развертывании для блокчейн-сканеров + * @return chains Массив всех поддерживаемых chain ID (все сети равноправны) + * @return defaultVotingChain ID сети по умолчанию для голосования (может быть любая из поддерживаемых) + */ + function getMultichainInfo() external view returns (uint256[] memory chains, uint256 defaultVotingChain) { + return (supportedChainIds, currentChainId); + } + + /** + * @dev Получить адреса контракта в других сетях (для мультичейн сканеров) + * @return chainIds Массив chain ID где развернут контракт + * @return addresses Массив адресов контракта в соответствующих сетях + */ + function getMultichainAddresses() external view returns (uint256[] memory chainIds, address[] memory addresses) { + uint256[] memory chains = new uint256[](supportedChainIds.length); + address[] memory addrs = new address[](supportedChainIds.length); + + for (uint256 i = 0; i < supportedChainIds.length; i++) { + chains[i] = supportedChainIds[i]; + addrs[i] = address(this); // CREATE2 обеспечивает одинаковые адреса + } + + return (chains, addrs); + } + + /** + * @dev Получить мультичейн метаданные в JSON формате для блокчейн-сканеров + * @return metadata JSON строка с информацией о мультичейн развертывании + * + * Архитектура: Single-Chain Governance - голосование происходит в одной сети, + * но исполнение может быть в любой из поддерживаемых сетей через подписи. + */ + function getMultichainMetadata() external view returns (string memory metadata) { + // Формируем JSON с информацией о мультичейн развертывании + string memory json = string(abi.encodePacked( + '{"multichain": {', + '"supportedChains": [' + )); + + for (uint256 i = 0; i < supportedChainIds.length; i++) { + if (i > 0) { + json = string(abi.encodePacked(json, ',')); + } + json = string(abi.encodePacked(json, _toString(supportedChainIds[i]))); + } + + json = string(abi.encodePacked( + json, + '],', + '"defaultVotingChain": ', + _toString(currentChainId), + ',', + '"note": "All chains are equal, voting can happen on any supported chain",', + '"contractAddress": "', + _toHexString(address(this)), + '"', + '}}' + )); + + return json; + } + + /** + * @dev Вспомогательная функция для конвертации uint256 в string + */ + function _toString(uint256 value) internal pure returns (string memory) { + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); + } + + /** + * @dev Вспомогательная функция для конвертации address в hex string + */ + function _toHexString(address addr) internal pure returns (string memory) { + return _toHexString(abi.encodePacked(addr)); + } + + /** + * @dev Вспомогательная функция для конвертации bytes в hex string + */ + function _toHexString(bytes memory data) internal pure returns (string memory) { + bytes memory alphabet = "0123456789abcdef"; + bytes memory str = new bytes(2 + data.length * 2); + str[0] = "0"; + str[1] = "x"; + for (uint256 i = 0; i < data.length; i++) { + str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))]; + str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))]; + } + return string(str); + } + + /** + * @dev Получить информацию об архитектуре мультичейн governance + * @return architecture Описание архитектуры в JSON формате + */ + function getGovernanceArchitecture() external pure returns (string memory architecture) { + return string(abi.encodePacked( + '{"architecture": {', + '"type": "Single-Chain Governance",', + '"description": "Voting happens on one chain per proposal, execution on any supported chain",', + '"features": [', + '"Equal chain support - no primary chain",', + '"Cross-chain execution via signatures",', + '"Deterministic addresses via CREATE2",', + '"No bridge dependencies"', + '],', + '"voting": "One chain per proposal (chosen by proposer)",', + '"execution": "Any supported chain via signature verification"', + '}}' + )); + } + // API функции вынесены в отдельный reader контракт для экономии байт-кода // 0=Pending, 1=Succeeded, 2=Defeated, 3=Executed, 4=Canceled, 5=ReadyForExecution diff --git a/backend/contracts/MockNoop.sol b/backend/contracts/MockNoop.sol index c1cfe05..5b3d6e6 100644 --- a/backend/contracts/MockNoop.sol +++ b/backend/contracts/MockNoop.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: PROPRIETARY +// SPDX-License-Identifier: MIT // Copyright (c) 2024-2025 Тарабанов Александр Викторович // All rights reserved. // @@ -8,8 +8,6 @@ // For licensing inquiries: info@hb3-accelerator.com // Website: https://hb3-accelerator.com // GitHub: https://github.com/HB3-ACCELERATOR - -// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** diff --git a/backend/contracts/MockToken.sol b/backend/contracts/MockToken.sol index 26242ae..65f78da 100644 --- a/backend/contracts/MockToken.sol +++ b/backend/contracts/MockToken.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: PROPRIETARY +// SPDX-License-Identifier: MIT // Copyright (c) 2024-2025 Тарабанов Александр Викторович // All rights reserved. // @@ -8,8 +8,6 @@ // For licensing inquiries: info@hb3-accelerator.com // Website: https://hb3-accelerator.com // GitHub: https://github.com/HB3-ACCELERATOR - -// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/backend/docs/MODULE_IDS.md b/backend/docs/MODULE_IDS.md new file mode 100644 index 0000000..f415446 --- /dev/null +++ b/backend/docs/MODULE_IDS.md @@ -0,0 +1,173 @@ +# ID Модулей DLE + +## Обзор + +В системе DLE каждый модуль имеет уникальный идентификатор (ID), который используется для: +- Идентификации модуля в смарт-контракте +- Создания governance предложений +- Проверки статуса модуля + +## Формат ID + +ID модулей представляют собой 32-байтные хеши в формате: +``` +0x[32 байта в hex формате] +``` + +### Стандартные модули + +Стандартные модули используют ASCII-коды названий, дополненные нулями до 32 байт: + +| Модуль | ID | Описание | +|--------|----|---------| +| **Treasury** | `0x7472656173757279000000000000000000000000000000000000000000000000` | Модуль управления казной | +| **Timelock** | `0x74696d656c6f636b000000000000000000000000000000000000000000000000` | Модуль задержки выполнения | +| **Reader** | `0x7265616465720000000000000000000000000000000000000000000000000000` | Модуль чтения данных | + +### Дополнительные модули + +Дополнительные модули могут использовать другие форматы ID: + +| Модуль | ID | Описание | +|--------|----|---------| +| **Multisig** | `0x6d756c7469736967000000000000000000000000000000000000000000000000` | Мультиподписный модуль | +| **Deactivation** | `0x646561637469766174696f6e0000000000000000000000000000000000000000` | Модуль деактивации | +| **Analytics** | `0x616e616c79746963730000000000000000000000000000000000000000000000` | Модуль аналитики | +| **Notifications** | `0x6e6f74696669636174696f6e7300000000000000000000000000000000000000` | Модуль уведомлений | + +## Использование в коде + +### Константы + +Все ID модулей определены в файле `backend/constants/moduleIds.js`: + +```javascript +const { MODULE_IDS, MODULE_TYPE_TO_ID, MODULE_ID_TO_TYPE } = require('../constants/moduleIds'); + +// Использование +const treasuryId = MODULE_IDS.TREASURY; +const moduleType = MODULE_ID_TO_TYPE[moduleId]; +const moduleId = MODULE_TYPE_TO_ID['treasury']; +``` + +### API Endpoints + +ID модулей используются в следующих API endpoints: + +- `POST /api/dle-modules/initialize-modules` - инициализация модулей +- `POST /api/dle-modules/deploy-module` - деплой модуля +- `GET /api/dle-modules/check-module-status` - проверка статуса модуля +- `POST /api/dle-history/get-extended-history` - получение истории + +### Смарт-контракт + +В смарт-контракте DLE ID модулей используются в: + +```solidity +// Добавление модуля +function createAddModuleProposal( + string memory _description, + uint256 _duration, + bytes32 _moduleId, // <-- ID модуля + address _moduleAddress, + uint256 _chainId +) external returns (uint256); + +// Проверка модуля +function isModuleActive(bytes32 _moduleId) external view returns (bool); +function getModuleAddress(bytes32 _moduleId) external view returns (address); +``` + +## Добавление новых модулей + +### 1. Определить ID модуля + +```javascript +// В backend/constants/moduleIds.js +const MODULE_IDS = { + // ... существующие модули + NEW_MODULE: '0x6e65776d6f64756c650000000000000000000000000000000000000000000000' +}; +``` + +### 2. Обновить маппинги + +```javascript +const MODULE_TYPE_TO_ID = { + // ... существующие модули + newModule: MODULE_IDS.NEW_MODULE +}; + +const MODULE_ID_TO_TYPE = { + // ... существующие модули + [MODULE_IDS.NEW_MODULE]: 'newModule' +}; + +const MODULE_NAMES = { + // ... существующие модули + newModule: 'New Module' +}; +``` + +### 3. Обновить функцию getModuleName + +```javascript +// В backend/routes/dleHistory.js +function getModuleName(moduleId) { + if (MODULE_ID_TO_TYPE[moduleId]) { + const moduleType = MODULE_ID_TO_TYPE[moduleId]; + return MODULE_NAMES[moduleType] || moduleType; + } + + const additionalModuleNames = { + // ... существующие модули + '0x6e65776d6f64756c650000000000000000000000000000000000000000000000': 'New Module' + }; + + return additionalModuleNames[moduleId] || `Module ${moduleId}`; +} +``` + +## Безопасность + +- ID модулей должны быть уникальными +- Не используйте предсказуемые ID для критических модулей +- Все изменения ID должны проходить через governance + +## Миграция + +При изменении ID модуля: + +1. Создать governance предложение для удаления старого модуля +2. Создать governance предложение для добавления нового модуля с новым ID +3. Обновить константы в коде +4. Обновить базу данных (если необходимо) + +## Примеры + +### Создание предложения для добавления модуля + +```javascript +const moduleId = MODULE_TYPE_TO_ID['treasury']; +const moduleAddress = '0x1234567890123456789012345678901234567890'; + +// Создание предложения через governance +const proposalId = await dleContract.createAddModuleProposal( + 'Добавить Treasury модуль', + 86400, // 1 день + moduleId, + moduleAddress, + 1 // Ethereum mainnet +); +``` + +### Проверка статуса модуля + +```javascript +const moduleId = MODULE_TYPE_TO_ID['treasury']; +const isActive = await dleContract.isModuleActive(moduleId); +const moduleAddress = await dleContract.getModuleAddress(moduleId); + +console.log(`Treasury модуль: ${isActive ? 'активен' : 'неактивен'}`); +console.log(`Адрес: ${moduleAddress}`); +``` diff --git a/backend/routes/dleHistory.js b/backend/routes/dleHistory.js index 80046cf..82a793b 100644 --- a/backend/routes/dleHistory.js +++ b/backend/routes/dleHistory.js @@ -14,6 +14,7 @@ const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); +const { MODULE_IDS, MODULE_ID_TO_TYPE, MODULE_NAMES } = require('../constants/moduleIds'); // Получить расширенную историю DLE router.post('/get-extended-history', async (req, res) => { @@ -342,14 +343,21 @@ router.post('/get-extended-history', async (req, res) => { // Вспомогательные функции function getModuleName(moduleId) { - const moduleNames = { - '0x7472656173757279000000000000000000000000000000000000000000000000': 'Treasury', + // Проверяем стандартные модули + if (MODULE_ID_TO_TYPE[moduleId]) { + const moduleType = MODULE_ID_TO_TYPE[moduleId]; + return MODULE_NAMES[moduleType] || moduleType; + } + + // Дополнительные модули (если появятся в будущем) + const additionalModuleNames = { '0x6d756c7469736967000000000000000000000000000000000000000000000000': 'Multisig', '0x646561637469766174696f6e0000000000000000000000000000000000000000': 'Deactivation', '0x616e616c79746963730000000000000000000000000000000000000000000000': 'Analytics', '0x6e6f74696669636174696f6e7300000000000000000000000000000000000000': 'Notifications' }; - return moduleNames[moduleId] || `Module ${moduleId}`; + + return additionalModuleNames[moduleId] || `Module ${moduleId}`; } function getChainName(chainId) { @@ -364,4 +372,9 @@ function getChainName(chainId) { return chainNames[chainId] || `Chain ID: ${chainId}`; } -module.exports = router; +// Экспортируем функции для использования в других модулях +module.exports = { + router, + getModuleName, + getChainName +}; diff --git a/backend/routes/dleModules.js b/backend/routes/dleModules.js index 8ecc4ba..80f5c39 100644 --- a/backend/routes/dleModules.js +++ b/backend/routes/dleModules.js @@ -18,6 +18,7 @@ const hre = require('hardhat'); const rpcProviderService = require('../services/rpcProviderService'); const { spawn } = require('child_process'); const path = require('path'); +const { MODULE_TYPE_TO_ID, MODULE_NAMES, MODULE_DESCRIPTIONS } = require('../constants/moduleIds'); // Утилитарная функция для автоматической компиляции контрактов async function autoCompileContracts() { @@ -274,17 +275,10 @@ router.post('/prepare-initialize-modules-all-networks', async (req, res) => { return res.status(400).json({ success: false, error: 'Не найдены поддерживаемые сети для DLE' }); } - // Интерфейс функции инициализации - const dleIface = new Interface([ - 'function initializeBaseModules(address _treasuryAddress, address _timelockAddress, address _readerAddress)' - ]); + // Модули инициализируются только через governance предложения - // Module IDs - const moduleIds = { - treasury: '0x7472656173757279000000000000000000000000000000000000000000000000', - timelock: '0x74696d656c6f636b000000000000000000000000000000000000000000000000', - reader: '0x7265616465720000000000000000000000000000000000000000000000000000' - }; + // Module IDs - используем константы + const moduleIds = MODULE_TYPE_TO_ID; const results = []; for (const network of supportedNetworks) { @@ -294,12 +288,12 @@ router.post('/prepare-initialize-modules-all-networks', async (req, res) => { dleAddress, [ 'function getModuleAddress(bytes32 _moduleId) external view returns (address)', - 'function modulesInitialized() external view returns (bool)' ], provider ); - const already = await dle.modulesInitialized(); + // Модули теперь инициализируются только через governance + const already = false; const treasuryAddress = await dle.getModuleAddress(moduleIds.treasury); const timelockAddress = await dle.getModuleAddress(moduleIds.timelock); const readerAddress = await dle.getModuleAddress(moduleIds.reader); @@ -318,11 +312,8 @@ router.post('/prepare-initialize-modules-all-networks', async (req, res) => { continue; } - const data = dleIface.encodeFunctionData('initializeBaseModules', [ - treasuryAddress, - timelockAddress, - readerAddress - ]); + // Модули инициализируются через governance предложения, а не напрямую + const data = null; results.push({ chainId: network.chainId, @@ -460,7 +451,7 @@ router.post('/get-all-modules', async (req, res) => { success: true, data: { modules: [], - modulesInitialized: false, + requiresGovernance: true, totalModules: 0, activeModules: 0, supportedNetworks: [] @@ -506,23 +497,13 @@ 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 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] Ошибка при проверке инициализации модулей в сети ${network.chainId}:`, error.message); - continue; - } - - if (!modulesInitialized) { - console.log(`[DLE Modules] Модули не инициализированы в сети ${network.chainId}, но проверяем отдельные модули`); - } + // Модули инициализируются только через governance + console.log(`[DLE Modules] Модули инициализируются через governance предложения в сети ${network.chainId}`); // Проверяем каждый тип модуля for (const [moduleType, moduleInfo] of Object.entries(moduleGroups)) { @@ -568,7 +549,7 @@ router.post('/get-all-modules', async (req, res) => { success: true, data: { modules: formattedModules, - modulesInitialized: formattedModules.length > 0, + requiresGovernance: true, totalModules: formattedModules.length, activeModules: formattedModules.length, supportedNetworks: supportedNetworks @@ -1054,7 +1035,7 @@ router.post('/check-modules-status', async (req, res) => { return res.json({ success: true, data: { - modulesInitialized: false, + requiresGovernance: true, initializer: null, modules: [], networks: [] @@ -1067,7 +1048,6 @@ router.post('/check-modules-status', async (req, res) => { const provider = new ethers.JsonRpcProvider(network.rpcUrl); const dleAbi = [ - "function modulesInitialized() external view returns (bool)", "function initializer() external view returns (address)", "function isModuleActive(bytes32 _moduleId) external view returns (bool)", "function getModuleAddress(bytes32 _moduleId) external view returns (address)" @@ -1075,16 +1055,11 @@ router.post('/check-modules-status', async (req, res) => { const dle = new ethers.Contract(dleAddress, dleAbi, provider); - // Проверяем статус инициализации - const modulesInitialized = await dle.modulesInitialized(); + // Модули инициализируются только через governance const initializer = await dle.initializer(); // Проверяем модули - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; const modules = []; for (const [name, moduleId] of Object.entries(moduleIds)) { @@ -1111,7 +1086,7 @@ router.post('/check-modules-status', async (req, res) => { res.json({ success: true, data: { - modulesInitialized: modulesInitialized, + requiresGovernance: true, initializer: initializer, modules: modules, networks: supportedNetworks @@ -1272,17 +1247,12 @@ router.post('/initialize-modules-all-networks', async (req, res) => { const results = []; const dleAbi = [ - "function initializeBaseModules(address _treasuryAddress, address _timelockAddress, address _readerAddress) external", - "function modulesInitialized() external view returns (bool)", - "function getModuleAddress(bytes32 _moduleId) external view returns (address)" + "function getModuleAddress(bytes32 _moduleId) external view returns (address)", + "function isModuleActive(bytes32 _moduleId) external view returns (bool)" ]; // ID модулей - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; for (const network of supportedNetworks) { console.log(`[DLE Modules] Инициализация модулей в сети: ${network.networkName} (${network.chainId})`); @@ -1292,19 +1262,8 @@ router.post('/initialize-modules-all-networks', async (req, res) => { const wallet = new ethers.Wallet(privateKey, provider); const dle = new ethers.Contract(dleAddress, dleAbi, wallet); - // Проверяем, уже ли инициализированы модули - const modulesInitialized = await dle.modulesInitialized(); - - if (modulesInitialized) { - console.log(`[DLE Modules] Модули уже инициализированы в сети ${network.chainId}`); - results.push({ - chainId: network.chainId, - networkName: network.networkName, - status: 'already_initialized', - message: 'Модули уже инициализированы' - }); - continue; - } + // Модули инициализируются только через governance + console.log(`[DLE Modules] Модули инициализируются через governance предложения в сети ${network.chainId}`); // Получаем адреса модулей const treasuryAddress = await dle.getModuleAddress(moduleIds.treasury); @@ -1325,17 +1284,16 @@ router.post('/initialize-modules-all-networks', async (req, res) => { continue; } - // Инициализируем модули - const tx = await dle.initializeBaseModules(treasuryAddress, timelockAddress, readerAddress); - await tx.wait(); - - console.log(`[DLE Modules] Модули успешно инициализированы в сети ${network.chainId}`); + // Модули инициализируются только через governance предложения + console.log(`[DLE Modules] Модули должны быть инициализированы через governance предложения в сети ${network.chainId}`); results.push({ chainId: network.chainId, networkName: network.networkName, - status: 'success', - message: 'Модули успешно инициализированы', - transactionHash: tx.hash + status: 'requires_governance', + message: 'Модули должны быть инициализированы через governance предложения', + treasuryAddress, + timelockAddress, + readerAddress }); } catch (error) { @@ -1401,11 +1359,7 @@ router.post('/verify-modules-all-networks', async (req, res) => { ]; // ID модулей - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; // Маппинг модулей для верификации const moduleTypes = { @@ -1609,7 +1563,6 @@ router.post('/check-dle-deployment-status', async (req, res) => { const dleAbi = [ "function name() external view returns (string)", "function symbol() external view returns (string)", - "function modulesInitialized() external view returns (bool)" ]; const dle = new ethers.Contract(dleAddress, dleAbi, provider); @@ -1697,11 +1650,7 @@ router.post('/check-module-deployment-status', async (req, res) => { console.log(`[DLE Modules] Проверка статуса деплоя модуля ${moduleType} для DLE: ${dleAddress} в сетях: ${chainIds.join(', ')}`); // Маппинг типов модулей на их ID - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; const moduleId = moduleIds[moduleType]; if (!moduleId) { @@ -2243,11 +2192,7 @@ router.post('/verify-module-all-networks', async (req, res) => { } // Маппинг типов модулей на их ID - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; const moduleId = moduleIds[moduleType]; if (!moduleId) { @@ -2387,11 +2332,7 @@ router.post('/initialize-module-all-networks', async (req, res) => { } // Маппинг типов модулей на их ID - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; const moduleId = moduleIds[moduleType]; if (!moduleId) { @@ -2416,23 +2357,12 @@ router.post('/initialize-module-all-networks', async (req, res) => { const dleAbi = [ "function getModuleAddress(bytes32 _moduleId) external view returns (address)", "function isModuleActive(bytes32 _moduleId) external view returns (bool)", - "function modulesInitialized() external view returns (bool)" ]; const dle = new ethers.Contract(dleAddress, dleAbi, wallet); - // Проверяем, уже ли инициализированы модули - const modulesInitialized = await dle.modulesInitialized(); - - if (modulesInitialized) { - console.log(`[DLE Modules] Модули уже инициализированы в сети ${network.chainId}`); - return { - chainId: network.chainId, - networkName: network.networkName, - status: 'already_initialized', - message: 'Модули уже инициализированы' - }; - } + // Модули инициализируются только через governance + console.log(`[DLE Modules] Модули инициализируются через governance предложения в сети ${network.chainId}`); // Получаем адрес модуля const moduleAddress = await dle.getModuleAddress(moduleId); @@ -2533,11 +2463,7 @@ router.post('/final-deployment-check', async (req, res) => { console.log(`[DLE Modules] Финальная проверка готовности DLE: ${dleAddress} в сетях: ${chainIds.join(', ')}`); // ID модулей для проверки - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; const results = []; let allComponentsReady = true; @@ -2558,7 +2484,7 @@ router.post('/final-deployment-check', async (req, res) => { const dleAbi = [ "function name() external view returns (string)", "function symbol() external view returns (string)", - "function modulesInitialized() external view returns (bool)", +, "function getModuleAddress(bytes32 _moduleId) external view returns (address)", "function isModuleActive(bytes32 _moduleId) external view returns (bool)" ]; @@ -2627,15 +2553,8 @@ router.post('/final-deployment-check', async (req, res) => { } } - // Проверяем инициализацию модулей - let modulesInitialized = false; - try { - modulesInitialized = await dle.modulesInitialized(); - } catch (error) { - console.log(`[DLE Modules] Ошибка проверки инициализации модулей в сети ${chainId}:`, error.message); - } - - const networkReady = dleDeployed && allModulesReady && modulesInitialized; + // Модули инициализируются только через governance + const networkReady = dleDeployed && allModulesReady; return { chainId: chainId, @@ -2647,7 +2566,7 @@ router.post('/final-deployment-check', async (req, res) => { info: dleInfo }, modules: modulesStatus, - modulesInitialized: modulesInitialized + requiresGovernance: true } }; }, @@ -2827,11 +2746,7 @@ router.post('/get-deployment-status', async (req, res) => { } // Проверяем модули - const moduleIds = { - treasury: "0x7472656173757279000000000000000000000000000000000000000000000000", - timelock: "0x74696d656c6f636b000000000000000000000000000000000000000000000000", - reader: "0x7265616465720000000000000000000000000000000000000000000000000000" - }; + const moduleIds = MODULE_TYPE_TO_ID; const moduleStages = [ { type: 'treasury', stages: ['deploy_treasury', 'verify_treasury', 'initialize_treasury'] }, @@ -2912,10 +2827,10 @@ router.post('/get-deployment-status', async (req, res) => { const rpcUrl = await rpcProviderService.getRpcUrlByChainId(supportedNetworks[0].chainId); const provider = new ethers.JsonRpcProvider(rpcUrl); const dle = new ethers.Contract(dleAddress, [ - "function modulesInitialized() external view returns (bool)" ], provider); - return await dle.modulesInitialized(); + // Модули инициализируются только через governance + return false; }, 'Проверка финальной инициализации модулей', 3, diff --git a/backend/scripts/check-modules.js b/backend/scripts/check-modules.js index 6635323..665a17c 100644 --- a/backend/scripts/check-modules.js +++ b/backend/scripts/check-modules.js @@ -24,18 +24,15 @@ async function checkModules() { // ABI для DLE контракта const dleAbi = [ - "function modulesInitialized() external view returns (bool)", "function initializer() external view returns (address)", "function isModuleActive(bytes32 _moduleId) external view returns (bool)", - "function getModuleAddress(bytes32 _moduleId) external view returns (address)", - "function initializeBaseModules(address _treasuryAddress, address _timelockAddress, address _readerAddress) external" + "function getModuleAddress(bytes32 _moduleId) external view returns (address)" ]; const dle = new ethers.Contract(dleAddress, dleAbi, provider); - // Проверяем статус инициализации - const modulesInitialized = await dle.modulesInitialized(); - console.log('Модули инициализированы:', modulesInitialized); + // Модули теперь инициализируются только через governance + console.log('Модули инициализируются только через governance предложения'); // Получаем initializer адрес const initializer = await dle.initializer(); diff --git a/backend/scripts/deploy/deploy-multichain.js b/backend/scripts/deploy/deploy-multichain.js index 33f9db6..5ed17a1 100755 --- a/backend/scripts/deploy/deploy-multichain.js +++ b/backend/scripts/deploy/deploy-multichain.js @@ -283,12 +283,8 @@ async function deployModulesInNetwork(rpcUrl, pk, dleAddress, params) { console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Timelock: ${timelockAddress}`); console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Reader: ${readerAddress}`); - // Инициализация базовых модулей - const initTx = await dleContract.initializeBaseModules(treasuryAddress, timelockAddress, readerAddress); - console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Module initialization tx: ${initTx.hash}`); - await initTx.wait(); - console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} base modules initialized successfully`); - currentNonce++; + // Модули деплоятся отдельно, инициализация через governance + console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Modules deployed successfully, initialization will be done through governance proposals`); } else { console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} skipping module initialization - not all modules deployed`); console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Treasury: ${treasuryAddress || 'MISSING'}`); diff --git a/docs/MODULE_DEPLOYMENT_IMPROVEMENTS.md b/docs/MODULE_DEPLOYMENT_IMPROVEMENTS.md index a235113..bfb4237 100644 --- a/docs/MODULE_DEPLOYMENT_IMPROVEMENTS.md +++ b/docs/MODULE_DEPLOYMENT_IMPROVEMENTS.md @@ -21,7 +21,7 @@ ### Что уже работает: - ✅ Деплой основного DLE контракта в 4 сетях с одинаковым адресом (через CREATE2) - ✅ Деплой модулей (Treasury, Timelock, Reader) в каждой сети -- ✅ Автоматическая инициализация базовых модулей через `initializeBaseModules()` +- ✅ Модули инициализируются только через governance предложения - ✅ Верификация контрактов в каждой сети - ✅ Отображение модулей в виде карточек с адресами во всех сетях @@ -72,7 +72,7 @@ **Функциональность:** - Получает список поддерживаемых сетей из DLE контракта - Проверяет статус инициализации в каждой сети -- Если модули не инициализированы, вызывает `initializeBaseModules()` +- Если модули не инициализированы, создает governance предложения для их добавления - Возвращает детальный отчет по каждой сети **Возвращаемые статусы:** @@ -520,9 +520,11 @@ console.log("TreasuryModule инициализация:", treasuryInit); // 8-15. Повторяем для TimelockModule и DLEReader... -// 16. Финальная инициализация всех модулей -const finalInit = await initializeBaseModules(dleResult.address, "0x..."); -console.log("Финальная инициализация:", finalInit); +// 16. Создание governance предложений для добавления модулей +const addTreasuryProposal = await createAddModuleProposal(dleResult.address, treasuryAddress, "Treasury Module"); +const addTimelockProposal = await createAddModuleProposal(dleResult.address, timelockAddress, "Timelock Module"); +const addReaderProposal = await createAddModuleProposal(dleResult.address, readerAddress, "Reader Module"); +console.log("Governance предложения созданы:", { addTreasuryProposal, addTimelockProposal, addReaderProposal }); ``` ### Обработка ошибок diff --git a/SETUP_ACCESS_LEVELS.md b/docs/SETUP_ACCESS_LEVELS.md similarity index 100% rename from SETUP_ACCESS_LEVELS.md rename to docs/SETUP_ACCESS_LEVELS.md diff --git a/frontend/src/components/deployment/DeploymentWizard.vue b/frontend/src/components/deployment/DeploymentWizard.vue index 625255a..11f68d9 100644 --- a/frontend/src/components/deployment/DeploymentWizard.vue +++ b/frontend/src/components/deployment/DeploymentWizard.vue @@ -146,12 +146,17 @@ const props = defineProps({ dleData: { type: Object, required: true - }, - etherscanApiKey: { - type: String, - required: false, - default: '' - } + }, + logoURI: { + type: String, + required: false, + default: '/uploads/logos/default-token.svg' + }, + etherscanApiKey: { + type: String, + required: false, + default: '' + } }); // Events @@ -260,6 +265,7 @@ const startDeployment = async () => { initialAmounts: props.dleData.partners.map(p => p.amount).filter(amount => amount > 0), supportedChainIds: props.selectedNetworks.filter(id => id !== null && id !== undefined), currentChainId: props.selectedNetworks[0] || 1, + logoURI: props.logoURI || '/uploads/logos/default-token.svg', privateKey: props.privateKey, etherscanApiKey: props.etherscanApiKey || '', autoVerifyAfterDeploy: false diff --git a/frontend/src/views/ManagementView.vue b/frontend/src/views/ManagementView.vue index 993e60e..58d96bb 100644 --- a/frontend/src/views/ManagementView.vue +++ b/frontend/src/views/ManagementView.vue @@ -113,39 +113,28 @@
Юрисдикция: {{ dle.jurisdiction }}
-
- Коды ОКВЭД: {{ dle.okvedCodes?.join(', ') || 'Не указаны' }} +
+ Кворум: + {{ dle.quorumPercentage }}%
- Партнеры: - - {{ dle.participantCount || dle.partnerBalances.length }} участников -
-
- {{ shortenAddress(partner.address) }} - {{ partner.balance }} токенов ({{ partner.percentage.toFixed(1) }}%) -
-
- +{{ dle.partnerBalances.length - 3 }} еще -
-
-
- - {{ dle.participantCount || 0 }} участников - + Коды ОКВЭД: {{ dle.okvedCodes?.join(', ') || 'Не указаны' }}
Статус: Активен
-
- Верификация: - - +
+ Общий объем токенов: + {{ parseFloat(dle.totalSupply).toLocaleString() }} {{ dle.symbol }} +
+
+ Логотип: + Установлен +
+
+ Дата создания: + {{ formatTimestamp(dle.creationTimestamp) }}
@@ -187,8 +176,6 @@ const router = useRouter(); // Состояние для DLE const deployedDles = ref([]); const isLoadingDles = ref(false); -const verificationStatuses = ref({}); // { [address]: { address, chains: { [chainId]: { guid, status } } } } -let verifyPollTimer = null; @@ -308,18 +295,6 @@ async function loadDeployedDles() { deployedDles.value = dlesWithBlockchainData; console.log('[ManagementView] Итоговый список DLE:', deployedDles.value); - - // Подгружаем статусы верификации для всех адресов - for (const dle of deployedDles.value) { - try { - const st = await api.get(`/dle-v2/verify/status/${dle.dleAddress}`); - if (st.data?.success && st.data.data) { - verificationStatuses.value[dle.dleAddress] = st.data.data; - } - } catch (e) { - // no-op - } - } } else { console.error('[ManagementView] Ошибка при загрузке DLE:', response.data.message); deployedDles.value = []; @@ -367,6 +342,19 @@ function getExplorerUrl(chainId, address) { return `${baseUrl}/address/${address}`; } + +function formatTimestamp(timestamp) { + if (!timestamp) return ''; + const date = new Date(timestamp * 1000); // Конвертируем из Unix timestamp + return date.toLocaleDateString('ru-RU', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); +} + function openDleOnEtherscan(address) { window.open(`https://sepolia.etherscan.io/address/${address}`, '_blank'); } @@ -377,35 +365,6 @@ function openDleManagement(dleAddress) { } -async function refreshVerification(address) { - try { - const resp = await api.post(`/dle-v2/verify/refresh/${address}`, {}); - if (resp.data?.success && resp.data.data) { - verificationStatuses.value[address] = resp.data.data; - } - } catch (e) { - // no-op - } -} - -function isTerminalStatus(status) { - if (!status) return false; - const s = String(status).toLowerCase(); - return s.includes('pass') || s.includes('verified') || s.startsWith('error'); -} - -async function pollVerifications() { - try { - const addresses = Object.keys(verificationStatuses.value || {}); - for (const addr of addresses) { - const chains = verificationStatuses.value[addr]?.chains || {}; - const hasPending = Object.values(chains).some((c) => !isTerminalStatus(c.status)); - if (hasPending) { - await refreshVerification(addr); - } - } - } catch {} -} // function openMultisig() { // router.push('/management/multisig'); @@ -416,14 +375,6 @@ async function pollVerifications() { onMounted(() => { loadDeployedDles(); - verifyPollTimer = setInterval(pollVerifications, 15000); -}); - -onBeforeUnmount(() => { - if (verifyPollTimer) { - clearInterval(verifyPollTimer); - verifyPollTimer = null; - } }); @@ -813,55 +764,31 @@ onBeforeUnmount(() => { align-self: flex-start; } -/* Стили для отображения партнеров */ -.partners-details { - margin-top: 0.5rem; - padding: 0.5rem; - background: #f8f9fa; - border-radius: 6px; - border-left: 3px solid var(--color-primary); -} -.partner-info { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.25rem 0; - font-size: 0.875rem; -} - -.partner-info:not(:last-child) { - border-bottom: 1px solid #e9ecef; -} - -.partner-address { - font-family: 'Courier New', monospace; - color: #495057; - font-weight: 600; -} - -.partner-balance { +/* Стили для новых элементов */ +.token-supply { color: var(--color-primary); font-weight: 600; } -.more-partners { - text-align: center; - color: #6c757d; - font-style: italic; - font-size: 0.8rem; - padding: 0.25rem 0; +.logo-info { + color: #28a745; + font-weight: 600; } +.quorum-info { + color: #fd7e14; + font-weight: 600; +} + +.creation-date { + color: #6c757d; + font-weight: 500; +} + + /* Адаптивность */ @media (max-width: 768px) { - - .partner-info { - flex-direction: column; - align-items: flex-start; - gap: 0.25rem; - } - .dle-title-section { flex-direction: column; align-items: flex-start; diff --git a/frontend/src/views/settings/DleDeployFormView.vue b/frontend/src/views/settings/DleDeployFormView.vue index 9e25f3f..7517f65 100644 --- a/frontend/src/views/settings/DleDeployFormView.vue +++ b/frontend/src/views/settings/DleDeployFormView.vue @@ -693,9 +693,17 @@
-
+

Основная информация DLE

+
+ 🎨 Логотип: +
+ Logo preview + {{ logoFile?.name || 'ENS аватар' || 'Дефолтный логотип' }} +
+
+
📋 Название: {{ dleSettings.name }}
@@ -929,6 +937,7 @@ :private-key="unifiedPrivateKey" :selected-networks="selectedNetworks" :dle-data="dleSettings" + :logo-uri="getLogoURI()" :etherscan-api-key="etherscanApiKey" @deployment-completed="handleDeploymentCompleted" /> @@ -2751,6 +2760,19 @@ async function resolveEnsAvatar() { } } +// Функция для получения URI логотипа +function getLogoURI() { + if (logoFile.value) { + // Если выбран файл, возвращаем временный URL для предпросмотра + // В реальности файл будет загружен на сервер и получен настоящий URL + return logoPreviewUrl.value || '/uploads/logos/default-token.svg'; + } else if (ensResolvedUrl.value) { + return ensResolvedUrl.value; + } else { + return '/uploads/logos/default-token.svg'; + } +} + async function submitDeploy() { try { // Подготовка данных формы diff --git a/frontend/src/views/smartcontracts/DleProposalsView.vue b/frontend/src/views/smartcontracts/DleProposalsView.vue index 6e415b5..d9f6b3b 100644 --- a/frontend/src/views/smartcontracts/DleProposalsView.vue +++ b/frontend/src/views/smartcontracts/DleProposalsView.vue @@ -289,7 +289,10 @@ {{ chain.name }} ({{ chain.chainId }})
- Для offchain‑действий целевые сети не требуются. + Выберите хотя бы одну целевую сеть для исполнения операции. +
+ ⚠️ Необходимо выбрать хотя бы одну целевую сеть +
@@ -958,7 +961,8 @@ const isFormValid = computed(() => { newProposal.value.governanceChainId && newProposal.value.operationType && newProposal.value.timelockHours >= 0 && - validateOperationParams() + validateOperationParams() && + validateTargetChains() ); }); @@ -1069,6 +1073,14 @@ function validateOperationParams() { } } +function validateTargetChains() { + // Если показываем целевые сети, то должна быть выбрана хотя бы одна + if (showTargetChains.value) { + return newProposal.value.targetChains.length > 0; + } + return true; +} + function validateAddress(address) { if (!address) return false; // Проверяем формат Ethereum адреса @@ -1582,22 +1594,22 @@ function encodeBurnOperation(from, amount) { } function encodeUpdateDLEInfoOperation(name, symbol, location, coordinates, jurisdiction, oktmo, kpp) { - // Селектор для updateDLEInfo(string,string,string,string,uint256,uint256,string[],uint256) - const selector = '0x' + ethers.keccak256(ethers.toUtf8Bytes('updateDLEInfo(string,string,string,string,uint256,uint256,string[],uint256)')).slice(0, 10); + // Селектор для _updateDLEInfo(string,string,string,string,uint256,string[],uint256) + const selector = '0x' + ethers.keccak256(ethers.toUtf8Bytes('_updateDLEInfo(string,string,string,string,uint256,string[],uint256)')).slice(0, 10); // Кодируем параметры const abiCoder = new ethers.AbiCoder(); const encodedData = abiCoder.encode( - ['string', 'string', 'string', 'string', 'uint256', 'uint256', 'string[]', 'uint256'], - [name, symbol, location, coordinates, jurisdiction, oktmo, [], kpp] // okvedCodes пока пустой массив + ['string', 'string', 'string', 'string', 'uint256', 'string[]', 'uint256'], + [name, symbol, location, coordinates, jurisdiction, [], kpp] // okvedCodes пока пустой массив ); return selector + encodedData.slice(2); } function encodeUpdateQuorumOperation(quorumPercentage) { - // Селектор для updateQuorumPercentage(uint256) - const selector = '0x' + ethers.keccak256(ethers.toUtf8Bytes('updateQuorumPercentage(uint256)')).slice(0, 10); + // Селектор для _updateQuorumPercentage(uint256) + const selector = '0x' + ethers.keccak256(ethers.toUtf8Bytes('_updateQuorumPercentage(uint256)')).slice(0, 10); // Кодируем параметр const abiCoder = new ethers.AbiCoder(); @@ -1607,8 +1619,8 @@ function encodeUpdateQuorumOperation(quorumPercentage) { } function encodeUpdateChainOperation(chainId) { - // Селектор для updateCurrentChainId(uint256) - const selector = '0x' + ethers.keccak256(ethers.toUtf8Bytes('updateCurrentChainId(uint256)')).slice(0, 10); + // Селектор для _updateCurrentChainId(uint256) + const selector = '0x' + ethers.keccak256(ethers.toUtf8Bytes('_updateCurrentChainId(uint256)')).slice(0, 10); // Кодируем параметр const abiCoder = new ethers.AbiCoder(); @@ -2656,4 +2668,43 @@ onUnmounted(() => { color: #28a745; font-weight: 500; } + +/* Стили для ошибок валидации */ +.form-error { + margin-top: 0.5rem; + padding: 0.5rem; + background-color: #f8d7da; + border: 1px solid #f5c6cb; + border-radius: 4px; +} + +.text-danger { + color: #dc3545 !important; +} + +.targets-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.5rem; + margin-bottom: 1rem; +} + +.target-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + border: 1px solid #e9ecef; + border-radius: 4px; + cursor: pointer; + transition: all 0.2s; +} + +.target-item:hover { + background-color: #f8f9fa; +} + +.target-item input[type="checkbox"] { + margin: 0; +} \ No newline at end of file diff --git a/frontend/src/views/smartcontracts/ModulesView.vue b/frontend/src/views/smartcontracts/ModulesView.vue index 31122d5..d742e02 100644 --- a/frontend/src/views/smartcontracts/ModulesView.vue +++ b/frontend/src/views/smartcontracts/ModulesView.vue @@ -840,14 +840,14 @@ async function loadModules() { active: m.isActive, id: m.moduleId })), - modulesInitialized: modulesResponse.data.modulesInitialized, + requiresGovernance: modulesResponse.data.requiresGovernance, totalModules: modulesResponse.data.totalModules, activeModules: modulesResponse.data.activeModules }); // Обновляем счетчики - if (modulesResponse.data.modulesInitialized === false) { - console.log('[ModulesView] Модули для DLE не инициализированы'); + if (modulesResponse.data.requiresGovernance === true) { + console.log('[ModulesView] Модули требуют инициализации через governance'); } // Обновляем время последнего обновления