/** * 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/VC-HB3-Accelerator */ const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); const { getSupportedChainIds } = require('../utils/networkLoader'); // Чтение данных 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(`[DLE Core] Чтение данных DLE из блокчейна: ${dleAddress}`); // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; // Получаем поддерживаемые сети из networkLoader const { getSupportedChainIds } = require('../utils/networkLoader'); const candidateChainIds = await getSupportedChainIds(); for (const cid of candidateChainIds) { try { const url = await rpcProviderService.getRpcUrlByChainId(cid); if (!url) continue; const prov = new ethers.JsonRpcProvider(url); const code = await prov.getCode(dleAddress); if (code && code !== '0x') { rpcUrl = url; targetChainId = cid; break; } } catch (_) {} } if (!rpcUrl) { return res.status(500).json({ success: false, error: 'Не удалось найти сеть, где по адресу есть контракт' }); } if (!rpcUrl) { return res.status(500).json({ success: false, error: 'RPC URL для Sepolia не найден' }); } const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId)); // ABI для чтения данных DLE const dleAbi = [ "function getDLEInfo() external view returns (tuple(string name, string symbol, string location, string coordinates, uint256 jurisdiction, 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)", "function logoURI() external view returns (string memory)" ]; 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(); // Читаем логотип let logoURI = ''; try { logoURI = await dle.logoURI(); } catch (error) { console.log(`[DLE Core] Ошибка при чтении logoURI:`, error.message); } // Получаем информацию о партнерах из блокчейна const partnerBalances = []; let participantCount = 0; // Получаем события InitialTokensDistributed для определения партнеров try { // Получаем последние блоки для поиска событий const currentBlock = await provider.getBlockNumber(); const fromBlock = Math.max(0, currentBlock - 10000); // Ищем в последних 10000 блоках // ABI для события InitialTokensDistributed const eventAbi = [ "event InitialTokensDistributed(address[] partners, uint256[] amounts)" ]; const dleWithEvents = new ethers.Contract(dleAddress, [...dleAbi, ...eventAbi], provider); // Ищем события InitialTokensDistributed const events = await dleWithEvents.queryFilter( dleWithEvents.filters.InitialTokensDistributed(), fromBlock, currentBlock ); if (events.length > 0) { // Берем последнее событие const lastEvent = events[events.length - 1]; const partners = lastEvent.args.partners; const amounts = lastEvent.args.amounts; for (let i = 0; i < partners.length; i++) { const partnerAddress = partners[i]; const amount = amounts[i]; // Проверяем текущий баланс const currentBalance = await dle.balanceOf(partnerAddress); if (Number(currentBalance) > 0) { participantCount++; partnerBalances.push({ address: partnerAddress, balance: ethers.formatUnits(currentBalance, 18), percentage: (Number(currentBalance) / Number(totalSupply)) * 100, initialAmount: ethers.formatUnits(amount, 18) }); } } } } catch (error) { console.log(`[DLE Core] Ошибка при получении событий партнеров:`, error.message); // Fallback: ищем держателей токенов через события Transfer try { const currentBlock = await provider.getBlockNumber(); const fromBlock = Math.max(0, currentBlock - 10000); const transferEventAbi = [ "event Transfer(address indexed from, address indexed to, uint256 value)" ]; const dleWithTransferEvents = new ethers.Contract(dleAddress, [...dleAbi, ...transferEventAbi], provider); // Ищем события Transfer с from = address(0) (mint события) const mintEvents = await dleWithTransferEvents.queryFilter( dleWithTransferEvents.filters.Transfer(ethers.ZeroAddress), fromBlock, currentBlock ); const uniqueRecipients = new Set(); for (const event of mintEvents) { uniqueRecipients.add(event.args.to); } // Проверяем текущие балансы всех получателей for (const recipient of uniqueRecipients) { const balance = await dle.balanceOf(recipient); if (Number(balance) > 0) { participantCount++; partnerBalances.push({ address: recipient, balance: ethers.formatUnits(balance, 18), percentage: (Number(balance) / Number(totalSupply)) * 100 }); } } } catch (fallbackError) { console.log(`[DLE Core] Ошибка при fallback поиске партнеров:`, fallbackError.message); } } const blockchainData = { name: dleInfo.name, symbol: dleInfo.symbol, dleAddress: dleAddress, // Добавляем адрес контракта location: dleInfo.location, coordinates: dleInfo.coordinates, jurisdiction: Number(dleInfo.jurisdiction), okvedCodes: dleInfo.okvedCodes, kpp: Number(dleInfo.kpp), creationTimestamp: Number(dleInfo.creationTimestamp), isActive: dleInfo.isActive, totalSupply: ethers.formatUnits(totalSupply, 18), partnerBalances: partnerBalances, // Информация о партнерах и их балансах logoURI: logoURI, // URL логотипа токена quorumPercentage: Number(quorumPercentage), currentChainId: Number(currentChainId), participantCount: participantCount }; console.log(`[DLE Core] Данные DLE прочитаны из блокчейна:`, blockchainData); res.json({ success: true, data: blockchainData }); } catch (error) { console.error('[DLE Core] Ошибка при чтении данных DLE из блокчейна:', error); res.status(500).json({ success: false, error: 'Ошибка при чтении данных из блокчейна: ' + error.message }); } }); // Получить параметры управления router.post('/get-governance-params', async (req, res) => { try { const { dleAddress } = req.body; if (!dleAddress) { return res.status(400).json({ success: false, error: 'Адрес DLE обязателен' }); } console.log(`[DLE Core] Получение параметров управления для DLE: ${dleAddress}`); // Получаем RPC URL из параметров деплоя или используем Sepolia как fallback let rpcUrl; try { const latestParams = await deployParamsService.getLatestDeployParams(1); if (latestParams.length > 0) { const params = latestParams[0]; const supportedChainIds = params.supportedChainIds || []; const chainId = supportedChainIds.length > 0 ? supportedChainIds[0] : 11155111; rpcUrl = await rpcProviderService.getRpcUrlByChainId(chainId); } else { rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111); } } catch (error) { console.error('❌ Ошибка получения параметров деплоя, используем Sepolia:', error); rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111); } if (!rpcUrl) { return res.status(500).json({ success: false, error: 'RPC URL не найден' }); } const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId)); const dleAbi = [ "function getGovernanceParams() external view returns (uint256 quorumPct, uint256 chainId, uint256 supportedCount)" ]; const dle = new ethers.Contract(dleAddress, dleAbi, provider); // Получаем параметры управления const params = await dle.getGovernanceParams(); console.log(`[DLE Core] Параметры управления:`, params); res.json({ success: true, data: { quorumPct: Number(params.quorumPct), chainId: Number(params.chainId), supportedCount: Number(params.supportedCount) } }); } catch (error) { console.error('[DLE Core] Ошибка при получении параметров управления:', error); res.status(500).json({ success: false, error: 'Ошибка при получении параметров управления: ' + error.message }); } }); // Проверить активность DLE router.post('/is-active', async (req, res) => { try { const { dleAddress } = req.body; if (!dleAddress) { return res.status(400).json({ success: false, error: 'Адрес DLE обязателен' }); } console.log(`[DLE Core] Проверка активности DLE: ${dleAddress}`); // Получаем RPC URL из параметров деплоя или используем Sepolia как fallback let rpcUrl; try { const latestParams = await deployParamsService.getLatestDeployParams(1); if (latestParams.length > 0) { const params = latestParams[0]; const supportedChainIds = params.supportedChainIds || []; const chainId = supportedChainIds.length > 0 ? supportedChainIds[0] : 11155111; rpcUrl = await rpcProviderService.getRpcUrlByChainId(chainId); } else { rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111); } } catch (error) { console.error('❌ Ошибка получения параметров деплоя, используем Sepolia:', error); rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111); } if (!rpcUrl) { return res.status(500).json({ success: false, error: 'RPC URL не найден' }); } const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId)); const dleAbi = [ "function isActive() external view returns (bool)" ]; const dle = new ethers.Contract(dleAddress, dleAbi, provider); // Проверяем активность DLE const isActive = await dle.isActive(); console.log(`[DLE Core] Активность DLE: ${isActive}`); res.json({ success: true, data: { isActive: isActive } }); } catch (error) { console.error('[DLE Core] Ошибка при проверке активности DLE:', error); res.status(500).json({ success: false, error: 'Ошибка при проверке активности DLE: ' + error.message }); } }); // Проверка возможности деактивации DLE router.post('/deactivate-dle', async (req, res) => { try { const { dleAddress, userAddress } = req.body; if (!dleAddress || !userAddress) { return res.status(400).json({ success: false, error: 'Адрес DLE и адрес пользователя обязательны' }); } console.log(`[DLE Core] Проверка возможности деактивации DLE: ${dleAddress} пользователем: ${userAddress}`); // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя const latestParams = await deployParamsService.getLatestDeployParams(1); if (latestParams.length > 0) { const params = latestParams[0]; candidateChainIds = params.supportedChainIds || candidateChainIds; } } catch (error) { console.error('❌ Ошибка получения параметров деплоя, используем fallback:', error); } for (const cid of candidateChainIds) { try { const url = await rpcProviderService.getRpcUrlByChainId(cid); if (!url) continue; const prov = new ethers.JsonRpcProvider(url); const code = await prov.getCode(dleAddress); if (code && code !== '0x') { rpcUrl = url; targetChainId = cid; break; } } catch (_) {} } if (!rpcUrl) { return res.status(500).json({ success: false, error: 'Не удалось найти сеть, где по адресу есть контракт' }); } if (!rpcUrl) { return res.status(500).json({ success: false, error: 'RPC URL для Sepolia не найден' }); } const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId)); // ABI для проверки деактивации DLE const dleAbi = [ "function isActive() external view returns (bool)", "function balanceOf(address) external view returns (uint256)" ]; const dle = new ethers.Contract(dleAddress, dleAbi, provider); // Проверяем, что пользователь имеет токены const balance = await dle.balanceOf(userAddress); if (balance <= 0) { return res.status(403).json({ success: false, error: 'Для деактивации DLE необходимо иметь токены' }); } // Проверяем текущий статус const isActive = await dle.isActive(); if (!isActive) { return res.status(400).json({ success: false, error: 'DLE уже деактивирован' }); } console.log(`[DLE Core] DLE ${dleAddress} может быть деактивирован пользователем ${userAddress}`); res.json({ success: true, data: { dleAddress: dleAddress, canDeactivate: true, message: 'DLE может быть деактивирован при наличии валидного предложения с кворумом.' } }); } catch (error) { console.error('[DLE Core] Ошибка при проверке возможности деактивации DLE:', error); res.status(500).json({ success: false, error: 'Ошибка при проверке возможности деактивации DLE: ' + error.message }); } }); module.exports = router;