ваше сообщение коммита
This commit is contained in:
@@ -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();
|
||||
|
||||
75
backend/constants/moduleIds.js
Normal file
75
backend/constants/moduleIds.js
Normal file
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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";
|
||||
|
||||
173
backend/docs/MODULE_IDS.md
Normal file
173
backend/docs/MODULE_IDS.md
Normal file
@@ -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}`);
|
||||
```
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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'}`);
|
||||
|
||||
@@ -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 });
|
||||
```
|
||||
|
||||
### Обработка ошибок
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -113,39 +113,28 @@
|
||||
<div class="detail-item">
|
||||
<strong>Юрисдикция:</strong> {{ dle.jurisdiction }}
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<strong>Коды ОКВЭД:</strong> {{ dle.okvedCodes?.join(', ') || 'Не указаны' }}
|
||||
<div class="detail-item" v-if="dle.quorumPercentage">
|
||||
<strong>Кворум:</strong>
|
||||
<span class="quorum-info">{{ dle.quorumPercentage }}%</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<strong>Партнеры:</strong>
|
||||
<span v-if="dle.partnerBalances && dle.partnerBalances.length > 0">
|
||||
{{ dle.participantCount || dle.partnerBalances.length }} участников
|
||||
<div class="partners-details">
|
||||
<div v-for="(partner, index) in dle.partnerBalances.slice(0, 3)" :key="index" class="partner-info">
|
||||
<span class="partner-address">{{ shortenAddress(partner.address) }}</span>
|
||||
<span class="partner-balance">{{ partner.balance }} токенов ({{ partner.percentage.toFixed(1) }}%)</span>
|
||||
</div>
|
||||
<div v-if="dle.partnerBalances.length > 3" class="more-partners">
|
||||
+{{ dle.partnerBalances.length - 3 }} еще
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ dle.participantCount || 0 }} участников
|
||||
</span>
|
||||
<strong>Коды ОКВЭД:</strong> {{ dle.okvedCodes?.join(', ') || 'Не указаны' }}
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<strong>Статус:</strong>
|
||||
<span class="status active">Активен</span>
|
||||
</div>
|
||||
<div class="detail-item" v-if="verificationStatuses[dle.dleAddress]">
|
||||
<strong>Верификация:</strong>
|
||||
<ul class="verify-list">
|
||||
<li v-for="(info, chainId) in verificationStatuses[dle.dleAddress].chains" :key="chainId">
|
||||
Chain {{ chainId }}: {{ info.status || '—' }}<span v-if="info.guid"> (guid: {{ info.guid.slice(0,8) }}…)</span>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="details-btn btn-sm" @click.stop="refreshVerification(dle.dleAddress)">Обновить статус</button>
|
||||
<div class="detail-item" v-if="dle.totalSupply">
|
||||
<strong>Общий объем токенов:</strong>
|
||||
<span class="token-supply">{{ parseFloat(dle.totalSupply).toLocaleString() }} {{ dle.symbol }}</span>
|
||||
</div>
|
||||
<div class="detail-item" v-if="dle.logoURI">
|
||||
<strong>Логотип:</strong>
|
||||
<span class="logo-info">Установлен</span>
|
||||
</div>
|
||||
<div class="detail-item" v-if="dle.creationTimestamp">
|
||||
<strong>Дата создания:</strong>
|
||||
<span class="creation-date">{{ formatTimestamp(dle.creationTimestamp) }}</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -693,9 +693,17 @@
|
||||
</div>
|
||||
|
||||
<!-- Основная информация DLE -->
|
||||
<div v-if="dleSettings.name || dleSettings.tokenSymbol" class="preview-section">
|
||||
<div v-if="dleSettings.name || dleSettings.tokenSymbol || logoPreviewUrl" class="preview-section">
|
||||
<h4>Основная информация DLE</h4>
|
||||
|
||||
<div v-if="logoPreviewUrl" class="preview-item">
|
||||
<strong>🎨 Логотип:</strong>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-top: 5px;">
|
||||
<img :src="logoPreviewUrl" alt="Logo preview" style="width: 48px; height: 48px; border-radius: 6px; object-fit: contain; border: 1px solid #e9ecef;" />
|
||||
<span style="color: #666; font-size: 0.9em;">{{ logoFile?.name || 'ENS аватар' || 'Дефолтный логотип' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="dleSettings.name" class="preview-item">
|
||||
<strong>📋 Название:</strong> {{ dleSettings.name }}
|
||||
</div>
|
||||
@@ -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 {
|
||||
// Подготовка данных формы
|
||||
|
||||
@@ -289,7 +289,10 @@
|
||||
<span>{{ chain.name }} ({{ chain.chainId }})</span>
|
||||
</label>
|
||||
</div>
|
||||
<small class="text-muted">Для offchain‑действий целевые сети не требуются.</small>
|
||||
<small class="text-muted">Выберите хотя бы одну целевую сеть для исполнения операции.</small>
|
||||
<div v-if="showTargetChains && newProposal.targetChains.length === 0" class="form-error">
|
||||
<small class="text-danger">⚠️ Необходимо выбрать хотя бы одну целевую сеть</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
</style>
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
// Обновляем время последнего обновления
|
||||
|
||||
Reference in New Issue
Block a user