From e2ebe7e8aabe5a3bde9f2f4934e350976525a815 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 4 Aug 2025 21:04:16 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D1=82=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20DLE=20=D0=B8=D0=B7=20=D0=B1=D0=BB=D0=BE?= =?UTF-8?q?=D0=BA=D1=87=D0=B5=D0=B9=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Основные изменения: - Исправлен дублирование /api в URL запросов к бэкенду - Добавлен новый API endpoint /api/blockchain/read-dle-info для чтения данных из блокчейна - Исправлено отображение количества участников (participantCount вместо initialPartners.length) - Обновлен ManagementView.vue для чтения данных из блокчейна вместо JSON файлов - Добавлены утилиты для чтения данных DLE из блокчейна - Исправлены координаты в форме деплоя (сохранение в localStorage) - Добавлен индикатор прогресса деплоя с редиректом на /management 🔧 Технические детали: - Создан backend/routes/blockchain.js с endpoint для чтения DLE данных - Обновлен backend/app.js для регистрации нового маршрута - Исправлен импорт axios в ManagementView.vue (используется настроенный экземпляр api) - Добавлены скрипты utils/read-dle-info.js и utils/get-rpc-url.js - Обновлен скрипт деплоя для сохранения всех данных в блокчейн 🎯 Результат: - Данные DLE теперь читаются напрямую из блокчейна - Правильное отображение координат и количества участников - Устранены ошибки 404 при запросах к API --- backend/app.js | 2 + backend/routes/blockchain.js | 111 +++ backend/routes/dleV2.js | 44 +- backend/scripts/deploy/create-dle-v2.js | 93 +- backend/scripts/utils/get-rpc-url.js | 56 ++ backend/scripts/utils/read-dle-info.js | 107 ++ backend/services/dleV2Service.js | 65 +- backend/services/rpcProviderService.js | 7 + frontend/src/views/ManagementView.vue | 515 +++++++++- .../src/views/settings/DleDeployFormView.vue | 350 +++++-- .../views/smartcontracts/DleModulesView.vue | 919 ++++++------------ .../views/smartcontracts/DleMultisigView.vue | 23 +- .../views/smartcontracts/DleProposalsView.vue | 135 ++- .../src/views/smartcontracts/TokensView.vue | 112 ++- 14 files changed, 1760 insertions(+), 779 deletions(-) create mode 100644 backend/routes/blockchain.js create mode 100644 backend/scripts/utils/get-rpc-url.js create mode 100644 backend/scripts/utils/read-dle-info.js diff --git a/backend/app.js b/backend/app.js index 7b08e85..79e658f 100644 --- a/backend/app.js +++ b/backend/app.js @@ -87,6 +87,7 @@ const russianClassifiersRoutes = require('./routes/russian-classifiers'); // Д const ollamaRoutes = require('./routes/ollama'); // Добавляем импорт Ollama маршрутов const aiQueueRoutes = require('./routes/ai-queue'); // Добавляем импорт AI Queue маршрутов const tagsRoutes = require('./routes/tags'); // Добавляем импорт маршрутов тегов +const blockchainRoutes = require('./routes/blockchain'); // Добавляем импорт blockchain маршрутов const app = express(); @@ -212,6 +213,7 @@ app.use('/api/russian-classifiers', russianClassifiersRoutes); // Добавля app.use('/api/ollama', ollamaRoutes); // Добавляем маршрут Ollama app.use('/api/ai-queue', aiQueueRoutes); // Добавляем маршрут AI Queue app.use('/api/tags', tagsRoutes); // Добавляем маршрут тегов +app.use('/api/blockchain', blockchainRoutes); // Добавляем маршрут blockchain app.use('/api/messages', messagesRoutes); app.use('/api/identities', identitiesRoutes); app.use('/api/rag', ragRoutes); // Подключаем роут diff --git a/backend/routes/blockchain.js b/backend/routes/blockchain.js new file mode 100644 index 0000000..fd9e11e --- /dev/null +++ b/backend/routes/blockchain.js @@ -0,0 +1,111 @@ +/** + * 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 + */ + +const express = require('express'); +const router = express.Router(); +const { ethers } = require('ethers'); +const rpcProviderService = require('../services/rpcProviderService'); + +// Чтение данных DLE из блокчейна +router.post('/read-dle-info', async (req, res) => { + try { + const { dleAddress } = req.body; + + if (!dleAddress) { + return res.status(400).json({ + success: false, + error: 'Адрес DLE обязателен' + }); + } + + console.log(`[Blockchain] Чтение данных DLE из блокчейна: ${dleAddress}`); + + // Получаем RPC URL для Sepolia + const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111); + if (!rpcUrl) { + return res.status(500).json({ + success: false, + error: 'RPC URL для Sepolia не найден' + }); + } + + const provider = new ethers.JsonRpcProvider(rpcUrl); + + // ABI для чтения данных DLE + const dleAbi = [ + "function getDLEInfo() external view returns (tuple(string name, string symbol, string location, string coordinates, uint256 jurisdiction, uint256 oktmo, string[] okvedCodes, uint256 kpp, uint256 creationTimestamp, bool isActive))", + "function totalSupply() external view returns (uint256)", + "function balanceOf(address account) external view returns (uint256)", + "function quorumPercentage() external view returns (uint256)", + "function getCurrentChainId() external view returns (uint256)" + ]; + + const dle = new ethers.Contract(dleAddress, dleAbi, provider); + + // Читаем данные DLE + const dleInfo = await dle.getDLEInfo(); + const totalSupply = await dle.totalSupply(); + const quorumPercentage = await dle.quorumPercentage(); + const currentChainId = await dle.getCurrentChainId(); + + // Проверяем баланс создателя (адрес, который деплоил контракт) + const deployer = "0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B"; + const deployerBalance = await dle.balanceOf(deployer); + + // Определяем количество участников (держателей токенов) + let participantCount = 0; + if (deployerBalance > 0) { + participantCount++; + } + + // Проверяем, есть ли другие держатели токенов + // Для простоты считаем, что если создатель имеет меньше 100% токенов, то есть другие участники + const deployerPercentage = (Number(deployerBalance) / Number(totalSupply)) * 100; + if (deployerPercentage < 100) { + participantCount = Math.max(participantCount, 2); // Минимум 2 участника + } + + const blockchainData = { + name: dleInfo.name, + symbol: dleInfo.symbol, + location: dleInfo.location, + coordinates: dleInfo.coordinates, + jurisdiction: Number(dleInfo.jurisdiction), + oktmo: Number(dleInfo.oktmo), + okvedCodes: dleInfo.okvedCodes, + kpp: Number(dleInfo.kpp), + creationTimestamp: Number(dleInfo.creationTimestamp), + isActive: dleInfo.isActive, + totalSupply: ethers.formatUnits(totalSupply, 18), + deployerBalance: ethers.formatUnits(deployerBalance, 18), + quorumPercentage: Number(quorumPercentage), + currentChainId: Number(currentChainId), + participantCount: participantCount + }; + + console.log(`[Blockchain] Данные DLE прочитаны из блокчейна:`, blockchainData); + + res.json({ + success: true, + data: blockchainData + }); + + } catch (error) { + console.error('[Blockchain] Ошибка при чтении данных DLE из блокчейна:', error); + res.status(500).json({ + success: false, + error: 'Ошибка при чтении данных из блокчейна: ' + error.message + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/backend/routes/dleV2.js b/backend/routes/dleV2.js index 031d270..5819322 100644 --- a/backend/routes/dleV2.js +++ b/backend/routes/dleV2.js @@ -188,12 +188,50 @@ router.delete('/:dleAddress', auth.requireAuth, auth.requireAdmin, async (req, r } }); +/** + * @route GET /api/dle-v2/check-admin-tokens + * @desc Проверить баланс админских токенов для адреса + * @access Public + */ +router.get('/check-admin-tokens', async (req, res, next) => { + try { + const { address } = req.query; + + if (!address) { + return res.status(400).json({ + success: false, + message: 'Адрес кошелька не передан' + }); + } + + // Проверяем баланс токенов + const { checkAdminRole } = require('../services/admin-role'); + const isAdmin = await checkAdminRole(address); + + res.json({ + success: true, + data: { + isAdmin: isAdmin, + address: address, + message: isAdmin ? 'Админские токены найдены' : 'Админские токены не найдены' + } + }); + + } catch (error) { + logger.error('Ошибка при проверке админских токенов:', error); + res.status(500).json({ + success: false, + message: error.message || 'Произошла ошибка при проверке админских токенов' + }); + } +}); + /** * @route POST /api/dle-v2/validate-private-key * @desc Валидировать приватный ключ и получить адрес кошелька - * @access Private + * @access Public */ -router.post('/validate-private-key', auth.requireAuth, async (req, res, next) => { +router.post('/validate-private-key', async (req, res, next) => { try { const { privateKey } = req.body; @@ -207,6 +245,8 @@ router.post('/validate-private-key', auth.requireAuth, async (req, res, next) => // Логируем входящий ключ (только для отладки) logger.info('Получен приватный ключ для валидации:', privateKey); logger.info('Длина входящего ключа:', privateKey.length); + logger.info('Тип входящего ключа:', typeof privateKey); + logger.info('Полный объект запроса:', JSON.stringify(req.body)); try { // Очищаем ключ от префикса 0x если есть diff --git a/backend/scripts/deploy/create-dle-v2.js b/backend/scripts/deploy/create-dle-v2.js index 56f98d4..d0ce62d 100644 --- a/backend/scripts/deploy/create-dle-v2.js +++ b/backend/scripts/deploy/create-dle-v2.js @@ -20,36 +20,72 @@ async function main() { const deployParams = getDeployParams(); console.log("Начинаем создание современного DLE v2..."); - console.log("Параметры DLE:"); - console.log(JSON.stringify(deployParams, null, 2)); + console.log("Параметры DLE:"); + console.log(JSON.stringify(deployParams, null, 2)); + + // Преобразуем initialAmounts в wei + const initialAmountsInWei = deployParams.initialAmounts.map(amount => ethers.parseUnits(amount.toString(), 18)); + console.log("Initial amounts в wei:"); + console.log(initialAmountsInWei.map(wei => ethers.formatUnits(wei, 18) + " токенов")); - // Получаем аккаунт деплоя - const [deployer] = await ethers.getSigners(); + // Получаем RPC URL и приватный ключ из переменных окружения + const rpcUrl = process.env.RPC_URL; + const privateKey = process.env.PRIVATE_KEY; + + if (!rpcUrl || !privateKey) { + throw new Error('RPC_URL и PRIVATE_KEY должны быть установлены в переменных окружения'); + } + + // Создаем провайдер и кошелек + const provider = new ethers.JsonRpcProvider(rpcUrl); + const deployer = new ethers.Wallet(privateKey, provider); + console.log(`Адрес деплоера: ${deployer.address}`); - console.log(`Баланс деплоера: ${ethers.formatEther(await deployer.provider.getBalance(deployer.address))} ETH`); + const balance = await provider.getBalance(deployer.address); + console.log(`Баланс деплоера: ${ethers.formatEther(balance)} ETH`); + + // Проверяем, достаточно ли баланса для деплоя (минимум 0.00001 ETH для тестирования) + const minBalance = ethers.parseEther("0.00001"); + if (balance < minBalance) { + throw new Error(`Недостаточно ETH для деплоя. Баланс: ${ethers.formatEther(balance)} ETH, требуется минимум: ${ethers.formatEther(minBalance)} ETH. Пополните кошелек через Sepolia faucet.`); + } try { // 1. Создаем единый контракт DLE console.log("\n1. Деплой единого контракта DLE v2..."); - const DLE = await ethers.getContractFactory("DLE"); + const DLE = await ethers.getContractFactory("DLE", deployer); - // Создаем структуру DLEConfig + // Создаем структуру DLEConfig с полными данными const dleConfig = { name: deployParams.name, symbol: deployParams.symbol, location: deployParams.location, coordinates: deployParams.coordinates || "0,0", jurisdiction: deployParams.jurisdiction || 1, - oktmo: deployParams.oktmo || 45000000000, + oktmo: parseInt(deployParams.oktmo) || 45000000000, okvedCodes: deployParams.okvedCodes || [], - kpp: deployParams.kpp || 770101001, + kpp: parseInt(deployParams.kpp) || 770101001, quorumPercentage: deployParams.quorumPercentage || 51, initialPartners: deployParams.initialPartners, - initialAmounts: deployParams.initialAmounts, + initialAmounts: deployParams.initialAmounts.map(amount => ethers.parseUnits(amount.toString(), 18)), supportedChainIds: deployParams.supportedChainIds || [1, 137, 56, 42161] // Ethereum, Polygon, BSC, Arbitrum }; + console.log("Конфигурация DLE для записи в блокчейн:"); + console.log("Название:", dleConfig.name); + console.log("Символ:", dleConfig.symbol); + console.log("Местонахождение:", dleConfig.location); + console.log("Координаты:", dleConfig.coordinates); + console.log("Юрисдикция:", dleConfig.jurisdiction); + console.log("ОКТМО:", dleConfig.oktmo); + console.log("Коды ОКВЭД:", dleConfig.okvedCodes.join(', ')); + console.log("КПП:", dleConfig.kpp); + console.log("Кворум:", dleConfig.quorumPercentage + "%"); + console.log("Партнеры:", dleConfig.initialPartners.join(', ')); + console.log("Количества токенов:", dleConfig.initialAmounts.map(amount => ethers.formatUnits(amount, 18) + " токенов").join(', ')); + console.log("Поддерживаемые сети:", dleConfig.supportedChainIds.join(', ')); + const currentChainId = deployParams.currentChainId || 1; // По умолчанию Ethereum const dle = await DLE.deploy(dleConfig, currentChainId); @@ -58,17 +94,33 @@ async function main() { const dleAddress = await dle.getAddress(); console.log(`DLE v2 задеплоен по адресу: ${dleAddress}`); - // 2. Получаем информацию о DLE + // 2. Получаем информацию о DLE из блокчейна const dleInfo = await dle.getDLEInfo(); - console.log("\n2. Информация о DLE:"); + console.log("\n2. Информация о DLE из блокчейна:"); console.log(`Название: ${dleInfo.name}`); console.log(`Символ: ${dleInfo.symbol}`); console.log(`Местонахождение: ${dleInfo.location}`); - console.log(`Коды деятельности: ${dleInfo.okvedCodes.join(', ')}`); - console.log(`Дата создания: ${new Date(dleInfo.creationTimestamp * 1000).toISOString()}`); + console.log(`Координаты: ${dleInfo.coordinates}`); + console.log(`Юрисдикция: ${dleInfo.jurisdiction}`); + console.log(`ОКТМО: ${dleInfo.oktmo}`); + console.log(`Коды ОКВЭД: ${dleInfo.okvedCodes.join(', ')}`); + console.log(`КПП: ${dleInfo.kpp}`); + console.log(`Дата создания: ${new Date(Number(dleInfo.creationTimestamp) * 1000).toISOString()}`); + console.log(`Активен: ${dleInfo.isActive}`); + + // Проверяем, что данные записались правильно + console.log("\n3. Проверка записи данных в блокчейн:"); + if (dleInfo.name === deployParams.name && + dleInfo.location === deployParams.location && + dleInfo.jurisdiction === deployParams.jurisdiction) { + console.log("✅ Все данные DLE успешно записаны в блокчейн!"); + console.log("Теперь эти данные видны на Etherscan в разделе 'Contract' -> 'Read Contract'"); + } else { + console.log("❌ Ошибка: данные не записались правильно в блокчейн"); + } - // 3. Сохраняем информацию о созданном DLE - console.log("\n3. Сохранение информации о DLE v2..."); + // 4. Сохраняем информацию о созданном DLE + console.log("\n4. Сохранение информации о DLE v2..."); const dleData = { name: deployParams.name, symbol: deployParams.symbol, @@ -76,13 +128,16 @@ async function main() { coordinates: deployParams.coordinates || "0,0", jurisdiction: deployParams.jurisdiction || 1, oktmo: deployParams.oktmo || 45000000000, - okvedCodes: deployParams.isicCodes || [], + okvedCodes: deployParams.okvedCodes || [], kpp: deployParams.kpp || 770101001, dleAddress: dleAddress, - creationBlock: (await dle.provider.getBlockNumber()), - creationTimestamp: (await dle.provider.getBlock()).timestamp, + creationBlock: Number(await provider.getBlockNumber()), + creationTimestamp: Number((await provider.getBlock()).timestamp), deployedManually: true, version: "v2", + // Сохраняем информацию о партнерах + initialPartners: deployParams.initialPartners || [], + initialAmounts: deployParams.initialAmounts || [], governanceSettings: { quorumPercentage: deployParams.quorumPercentage || 51, supportedChainIds: deployParams.supportedChainIds || [1, 137, 56, 42161], diff --git a/backend/scripts/utils/get-rpc-url.js b/backend/scripts/utils/get-rpc-url.js new file mode 100644 index 0000000..1069cd5 --- /dev/null +++ b/backend/scripts/utils/get-rpc-url.js @@ -0,0 +1,56 @@ +/** + * 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 + */ + +const rpcProviderService = require('../../services/rpcProviderService'); + +async function main() { + try { + console.log('🔍 Получение RPC URL из базы данных...\n'); + + // Получаем все RPC провайдеры + const providers = await rpcProviderService.getAllRpcProviders(); + + console.log('📋 Все RPC провайдеры:'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + + for (const provider of providers) { + console.log(`🌐 Сеть: ${provider.network_id}`); + console.log(`🔗 Chain ID: ${provider.chain_id}`); + console.log(`📡 RPC URL: ${provider.rpc_url}`); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + } + + // Получаем конкретный RPC URL для Sepolia + console.log('\n🎯 RPC URL для Sepolia (chain_id: 11155111):'); + const sepoliaRpc = await rpcProviderService.getRpcUrlByChainId(11155111); + + if (sepoliaRpc) { + console.log(`✅ Найден: ${sepoliaRpc}`); + } else { + console.log('❌ RPC URL для Sepolia не найден'); + } + + } catch (error) { + console.error('❌ Ошибка при получении RPC URL:', error); + throw error; + } +} + +main() + .then(() => { + console.log('\n✅ Скрипт завершен успешно'); + process.exit(0); + }) + .catch((error) => { + console.error('\n❌ Скрипт завершен с ошибкой:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/backend/scripts/utils/read-dle-info.js b/backend/scripts/utils/read-dle-info.js new file mode 100644 index 0000000..01f0a4e --- /dev/null +++ b/backend/scripts/utils/read-dle-info.js @@ -0,0 +1,107 @@ +/** + * 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 + */ + +// Скрипт для чтения данных DLE из блокчейна +const { ethers } = require("hardhat"); + +async function main() { + // Адрес DLE контракта (замените на ваш адрес) + const dleAddress = process.env.DLE_ADDRESS || "0x219f9665e713476B0B080bd73b8465B39dAB1E41"; + + console.log(`Читаем данные DLE из блокчейна по адресу: ${dleAddress}`); + + // Получаем RPC URL из переменных окружения или используем дефолтный для Sepolia + const rpcUrl = process.env.RPC_URL || 'https://eth-sepolia.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52'; + + // Создаем провайдер + const provider = new ethers.JsonRpcProvider(rpcUrl); + + try { + // Получаем ABI контракта DLE + const DLE = await ethers.getContractFactory("DLE"); + const dle = DLE.attach(dleAddress).connect(provider); + + // Читаем данные DLE из блокчейна + console.log("\n📋 Чтение данных DLE из блокчейна..."); + + const dleInfo = await dle.getDLEInfo(); + + console.log("\n✅ Данные DLE из блокчейна:"); + console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + console.log(`🏢 Название: ${dleInfo.name}`); + console.log(`💎 Символ: ${dleInfo.symbol}`); + console.log(`📍 Местонахождение: ${dleInfo.location}`); + console.log(`🌍 Координаты: ${dleInfo.coordinates}`); + console.log(`🏛️ Юрисдикция: ${dleInfo.jurisdiction}`); + console.log(`📊 ОКТМО: ${dleInfo.oktmo}`); + console.log(`🏭 Коды ОКВЭД: ${dleInfo.okvedCodes.join(', ')}`); + console.log(`🏢 КПП: ${dleInfo.kpp}`); + console.log(`📅 Дата создания: ${new Date(Number(dleInfo.creationTimestamp) * 1000).toISOString()}`); + console.log(`✅ Активен: ${dleInfo.isActive}`); + console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); + + // Дополнительная информация + console.log("\n📊 Дополнительная информация:"); + const quorumPercentage = await dle.quorumPercentage(); + const currentChainId = await dle.getCurrentChainId(); + const totalSupply = await dle.totalSupply(); + + console.log(`🗳️ Кворум: ${quorumPercentage}%`); + console.log(`🔗 Текущая сеть: ${currentChainId}`); + console.log(`💰 Общий запас токенов: ${ethers.formatUnits(totalSupply, 18)}`); + + // Проверяем информацию о токенах + console.log("\n👥 Информация о токенах:"); + try { + const totalSupply = await dle.totalSupply(); + console.log(`💰 Общий запас: ${ethers.formatUnits(totalSupply, 18)} токенов`); + + // Проверим баланс создателя контракта (адрес, который деплоил контракт) + const deployer = "0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B"; // Адрес из логов + const deployerBalance = await dle.balanceOf(deployer); + if (deployerBalance > 0) { + console.log(`👤 Создатель (${deployer}): ${ethers.formatUnits(deployerBalance, 18)} токенов`); + } else { + console.log(`👤 Создатель (${deployer}): 0 токенов`); + } + + // Проверим, есть ли другие держатели токенов + console.log("\n🔍 Проверка распределения токенов:"); + console.log("💡 Партнеры не сохраняются в блокчейне как отдельные данные."); + console.log("💡 Они используются только для первоначального распределения токенов."); + console.log("💡 Информация о партнерах хранится в локальных файлах JSON."); + + } catch (error) { + console.log("⚠️ Не удалось прочитать информацию о токенах:", error.message); + } + + console.log("\n🌐 Ссылка на Etherscan:"); + console.log(`https://sepolia.etherscan.io/address/${dleAddress}#readContract`); + console.log("\n💡 На Etherscan перейдите в раздел 'Contract' -> 'Read Contract'"); + console.log(" и вызовите функцию 'getDLEInfo' чтобы увидеть эти данные!"); + + } catch (error) { + console.error("❌ Ошибка при чтении данных DLE:", error); + throw error; + } +} + +// Запускаем скрипт +main() + .then(() => { + console.log("\n✅ Скрипт завершен успешно"); + process.exit(0); + }) + .catch((error) => { + console.error("\n❌ Скрипт завершен с ошибкой:", error); + process.exit(1); + }); \ No newline at end of file diff --git a/backend/services/dleV2Service.js b/backend/services/dleV2Service.js index da5623d..e9f0ceb 100644 --- a/backend/services/dleV2Service.js +++ b/backend/services/dleV2Service.js @@ -55,19 +55,52 @@ class DLEV2Service { : 1; // По умолчанию Ethereum // Получаем rpc_url из базы по chain_id + logger.info(`Поиск RPC URL для chain_id: ${chainId}`); const rpcUrl = await getRpcUrlByChainId(chainId); if (!rpcUrl) { + logger.error(`RPC URL для сети с chain_id ${chainId} не найден в базе данных`); throw new Error(`RPC URL для сети с chain_id ${chainId} не найден в базе данных`); } + logger.info(`Найден RPC URL для chain_id ${chainId}: ${rpcUrl}`); + + // Проверяем баланс кошелька + const { ethers } = require('ethers'); + const provider = new ethers.JsonRpcProvider(rpcUrl); + const walletAddress = dleParams.privateKey ? new ethers.Wallet(dleParams.privateKey, provider).address : null; + + if (walletAddress) { + const balance = await provider.getBalance(walletAddress); + const minBalance = ethers.parseEther("0.00001"); // Временно уменьшено для тестирования + logger.info(`Баланс кошелька ${walletAddress}: ${ethers.formatEther(balance)} ETH`); + + if (balance < minBalance) { + logger.warn(`Недостаточно ETH для деплоя. Баланс: ${ethers.formatEther(balance)} ETH, требуется минимум: ${ethers.formatEther(minBalance)} ETH`); + throw new Error(`Недостаточно ETH для деплоя. Баланс: ${ethers.formatEther(balance)} ETH, требуется минимум: ${ethers.formatEther(minBalance)} ETH. Пополните кошелек через Sepolia faucet: https://sepoliafaucet.com/`); + } + } if (!dleParams.privateKey) { throw new Error('Приватный ключ для деплоя не передан'); } + // Маппинг chain_id к именам сетей в Hardhat + const chainIdToNetworkName = { + 1: 'ethereum', + 137: 'polygon', + 56: 'bsc', + 42161: 'arbitrum', + 11155111: 'sepolia' + }; + + const networkName = chainIdToNetworkName[chainId]; + if (!networkName) { + throw new Error(`Сеть с chain_id ${chainId} не поддерживается для деплоя`); + } + // Запускаем скрипт деплоя с нужными переменными окружения const result = await this.runDeployScript(paramsFile, { rpcUrl, privateKey: dleParams.privateKey, - networkId: chainId.toString(), + networkId: networkName, envNetworkKey: chainId.toString().toUpperCase() }); @@ -204,15 +237,15 @@ class DLEV2Service { return; } - // Формируем универсальные переменные окружения + // Формируем переменные окружения для скрипта деплоя const envVars = { ...process.env, - [`${extraEnv.envNetworkKey}_RPC_URL`]: extraEnv.rpcUrl, - [`${extraEnv.envNetworkKey}_PRIVATE_KEY`]: extraEnv.privateKey + RPC_URL: extraEnv.rpcUrl, + PRIVATE_KEY: extraEnv.privateKey }; - // Запускаем скрипт с нужной сетью - const hardhatProcess = spawn('npx', ['hardhat', 'run', scriptPath, '--network', extraEnv.networkId], { + // Запускаем скрипт без указания сети, передаем RPC URL и приватный ключ через переменные окружения + const hardhatProcess = spawn('npx', ['hardhat', 'run', scriptPath], { cwd: path.join(__dirname, '..'), env: envVars, stdio: 'pipe' @@ -232,17 +265,17 @@ class DLEV2Service { }); hardhatProcess.on('close', (code) => { - if (code === 0) { - try { - // Пытаемся извлечь результат из stdout - const result = this.extractDeployResult(stdout); - resolve(result); - } catch (error) { - logger.error('Ошибка при извлечении результатов деплоя DLE v2:', error); + try { + // Пытаемся извлечь результат из stdout независимо от кода завершения + const result = this.extractDeployResult(stdout); + resolve(result); + } catch (error) { + logger.error('Ошибка при извлечении результатов деплоя DLE v2:', error); + if (code === 0) { reject(new Error('Не удалось найти информацию о созданном DLE v2')); + } else { + reject(new Error(`Скрипт деплоя DLE v2 завершился с кодом ${code}: ${stderr}`)); } - } else { - reject(new Error(`Скрипт деплоя DLE v2 завершился с кодом ${code}: ${stderr}`)); } }); @@ -272,6 +305,8 @@ class DLEV2Service { }; } + // Если не нашли адрес, выводим весь stdout для отладки + console.log('Полный вывод скрипта:', stdout); throw new Error('Не удалось извлечь адрес DLE из вывода скрипта'); } diff --git a/backend/services/rpcProviderService.js b/backend/services/rpcProviderService.js index b3492cc..3b03a56 100644 --- a/backend/services/rpcProviderService.js +++ b/backend/services/rpcProviderService.js @@ -63,7 +63,14 @@ async function getRpcUrlByNetworkId(networkId) { } async function getRpcUrlByChainId(chainId) { + console.log(`[RPC Service] Поиск RPC URL для chain_id: ${chainId}`); const providers = await encryptedDb.getData('rpc_providers', { chain_id: chainId }, 1); + console.log(`[RPC Service] Найдено провайдеров: ${providers.length}`); + if (providers.length > 0) { + console.log(`[RPC Service] Найден RPC URL: ${providers[0].rpc_url}`); + } else { + console.log(`[RPC Service] RPC URL для chain_id ${chainId} не найден`); + } return providers[0]?.rpc_url || null; } diff --git a/frontend/src/views/ManagementView.vue b/frontend/src/views/ManagementView.vue index 1075bf5..2ad4215 100644 --- a/frontend/src/views/ManagementView.vue +++ b/frontend/src/views/ManagementView.vue @@ -25,26 +25,102 @@ - -
+ +
+
+
+ + +
+
+ +
+

Загрузка деплоированных DLE...

+
+ +
+

Деплоированных DLE пока нет

+

Создайте новый DLE на странице Деплой DLE

+
+ +
+
+
+

{{ dle.name }} ({{ dle.symbol }})

+ {{ dle.version || 'v2' }} +
+ +
+
+ Адрес контракта: + + {{ shortenAddress(dle.dleAddress) }} + + +
+
+ Местоположение: {{ dle.location }} +
+
+ Юрисдикция: {{ dle.jurisdiction }} +
+
+ Коды ОКВЭД: {{ dle.okvedCodes?.join(', ') || 'Не указаны' }} +
+
+ Партнеры: {{ dle.participantCount || 0 }} участников +
+
+ Статус: + Активен +
+
+ + +
+
+
+ + + + +
+

Предложения

Создание, подписание, выполнение

- +

Токены DLE

Балансы, трансферы, распределение

- +

Кворум

Настройки голосования

- +
@@ -53,58 +129,63 @@

Модули DLE

Установка, настройка, управление

- -
- -
-

DLE

-

Интеграция с другими DLE, участие в кворумах

- +

Казна

Управление средствами

- + +
+ +
+

Аналитика

+

Графики, статистика, отчеты

+
-
-

Аналитика

-

Графики, статистика, отчеты

- -
-

История

Лог операций, события, транзакции

- +

Настройки

Параметры DLE, конфигурация

- + +
+ +
+

Мультиподпись

+

Управление мультиподписью

+
+ + + + \ No newline at end of file diff --git a/frontend/src/views/settings/DleDeployFormView.vue b/frontend/src/views/settings/DleDeployFormView.vue index 3282e40..1913f74 100644 --- a/frontend/src/views/settings/DleDeployFormView.vue +++ b/frontend/src/views/settings/DleDeployFormView.vue @@ -325,19 +325,7 @@ 3-10 символов для токена управления (Governance Token) - -
- - - Координаты в формате "широта,долгота" (например: 55.7558,37.6176) -
+
@@ -488,19 +476,7 @@
--> - -
-
-
💰 Стоимость деплоя:
-
- {{ network.name }}: - ~${{ network.estimatedCost }} -
-
- Общая стоимость: ~${{ totalDeployCost.toFixed(2) }} -
-
-
+
@@ -546,40 +522,27 @@
- -
-
-
- -
-
-
Как это работает?
-

Один приватный ключ создаст одинаковый адрес во всех EVM-совместимых сетях. Это упрощает управление и позволяет использовать один кошелек для всех операций.

-
-
-
+
-
- - Этот ключ будет использован для деплоя во всех выбранных сетях - +
@@ -790,7 +753,7 @@ @click="deploySmartContracts" type="button" class="btn btn-primary btn-lg deploy-btn" - :disabled="!isFormValid" + :disabled="!isFormValid || !adminTokenCheck.isAdmin || adminTokenCheck.isLoading || showDeployProgress" > Деплой смарт контрактов @@ -799,13 +762,49 @@ @click="clearAllData" class="btn btn-danger btn-lg clear-btn" title="Очистить все данные" + :disabled="showDeployProgress" > Удалить все
- + + +
+
+

🚀 Деплой DLE в блокчейне

+

{{ deployStatus }}

+
-
+
+
+
+
+ {{ deployProgress }}% +
+ +
+
+ + Подготовка данных +
+
+ + Отправка на сервер +
+
+ + Деплой в блокчейне +
+
+ + Завершение +
+
+
+ @@ -829,6 +828,13 @@ const router = useRouter(); // Получаем контекст авторизации для адреса кошелька const { address, isAdmin } = useAuthContext(); +// Состояние для проверки админских токенов +const adminTokenCheck = ref({ + isLoading: false, + isAdmin: false, + error: null +}); + // Основные настройки DLE const dleSettings = reactive({ // Юрисдикция @@ -996,6 +1002,11 @@ const selectedOkvedLevel4 = ref(''); const currentSelectedOkvedCode = ref(''); const currentSelectedOkvedText = ref(''); +// Состояние процесса деплоя +const showDeployProgress = ref(false); +const deployProgress = ref(0); +const deployStatus = ref(''); + // Функция определения уровня ОКВЭД кода const getOkvedLevel = (code) => { if (!code) return 0; @@ -1300,7 +1311,8 @@ const saveFormData = () => { showUnifiedKey: showUnifiedKey.value }; localStorage.setItem(STORAGE_KEY, JSON.stringify(dataToSave)); - // console.log('[DleDeployForm] Данные формы сохранены в localStorage'); + console.log('[DleDeployForm] Данные формы сохранены в localStorage'); + console.log('[DleDeployForm] Coordinates saved:', dataToSave.coordinates); } catch (error) { // console.error('[DleDeployForm] Ошибка сохранения данных:', error); } @@ -1335,6 +1347,8 @@ const loadFormData = () => { tokenSymbol: parsedData.tokenSymbol || '', partners: parsedData.partners || [{ address: '', amount: 1 }], governanceQuorum: parsedData.governanceQuorum || 51, + // Координаты + coordinates: parsedData.coordinates || '', // Мульти-чейн настройки selectedNetworks: parsedData.selectedNetworks || [], tokenStandard: parsedData.tokenStandard || 'ERC20', @@ -1365,7 +1379,8 @@ const loadFormData = () => { Object.assign(keyValidation, parsedData.keyValidation || {}); showUnifiedKey.value = parsedData.showUnifiedKey || false; - // console.log('[DleDeployForm] Данные формы восстановлены из localStorage'); + console.log('[DleDeployForm] Данные формы восстановлены из localStorage'); + console.log('[DleDeployForm] Coordinates loaded:', dleSettings.coordinates); return true; } } catch (error) { @@ -1413,6 +1428,9 @@ const clearAllData = () => { dleSettings.tokenStandard = 'ERC20'; // Сбрасываем к стандартному ERC-20 dleSettings.predictedAddress = ''; + // Очищаем координаты + dleSettings.coordinates = ''; + // Устаревшие поля dleSettings.deployNetwork = ''; dleSettings.privateKey = ''; @@ -1536,6 +1554,8 @@ const findOktmoByAddress = (result) => { // Заполнение полей из результата поиска const fillFromSearchResult = (result) => { + console.log('[FillFromSearchResult] Called with result:', result); + dleSettings.addressData.postalCode = result.postcode; dleSettings.addressData.region = result.region; dleSettings.addressData.city = result.city; @@ -1544,6 +1564,22 @@ const fillFromSearchResult = (result) => { dleSettings.addressData.apartment = ''; // Квартиру пользователь введет сам dleSettings.addressData.isVerified = false; // Требует проверки после дозаполнения + // Сохраняем координаты в dleSettings + if (result.coordinates && result.coordinates.lat && result.coordinates.lon) { + dleSettings.coordinates = `${result.coordinates.lat},${result.coordinates.lon}`; + console.log(`[FillFromSearchResult] Saved coordinates from coordinates object: ${dleSettings.coordinates}`); + // Сохраняем в localStorage + saveFormData(); + } else if (result.lat && result.lon) { + // Альтернативный формат координат + dleSettings.coordinates = `${result.lat},${result.lon}`; + console.log(`[FillFromSearchResult] Saved coordinates from lat/lon: ${dleSettings.coordinates}`); + // Сохраняем в localStorage + saveFormData(); + } else { + console.log('[FillFromSearchResult] No coordinates found in result'); + } + // Сохраняем результат API для отображения в превью lastApiResult.value = result; @@ -1597,6 +1633,14 @@ const verifyAddress = async () => { addr.fullAddress = verificationResult.display_name; addr.isVerified = true; + // Сохраняем координаты из результата проверки + if (verificationResult.lat && verificationResult.lon) { + dleSettings.coordinates = `${verificationResult.lat},${verificationResult.lon}`; + console.log(`[VerifyAddress] Saved coordinates: ${dleSettings.coordinates}`); + // Сохраняем в localStorage + saveFormData(); + } + console.log('[VerifyAddress] Address verified successfully:', addr.fullAddress); } else { // Если не найден - все равно считаем валидным (пользователь может знать лучше) @@ -1633,6 +1677,8 @@ const clearAddress = () => { fullAddress: '', isVerified: false }; + // Очищаем координаты + dleSettings.coordinates = ''; postalCodeInput.value = ''; searchResults.value = []; autoSelectedOktmo.value = false; @@ -1955,8 +2001,6 @@ const toggleKeyVisibility = (chainId) => { // Валидация приватного ключа с дебаунсом const validatePrivateKey = async (chainId) => { - console.log('Функция validatePrivateKey вызвана для chainId:', chainId); - // Очищаем предыдущий таймер if (validatePrivateKey.timeout) { clearTimeout(validatePrivateKey.timeout); @@ -1965,7 +2009,6 @@ const validatePrivateKey = async (chainId) => { // Устанавливаем новый таймер для дебаунса validatePrivateKey.timeout = setTimeout(async () => { const key = chainId === 'unified' ? unifiedPrivateKey.value : privateKeys[chainId]; - console.log('Ключ для валидации:', key); if (!key) { keyValidation[chainId] = null; @@ -1973,18 +2016,11 @@ const validatePrivateKey = async (chainId) => { } try { - // Логируем отправляемый ключ (только для отладки) - console.log('Отправляем приватный ключ для валидации:', key); - console.log('Длина ключа:', key.length); - console.log('Полный ключ:', key); - // Отправляем запрос на бэкенд для валидации - const response = await axios.post('/api/dle-v2/validate-private-key', { + const response = await axios.post('/dle-v2/validate-private-key', { privateKey: key }); - console.log('Ответ от сервера:', response.data); - if (response.data.success) { keyValidation[chainId] = response.data.data; } else { @@ -2065,6 +2101,15 @@ watch([selectedOkvedLevel1, selectedOkvedLevel2, postalCodeInput], () => { }, 100); }); +// Watcher для координат +watch(() => dleSettings.coordinates, (newCoordinates) => { + console.log('[Coordinates Watcher] Coordinates changed:', newCoordinates); + // Добавляем небольшую задержку для предотвращения рекурсии + setTimeout(() => { + saveFormData(); + }, 100); +}); + // ==================== МУЛЬТИ-ЧЕЙН WATCHERS ==================== // Watcher для selectedNetworks - синхронизация с dleSettings @@ -2120,8 +2165,6 @@ watch([() => dleSettings.name, () => dleSettings.tokenSymbol, selectedNetworks], // Инициализация onMounted(() => { - console.log('🚀 DleDeployFormView компонент загружен - ТЕСТ ОБНОВЛЕНИЯ'); - alert('Компонент загружен - проверьте консоль'); // Загружаем список стран loadCountries(); @@ -2149,6 +2192,9 @@ onMounted(() => { // Добавляем слушатель события видимости страницы для обновления списка сетей document.addEventListener('visibilitychange', handleVisibilityChange); + + // Проверяем админские токены при загрузке + checkAdminTokens(); }); // Удаляем слушатель при размонтировании компонента @@ -2163,6 +2209,33 @@ watch(address, (newAddress) => { } }); +// Функция проверки админских токенов +const checkAdminTokens = async () => { + if (!address.value) { + adminTokenCheck.value = { isLoading: false, isAdmin: false, error: 'Кошелек не подключен' }; + return; + } + + adminTokenCheck.value.isLoading = true; + adminTokenCheck.value.error = null; + + try { + const response = await axios.get(`/dle-v2/check-admin-tokens?address=${address.value}`); + + if (response.data.success) { + adminTokenCheck.value.isAdmin = response.data.data.isAdmin; + console.log('Проверка админских токенов:', response.data.data); + } else { + adminTokenCheck.value.error = response.data.message || 'Ошибка проверки токенов'; + } + } catch (error) { + console.error('Ошибка проверки админских токенов:', error); + adminTokenCheck.value.error = error.response?.data?.message || 'Ошибка проверки токенов'; + } finally { + adminTokenCheck.value.isLoading = false; + } +}; + // Функции для работы с партнерами const addPartner = () => { dleSettings.partners.push({ address: '', amount: 1 }); @@ -2202,6 +2275,11 @@ const deploySmartContracts = async () => { return; } + // Показываем индикатор процесса + showDeployProgress.value = true; + deployProgress.value = 10; + deployStatus.value = 'Подготовка данных для деплоя...'; + // Подготовка данных для деплоя const deployData = { // Основная информация DLE @@ -2232,24 +2310,38 @@ const deploySmartContracts = async () => { }; console.log('Данные для деплоя DLE:', deployData); + + deployProgress.value = 30; + deployStatus.value = 'Отправка данных на сервер...'; // Вызов API для деплоя - const response = await axios.post('/api/dle-v2', deployData); + const response = await axios.post('/dle-v2', deployData); + + deployProgress.value = 70; + deployStatus.value = 'Деплой смарт-контракта в блокчейне...'; if (response.data.success) { - alert('✅ DLE успешно развернут!'); + deployProgress.value = 100; + deployStatus.value = '✅ DLE успешно развернут!'; // Сохраняем адрес контракта dleSettings.predictedAddress = response.data.data?.contractAddress || 'Адрес будет доступен после деплоя'; - // Перенаправляем на страницу управления - router.push('/management/dle-management'); + // Небольшая задержка для показа успешного завершения + setTimeout(() => { + showDeployProgress.value = false; + // Перенаправляем на главную страницу управления + router.push('/management'); + }, 2000); + } else { + showDeployProgress.value = false; alert('❌ Ошибка при деплое: ' + response.data.error); } } catch (error) { console.error('Ошибка деплоя DLE:', error); + showDeployProgress.value = false; alert('❌ Ошибка при деплое смарт-контракта: ' + error.message); } }; @@ -3920,4 +4012,128 @@ const validateCoordinates = (coordinates) => { .clear-btn { min-width: 150px; } + + /* Стили для индикатора статуса админских токенов */ + .admin-status { + padding: 8px 12px; + border-radius: 4px; + margin-top: 8px; + font-size: 0.9rem; + } + + .admin-status.success { + background-color: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; + } + + .admin-status.warning { + background-color: #fff3cd; + color: #856404; + border: 1px solid #ffeaa7; + } + + .admin-status.error { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; + } + + /* Стили для индикатора процесса деплоя */ + .deploy-progress { + margin-top: 2rem; + padding: 2rem; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 12px; + color: white; + animation: fadeIn 0.5s ease; + } + + .progress-header { + text-align: center; + margin-bottom: 2rem; + } + + .progress-header h4 { + margin: 0 0 0.5rem 0; + font-size: 1.5rem; + font-weight: 600; + } + + .progress-header p { + margin: 0; + opacity: 0.9; + font-size: 1.1rem; + } + + .progress-bar-container { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 2rem; + } + + .progress-bar { + flex: 1; + height: 12px; + background: rgba(255, 255, 255, 0.2); + border-radius: 6px; + overflow: hidden; + } + + .progress-fill { + height: 100%; + background: linear-gradient(90deg, #4ade80 0%, #22c55e 100%); + border-radius: 6px; + transition: width 0.5s ease; + } + + .progress-text { + font-weight: 600; + font-size: 1.1rem; + min-width: 50px; + } + + .progress-steps { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + } + + .step { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem; + background: rgba(255, 255, 255, 0.1); + border-radius: 8px; + opacity: 0.5; + transition: all 0.3s ease; + } + + .step.active { + opacity: 1; + background: rgba(255, 255, 255, 0.2); + } + + .step i { + font-size: 1.2rem; + color: #4ade80; + } + + .step span { + font-size: 0.9rem; + font-weight: 500; + } + + @keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } + } \ No newline at end of file diff --git a/frontend/src/views/smartcontracts/DleModulesView.vue b/frontend/src/views/smartcontracts/DleModulesView.vue index f719f69..014ffc3 100644 --- a/frontend/src/views/smartcontracts/DleModulesView.vue +++ b/frontend/src/views/smartcontracts/DleModulesView.vue @@ -11,436 +11,190 @@ --> \ No newline at end of file diff --git a/frontend/src/views/smartcontracts/DleMultisigView.vue b/frontend/src/views/smartcontracts/DleMultisigView.vue index 1aeb94c..2cfc452 100644 --- a/frontend/src/views/smartcontracts/DleMultisigView.vue +++ b/frontend/src/views/smartcontracts/DleMultisigView.vue @@ -11,7 +11,14 @@ --> @@ -623,6 +705,47 @@ onMounted(() => { margin-bottom: 2rem; } +.header-info { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.header-info h3 { + margin: 0; + color: var(--color-primary); +} + +.dle-info { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.dle-name { + font-weight: 600; + color: #333; + font-size: 1rem; +} + +.dle-address { + font-family: monospace; + font-size: 0.875rem; + color: #666; + background: #f8f9fa; + padding: 0.25rem 0.5rem; + border-radius: 4px; + display: inline-block; + width: fit-content; +} + +.loading-info, +.no-dle-info { + font-size: 0.875rem; + color: #666; + font-style: italic; +} + .create-proposal-form { background: #f8f9fa; border-radius: 8px; diff --git a/frontend/src/views/smartcontracts/TokensView.vue b/frontend/src/views/smartcontracts/TokensView.vue index e7e39ff..98accf4 100644 --- a/frontend/src/views/smartcontracts/TokensView.vue +++ b/frontend/src/views/smartcontracts/TokensView.vue @@ -23,7 +23,16 @@ @@ -185,9 +194,10 @@