ваше сообщение коммита

This commit is contained in:
2025-08-16 02:29:42 +03:00
parent 3765c65a18
commit 9134e83b8f
33 changed files with 8680 additions and 1435 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
/**
* 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('/get-dle-analytics', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Analytics] Получение аналитики для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function totalSupply() external view returns (uint256)",
"function balanceOf(address account) external view returns (uint256)",
"function getProposalsCount() external view returns (uint256)",
"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))"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем данные для аналитики
const totalSupply = await dle.totalSupply();
const proposalsCount = await dle.getProposalsCount();
const dleInfo = await dle.getDLEInfo();
// Проверяем баланс создателя (адрес, который деплоил контракт)
const deployer = "0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B";
const deployerBalance = await dle.balanceOf(deployer);
// Определяем количество участников (держателей токенов)
let participantCount = 0;
if (deployerBalance > 0) {
participantCount++;
}
// Проверяем, есть ли другие держатели токенов
const deployerPercentage = (Number(deployerBalance) / Number(totalSupply)) * 100;
if (deployerPercentage < 100) {
participantCount = Math.max(participantCount, 2); // Минимум 2 участника
}
// Рассчитываем аналитические метрики на основе реальных данных
const totalValue = Number(ethers.formatUnits(totalSupply, 18));
// Показываем только реальные данные без фейковых изменений
const valueChange = 0; // Нет исторических данных для расчета изменения
const activeParticipants = participantCount;
const participantsChange = 0; // Нет исторических данных для расчета изменения
const totalProposals = Number(proposalsCount);
const proposalsChange = 0; // Нет исторических данных для расчета изменения
// Базовая доходность на основе количества предложений
const yieldRate = totalProposals > 0 ? 3 : 2; // Минимальная доходность
const yieldChange = 0; // Нет исторических данных для расчета изменения
// Получаем реальные данные о держателях токенов
const topHolders = [];
// Добавляем создателя
if (deployerBalance > 0) {
topHolders.push({
address: deployer,
balance: ethers.formatUnits(deployerBalance, 18),
percentage: deployerPercentage
});
}
// Проверяем другие адреса с токенами (основные адреса из системы)
const knownAddresses = [
"0x15A4ed4759e5762174b300a4Cf51cc17ad967f4d", // Инициатор предложения
"0x2F2F070AA10bD3Ea14949b9953E2040a05421B17", // Сам DLE контракт
"0x0000000000000000000000000000000000000000" // Нулевой адрес
];
for (const address of knownAddresses) {
try {
const balance = await dle.balanceOf(address);
if (balance > 0 && address !== deployer) {
const percentage = (Number(balance) / Number(totalSupply)) * 100;
topHolders.push({
address: address,
balance: ethers.formatUnits(balance, 18),
percentage: percentage
});
}
} catch (error) {
console.log(`[DLE Analytics] Ошибка при получении баланса для ${address}:`, error.message);
}
}
// Сортируем по балансу (убывание)
topHolders.sort((a, b) => Number(b.balance) - Number(a.balance));
const analytics = {
totalValue,
valueChange,
activeParticipants,
participantsChange,
totalProposals,
proposalsChange,
yieldRate,
yieldChange,
topHolders
};
console.log(`[DLE Analytics] Аналитика получена:`, analytics);
res.json({
success: true,
data: analytics
});
} catch (error) {
console.error('[DLE Analytics] Ошибка при получении аналитики:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении аналитики: ' + error.message
});
}
});
// Получить историю DLE
router.post('/get-dle-history', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Analytics] Получение истории для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getProposalsCount() external view returns (uint256)",
"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 getProposalSummary(uint256 _proposalId) external view returns (uint256 id, string memory description, uint256 forVotes, uint256 againstVotes, bool executed, bool canceled, uint256 deadline, address initiator, uint256 governanceChainId, uint256 snapshotTimepoint, uint256[] memory targets)",
"event ProposalCreated(uint256 proposalId, address initiator, string description)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем данные для истории
const proposalsCount = await dle.getProposalsCount();
const dleInfo = await dle.getDLEInfo();
// Генерируем историю событий на основе реальных данных
const history = [];
// Событие создания DLE
history.push({
id: 1,
type: 'dle_created',
title: 'DLE создан',
description: `Создан DLE "${dleInfo.name}" (${dleInfo.symbol})`,
timestamp: Number(dleInfo.creationTimestamp) * 1000,
blockNumber: 0,
transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000'
});
// Получаем реальные события предложений
const currentBlock = await provider.getBlockNumber();
const fromBlock = Math.max(0, currentBlock - 10000); // Последние 10000 блоков
try {
const events = await dle.queryFilter('ProposalCreated', fromBlock, currentBlock);
for (let i = 0; i < events.length; i++) {
const event = events[i];
const proposalId = event.args.proposalId;
try {
// Получаем информацию о предложении
const proposal = await dle.getProposalSummary(proposalId);
history.push({
id: i + 2,
type: 'proposal_created',
title: `Предложение #${Number(proposalId)} создано`,
description: proposal.description || `Предложение #${Number(proposalId)}`,
timestamp: event.blockNumber * 1000, // Примерное время блока
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
initiator: proposal.initiator,
deadline: Number(proposal.deadline),
executed: proposal.executed,
canceled: proposal.canceled
});
// Если предложение исполнено, добавляем событие исполнения
if (proposal.executed) {
history.push({
id: history.length + 1,
type: 'proposal_executed',
title: `Предложение #${Number(proposalId)} исполнено`,
description: `Предложение "${proposal.description}" успешно исполнено`,
timestamp: (event.blockNumber + 100) * 1000, // Примерное время исполнения
blockNumber: event.blockNumber + 100,
transactionHash: event.transactionHash,
proposalId: Number(proposalId)
});
}
} catch (error) {
console.log(`[DLE Analytics] Ошибка при получении данных предложения ${proposalId}:`, error.message);
// Добавляем базовую информацию о событии
history.push({
id: i + 2,
type: 'proposal_created',
title: `Предложение #${Number(proposalId)} создано`,
description: `Предложение #${Number(proposalId)}`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
initiator: event.args.initiator
});
}
}
} catch (error) {
console.log(`[DLE Analytics] Ошибка при получении событий предложений:`, error.message);
// Если не удалось получить события, создаем базовую историю
for (let i = 0; i < Math.min(Number(proposalsCount), 3); i++) {
history.push({
id: i + 2,
type: 'proposal_created',
title: `Предложение #${i + 1} создано`,
description: `Создано предложение #${i + 1}`,
timestamp: Date.now() - (i * 86400000),
blockNumber: 0,
transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000'
});
}
}
// Сортируем по времени (новые сверху)
history.sort((a, b) => b.timestamp - a.timestamp);
console.log(`[DLE Analytics] История получена:`, history);
res.json({
success: true,
data: {
history: history,
totalEvents: history.length,
dleInfo: {
name: dleInfo.name,
symbol: dleInfo.symbol,
creationTimestamp: Number(dleInfo.creationTimestamp),
proposalsCount: Number(proposalsCount)
}
}
});
} catch (error) {
console.error('[DLE Analytics] Ошибка при получении истории:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении истории: ' + error.message
});
}
});
module.exports = router;

287
backend/routes/dleCore.js Normal file
View File

@@ -0,0 +1,287 @@
/**
* 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(`[DLE Core] Чтение данных 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,
dleAddress: dleAddress, // Добавляем адрес контракта
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(`[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}`);
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);
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}`);
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);
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}`);
// Получаем 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 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;

View File

@@ -0,0 +1,356 @@
/**
* 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('/get-extended-history', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE History] Получение расширенной истории для DLE: ${dleAddress}`);
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);
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 getGovernanceParams() external view returns (uint256 quorumPct, uint256 chainId, uint256 supportedCount)",
"function getCurrentChainId() external view returns (uint256)",
"function listSupportedChains() external view returns (uint256[] memory)",
"function getProposalsCount() external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем текущие данные для сравнения
const dleInfo = await dle.getDLEInfo();
const governanceParams = await dle.getGovernanceParams();
const currentChainId = await dle.getCurrentChainId();
const supportedChains = await dle.listSupportedChains();
const proposalsCount = await dle.getProposalsCount();
const history = [];
// 1. Событие создания DLE
history.push({
id: 1,
type: 'dle_created',
title: 'DLE создан',
description: `Создан DLE "${dleInfo.name}" (${dleInfo.symbol})`,
timestamp: Number(dleInfo.creationTimestamp) * 1000,
blockNumber: 0,
transactionHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
details: {
name: dleInfo.name,
symbol: dleInfo.symbol,
location: dleInfo.location,
jurisdiction: Number(dleInfo.jurisdiction),
supportedChains: supportedChains.map(chain => Number(chain))
}
});
// 2. История изменений настроек (кворум, цепочка)
const currentBlock = await provider.getBlockNumber();
const fromBlock = Math.max(0, currentBlock - 10000);
try {
// События изменения кворума
const quorumEvents = await dle.queryFilter('QuorumPercentageUpdated', fromBlock, currentBlock);
for (let i = 0; i < quorumEvents.length; i++) {
const event = quorumEvents[i];
history.push({
id: history.length + 1,
type: 'quorum_updated',
title: 'Изменен кворум',
description: `Кворум изменен с ${Number(event.args.oldQuorumPercentage)}% на ${Number(event.args.newQuorumPercentage)}%`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
oldQuorum: Number(event.args.oldQuorumPercentage),
newQuorum: Number(event.args.newQuorumPercentage)
}
});
}
// События изменения текущей цепочки
const chainEvents = await dle.queryFilter('CurrentChainIdUpdated', fromBlock, currentBlock);
for (let i = 0; i < chainEvents.length; i++) {
const event = chainEvents[i];
history.push({
id: history.length + 1,
type: 'chain_updated',
title: 'Изменена текущая цепочка',
description: `Текущая цепочка изменена с ${Number(event.args.oldChainId)} на ${Number(event.args.newChainId)}`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
oldChainId: Number(event.args.oldChainId),
newChainId: Number(event.args.newChainId)
}
});
}
// События обновления информации DLE
const infoEvents = await dle.queryFilter('DLEInfoUpdated', fromBlock, currentBlock);
for (let i = 0; i < infoEvents.length; i++) {
const event = infoEvents[i];
history.push({
id: history.length + 1,
type: 'dle_info_updated',
title: 'Обновлена информация DLE',
description: `Обновлена информация: ${event.args.name} (${event.args.symbol})`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
name: event.args.name,
symbol: event.args.symbol,
location: event.args.location,
jurisdiction: Number(event.args.jurisdiction)
}
});
}
// 3. История модулей
const moduleAddedEvents = await dle.queryFilter('ModuleAdded', fromBlock, currentBlock);
for (let i = 0; i < moduleAddedEvents.length; i++) {
const event = moduleAddedEvents[i];
const moduleName = getModuleName(event.args.moduleId);
history.push({
id: history.length + 1,
type: 'module_added',
title: 'Модуль добавлен',
description: `Добавлен модуль "${moduleName}"`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
moduleId: event.args.moduleId,
moduleName: moduleName,
moduleAddress: event.args.moduleAddress
}
});
}
const moduleRemovedEvents = await dle.queryFilter('ModuleRemoved', fromBlock, currentBlock);
for (let i = 0; i < moduleRemovedEvents.length; i++) {
const event = moduleRemovedEvents[i];
const moduleName = getModuleName(event.args.moduleId);
history.push({
id: history.length + 1,
type: 'module_removed',
title: 'Модуль удален',
description: `Удален модуль "${moduleName}"`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
moduleId: event.args.moduleId,
moduleName: moduleName
}
});
}
// 4. Мульти-чейн история
const chainAddedEvents = await dle.queryFilter('ChainAdded', fromBlock, currentBlock);
for (let i = 0; i < chainAddedEvents.length; i++) {
const event = chainAddedEvents[i];
const chainName = getChainName(Number(event.args.chainId));
history.push({
id: history.length + 1,
type: 'chain_added',
title: 'Сеть добавлена',
description: `Добавлена сеть "${chainName}" (ID: ${Number(event.args.chainId)})`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
chainId: Number(event.args.chainId),
chainName: chainName
}
});
}
const chainRemovedEvents = await dle.queryFilter('ChainRemoved', fromBlock, currentBlock);
for (let i = 0; i < chainRemovedEvents.length; i++) {
const event = chainRemovedEvents[i];
const chainName = getChainName(Number(event.args.chainId));
history.push({
id: history.length + 1,
type: 'chain_removed',
title: 'Сеть удалена',
description: `Удалена сеть "${chainName}" (ID: ${Number(event.args.chainId)})`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
chainId: Number(event.args.chainId),
chainName: chainName
}
});
}
const executionApprovedEvents = await dle.queryFilter('ProposalExecutionApprovedInChain', fromBlock, currentBlock);
for (let i = 0; i < executionApprovedEvents.length; i++) {
const event = executionApprovedEvents[i];
const chainName = getChainName(Number(event.args.chainId));
history.push({
id: history.length + 1,
type: 'proposal_execution_approved',
title: 'Исполнение предложения одобрено',
description: `Исполнение предложения #${Number(event.args.proposalId)} одобрено в сети "${chainName}"`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
proposalId: Number(event.args.proposalId),
chainId: Number(event.args.chainId),
chainName: chainName
}
});
}
// 5. События предложений (базовые)
const proposalEvents = await dle.queryFilter('ProposalCreated', fromBlock, currentBlock);
for (let i = 0; i < proposalEvents.length; i++) {
const event = proposalEvents[i];
history.push({
id: history.length + 1,
type: 'proposal_created',
title: `Предложение #${Number(event.args.proposalId)} создано`,
description: event.args.description,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
proposalId: Number(event.args.proposalId),
initiator: event.args.initiator,
description: event.args.description
}
});
}
const proposalExecutedEvents = await dle.queryFilter('ProposalExecuted', fromBlock, currentBlock);
for (let i = 0; i < proposalExecutedEvents.length; i++) {
const event = proposalExecutedEvents[i];
history.push({
id: history.length + 1,
type: 'proposal_executed',
title: `Предложение #${Number(event.args.proposalId)} исполнено`,
description: `Предложение успешно исполнено`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
proposalId: Number(event.args.proposalId),
operation: event.args.operation
}
});
}
const proposalCancelledEvents = await dle.queryFilter('ProposalCancelled', fromBlock, currentBlock);
for (let i = 0; i < proposalCancelledEvents.length; i++) {
const event = proposalCancelledEvents[i];
history.push({
id: history.length + 1,
type: 'proposal_cancelled',
title: `Предложение #${Number(event.args.proposalId)} отменено`,
description: `Причина: ${event.args.reason}`,
timestamp: event.blockNumber * 1000,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
details: {
proposalId: Number(event.args.proposalId),
reason: event.args.reason
}
});
}
} catch (error) {
console.log(`[DLE History] Ошибка при получении событий:`, error.message);
}
// Сортируем по времени (новые сверху)
history.sort((a, b) => b.timestamp - a.timestamp);
console.log(`[DLE History] Расширенная история получена:`, history.length, 'событий');
res.json({
success: true,
data: {
history: history,
totalEvents: history.length,
dleInfo: {
name: dleInfo.name,
symbol: dleInfo.symbol,
creationTimestamp: Number(dleInfo.creationTimestamp),
proposalsCount: Number(proposalsCount),
currentChainId: Number(currentChainId),
supportedChains: supportedChains.map(chain => Number(chain))
}
}
});
} catch (error) {
console.error('[DLE History] Ошибка при получении расширенной истории:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении расширенной истории: ' + error.message
});
}
});
// Вспомогательные функции
function getModuleName(moduleId) {
const moduleNames = {
'0x7472656173757279000000000000000000000000000000000000000000000000': 'Treasury',
'0x6d756c7469736967000000000000000000000000000000000000000000000000': 'Multisig',
'0x646561637469766174696f6e0000000000000000000000000000000000000000': 'Deactivation',
'0x616e616c79746963730000000000000000000000000000000000000000000000': 'Analytics',
'0x6e6f74696669636174696f6e7300000000000000000000000000000000000000': 'Notifications'
};
return moduleNames[moduleId] || `Module ${moduleId}`;
}
function getChainName(chainId) {
const chainNames = {
1: 'Ethereum Mainnet',
11155111: 'Sepolia Testnet',
137: 'Polygon',
56: 'BSC',
42161: 'Arbitrum One',
17000: 'Holesky Testnet'
};
return chainNames[chainId] || `Chain ID: ${chainId}`;
}
module.exports = router;

View File

@@ -0,0 +1,303 @@
/**
* 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');
// Проверить активность модуля
router.post('/is-module-active', async (req, res) => {
try {
const { dleAddress, moduleId } = req.body;
if (!dleAddress || !moduleId) {
return res.status(400).json({
success: false,
error: 'Адрес DLE и ID модуля обязательны'
});
}
console.log(`[DLE Modules] Проверка активности модуля: ${moduleId} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function isModuleActive(bytes32 _moduleId) external view returns (bool)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Проверяем активность модуля
const isActive = await dle.isModuleActive(moduleId);
console.log(`[DLE Modules] Активность модуля ${moduleId}: ${isActive}`);
res.json({
success: true,
data: {
isActive: isActive
}
});
} catch (error) {
console.error('[DLE Modules] Ошибка при проверке активности модуля:', error);
res.status(500).json({
success: false,
error: 'Ошибка при проверке активности модуля: ' + error.message
});
}
});
// Получить адрес модуля
router.post('/get-module-address', async (req, res) => {
try {
const { dleAddress, moduleId } = req.body;
if (!dleAddress || !moduleId) {
return res.status(400).json({
success: false,
error: 'Адрес DLE и ID модуля обязательны'
});
}
console.log(`[DLE Modules] Получение адреса модуля: ${moduleId} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getModuleAddress(bytes32 _moduleId) external view returns (address)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем адрес модуля
const moduleAddress = await dle.getModuleAddress(moduleId);
console.log(`[DLE Modules] Адрес модуля ${moduleId}: ${moduleAddress}`);
res.json({
success: true,
data: {
moduleAddress: moduleAddress
}
});
} catch (error) {
console.error('[DLE Modules] Ошибка при получении адреса модуля:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении адреса модуля: ' + error.message
});
}
});
// Получить все модули
router.post('/get-all-modules', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Modules] Получение всех модулей для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function isModuleActive(bytes32 _moduleId) external view returns (bool)",
"function getModuleAddress(bytes32 _moduleId) external view returns (address)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Список известных модулей для проверки
const knownModules = [
"0x7472656173757279000000000000000000000000000000000000000000000000", // "treasury"
"0x6d756c7469736967000000000000000000000000000000000000000000000000", // "multisig"
"0x646561637469766174696f6e0000000000000000000000000000000000000000", // "deactivation"
"0x616e616c79746963730000000000000000000000000000000000000000000000", // "analytics"
"0x6e6f74696669636174696f6e7300000000000000000000000000000000000000" // "notifications"
];
const modules = [];
// Проверяем активность известных модулей
for (const moduleId of knownModules) {
try {
const isActive = await dle.isModuleActive(moduleId);
if (isActive) {
const address = await dle.getModuleAddress(moduleId);
modules.push({
id: moduleId,
address: address,
isActive: isActive
});
}
} catch (error) {
console.log(`[DLE Modules] Ошибка при проверке модуля ${moduleId}:`, error.message);
}
}
console.log(`[DLE Modules] Найдено активных модулей: ${modules.length}`);
res.json({
success: true,
data: {
modules: modules
}
});
} catch (error) {
console.error('[DLE Modules] Ошибка при получении всех модулей:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении всех модулей: ' + error.message
});
}
});
// Создать предложение о добавлении модуля
router.post('/create-add-module-proposal', async (req, res) => {
try {
const { dleAddress, description, duration, moduleId, moduleAddress, chainId } = req.body;
if (!dleAddress || !description || !duration || !moduleId || !moduleAddress || !chainId) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Modules] Создание предложения о добавлении модуля: ${moduleId} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function createAddModuleProposal(string memory _description, uint256 _duration, bytes32 _moduleId, address _moduleAddress, uint256 _chainId) external returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Создаем предложение
const tx = await dle.createAddModuleProposal(description, duration, moduleId, moduleAddress, chainId);
const receipt = await tx.wait();
console.log(`[DLE Modules] Предложение о добавлении модуля создано:`, receipt);
res.json({
success: true,
data: {
proposalId: receipt.logs[0].args.proposalId,
transactionHash: receipt.hash
}
});
} catch (error) {
console.error('[DLE Modules] Ошибка при создании предложения о добавлении модуля:', error);
res.status(500).json({
success: false,
error: 'Ошибка при создании предложения о добавлении модуля: ' + error.message
});
}
});
// Создать предложение об удалении модуля
router.post('/create-remove-module-proposal', async (req, res) => {
try {
const { dleAddress, description, duration, moduleId, chainId } = req.body;
if (!dleAddress || !description || !duration || !moduleId || !chainId) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Modules] Создание предложения об удалении модуля: ${moduleId} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function createRemoveModuleProposal(string memory _description, uint256 _duration, bytes32 _moduleId, uint256 _chainId) external returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Создаем предложение
const tx = await dle.createRemoveModuleProposal(description, duration, moduleId, chainId);
const receipt = await tx.wait();
console.log(`[DLE Modules] Предложение об удалении модуля создано:`, receipt);
res.json({
success: true,
data: {
proposalId: receipt.logs[0].args.proposalId,
transactionHash: receipt.hash
}
});
} catch (error) {
console.error('[DLE Modules] Ошибка при создании предложения об удалении модуля:', error);
res.status(500).json({
success: false,
error: 'Ошибка при создании предложения об удалении модуля: ' + error.message
});
}
});
module.exports = router;

View File

@@ -0,0 +1,434 @@
/**
* 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');
// Получить поддерживаемые сети
router.post('/get-supported-chains', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Multichain] Получение поддерживаемых сетей для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function listSupportedChains() external view returns (uint256[] memory)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем поддерживаемые сети
const supportedChains = await dle.listSupportedChains();
console.log(`[DLE Multichain] Поддерживаемые сети:`, supportedChains);
res.json({
success: true,
data: {
chains: supportedChains.map(chainId => Number(chainId))
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при получении поддерживаемых сетей:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении поддерживаемых сетей: ' + error.message
});
}
});
// Проверить поддержку сети
router.post('/is-chain-supported', async (req, res) => {
try {
const { dleAddress, chainId } = req.body;
if (!dleAddress || chainId === undefined) {
return res.status(400).json({
success: false,
error: 'Адрес DLE и ID сети обязательны'
});
}
console.log(`[DLE Multichain] Проверка поддержки сети ${chainId} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function isChainSupported(uint256 _chainId) external view returns (bool)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Проверяем поддержку сети
const isSupported = await dle.isChainSupported(chainId);
console.log(`[DLE Multichain] Поддержка сети ${chainId}: ${isSupported}`);
res.json({
success: true,
data: {
chainId: Number(chainId),
isSupported: isSupported
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при проверке поддержки сети:', error);
res.status(500).json({
success: false,
error: 'Ошибка при проверке поддержки сети: ' + error.message
});
}
});
// Получить количество поддерживаемых сетей
router.post('/get-supported-chain-count', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Multichain] Получение количества поддерживаемых сетей для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getSupportedChainCount() external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем количество поддерживаемых сетей
const count = await dle.getSupportedChainCount();
console.log(`[DLE Multichain] Количество поддерживаемых сетей: ${count}`);
res.json({
success: true,
data: {
count: Number(count)
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при получении количества поддерживаемых сетей:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении количества поддерживаемых сетей: ' + error.message
});
}
});
// Получить ID сети по индексу
router.post('/get-supported-chain-id', async (req, res) => {
try {
const { dleAddress, index } = req.body;
if (!dleAddress || index === undefined) {
return res.status(400).json({
success: false,
error: 'Адрес DLE и индекс обязательны'
});
}
console.log(`[DLE Multichain] Получение ID сети по индексу ${index} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getSupportedChainId(uint256 _index) external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем ID сети по индексу
const chainId = await dle.getSupportedChainId(index);
console.log(`[DLE Multichain] ID сети по индексу ${index}: ${chainId}`);
res.json({
success: true,
data: {
index: Number(index),
chainId: Number(chainId)
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при получении ID сети по индексу:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении ID сети по индексу: ' + error.message
});
}
});
// Проверить подключение к сети
router.post('/check-chain-connection', async (req, res) => {
try {
const { dleAddress, chainId } = req.body;
if (!dleAddress || chainId === undefined) {
return res.status(400).json({
success: false,
error: 'Адрес DLE и ID сети обязательны'
});
}
console.log(`[DLE Multichain] Проверка подключения к сети ${chainId} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function checkChainConnection(uint256 _chainId) external view returns (bool)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Проверяем подключение к сети
const isAvailable = await dle.checkChainConnection(chainId);
console.log(`[DLE Multichain] Подключение к сети ${chainId}: ${isAvailable}`);
res.json({
success: true,
data: {
chainId: Number(chainId),
isAvailable: isAvailable
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при проверке подключения к сети:', error);
res.status(500).json({
success: false,
error: 'Ошибка при проверке подключения к сети: ' + error.message
});
}
});
// Проверить готовность к синхронизации
router.post('/check-sync-readiness', async (req, res) => {
try {
const { dleAddress, proposalId } = req.body;
if (!dleAddress || proposalId === undefined) {
return res.status(400).json({
success: false,
error: 'Адрес DLE и ID предложения обязательны'
});
}
console.log(`[DLE Multichain] Проверка готовности к синхронизации предложения ${proposalId} для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function checkSyncReadiness(uint256 _proposalId) external view returns (bool)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Проверяем готовность к синхронизации
const allChainsReady = await dle.checkSyncReadiness(proposalId);
console.log(`[DLE Multichain] Готовность к синхронизации предложения ${proposalId}: ${allChainsReady}`);
res.json({
success: true,
data: {
proposalId: Number(proposalId),
allChainsReady: allChainsReady
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при проверке готовности к синхронизации:', error);
res.status(500).json({
success: false,
error: 'Ошибка при проверке готовности к синхронизации: ' + error.message
});
}
});
// Синхронизировать во все сети
router.post('/sync-to-all-chains', async (req, res) => {
try {
const { dleAddress, proposalId, userAddress, privateKey } = req.body;
if (!dleAddress || proposalId === undefined || !userAddress || !privateKey) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны, включая приватный ключ'
});
}
console.log(`[DLE Multichain] Синхронизация предложения ${proposalId} во все сети для DLE: ${dleAddress}`);
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);
const wallet = new ethers.Wallet(privateKey, provider);
const dleAbi = [
"function syncToAllChains(uint256 _proposalId) external"
];
const dle = new ethers.Contract(dleAddress, dleAbi, wallet);
// Синхронизируем во все сети
const tx = await dle.syncToAllChains(proposalId);
const receipt = await tx.wait();
console.log(`[DLE Multichain] Синхронизация выполнена:`, receipt);
res.json({
success: true,
data: {
transactionHash: receipt.hash
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при синхронизации во все сети:', error);
res.status(500).json({
success: false,
error: 'Ошибка при синхронизации во все сети: ' + error.message
});
}
});
// Исполнить предложение по подписям
router.post('/execute-proposal-by-signatures', async (req, res) => {
try {
const { dleAddress, proposalId, signatures, userAddress, privateKey } = req.body;
if (!dleAddress || proposalId === undefined || !signatures || !userAddress || !privateKey) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны, включая приватный ключ'
});
}
console.log(`[DLE Multichain] Исполнение предложения ${proposalId} по подписям для DLE: ${dleAddress}`);
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);
const wallet = new ethers.Wallet(privateKey, provider);
const dleAbi = [
"function executeProposalBySignatures(uint256 _proposalId, bytes[] calldata _signatures) external"
];
const dle = new ethers.Contract(dleAddress, dleAbi, wallet);
// Исполняем предложение по подписям
const tx = await dle.executeProposalBySignatures(proposalId, signatures);
const receipt = await tx.wait();
console.log(`[DLE Multichain] Предложение исполнено по подписям:`, receipt);
res.json({
success: true,
data: {
transactionHash: receipt.hash
}
});
} catch (error) {
console.error('[DLE Multichain] Ошибка при исполнении предложения по подписям:', error);
res.status(500).json({
success: false,
error: 'Ошибка при исполнении предложения по подписям: ' + error.message
});
}
});
module.exports = router;

View File

@@ -0,0 +1,798 @@
/**
* 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');
// Получение списка всех предложений
router.post('/get-proposals', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Proposals] Получение списка предложений для 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 для чтения предложений (используем правильные функции из смарт-контракта)
const dleAbi = [
"function getProposalSummary(uint256 _proposalId) external view returns (uint256 id, string memory description, uint256 forVotes, uint256 againstVotes, bool executed, bool canceled, uint256 deadline, address initiator, uint256 governanceChainId, uint256 snapshotTimepoint, uint256[] memory targets)",
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
"function getProposalState(uint256 _proposalId) external view returns (uint8 state)",
"event ProposalCreated(uint256 proposalId, address initiator, string description)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем события ProposalCreated для определения количества предложений
const currentBlock = await provider.getBlockNumber();
const fromBlock = Math.max(0, currentBlock - 10000); // Последние 10000 блоков
const events = await dle.queryFilter('ProposalCreated', fromBlock, currentBlock);
console.log(`[DLE Proposals] Найдено событий ProposalCreated: ${events.length}`);
console.log(`[DLE Proposals] Диапазон блоков: ${fromBlock} - ${currentBlock}`);
const proposals = [];
// Читаем информацию о каждом предложении
for (let i = 0; i < events.length; i++) {
try {
const proposalId = events[i].args.proposalId;
console.log(`[DLE Proposals] Читаем предложение ID: ${proposalId}`);
// Пробуем несколько раз для новых предложений
let proposal, isPassed;
let retryCount = 0;
const maxRetries = 3;
while (retryCount < maxRetries) {
try {
proposal = await dle.getProposalSummary(proposalId);
const result = await dle.checkProposalResult(proposalId);
isPassed = result.passed;
break; // Успешно прочитали
} catch (error) {
retryCount++;
console.log(`[DLE Proposals] Попытка ${retryCount} чтения предложения ${proposalId} не удалась:`, error.message);
if (retryCount < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 2000)); // Ждем 2 секунды
} else {
throw error; // Превышено количество попыток
}
}
}
console.log(`[DLE Proposals] Данные предложения ${proposalId}:`, {
id: Number(proposal.id),
description: proposal.description,
forVotes: Number(proposal.forVotes),
againstVotes: Number(proposal.againstVotes),
executed: proposal.executed,
canceled: proposal.canceled,
deadline: Number(proposal.deadline),
initiator: proposal.initiator,
governanceChainId: Number(proposal.governanceChainId),
snapshotTimepoint: Number(proposal.snapshotTimepoint),
targets: proposal.targets
});
const proposalInfo = {
id: Number(proposal.id),
description: proposal.description,
forVotes: Number(proposal.forVotes),
againstVotes: Number(proposal.againstVotes),
executed: proposal.executed,
canceled: proposal.canceled,
deadline: Number(proposal.deadline),
initiator: proposal.initiator,
governanceChainId: Number(proposal.governanceChainId),
snapshotTimepoint: Number(proposal.snapshotTimepoint),
targetChains: proposal.targets.map(chainId => Number(chainId)),
isPassed: isPassed,
blockNumber: events[i].blockNumber
};
proposals.push(proposalInfo);
} catch (error) {
console.log(`[DLE Proposals] Ошибка при чтении предложения ${i}:`, error.message);
// Если это ошибка декодирования, возможно предложение еще не полностью записано
if (error.message.includes('could not decode result data')) {
console.log(`[DLE Proposals] Предложение ${i} еще не полностью синхронизировано, пропускаем`);
continue;
}
// Продолжаем с следующим предложением
}
}
// Сортируем по ID предложения (новые сверху)
proposals.sort((a, b) => b.id - a.id);
console.log(`[DLE Proposals] Найдено предложений: ${proposals.length}`);
res.json({
success: true,
data: {
proposals: proposals,
totalCount: proposals.length
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении списка предложений:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении списка предложений: ' + error.message
});
}
});
// Получение информации о предложении
router.post('/get-proposal-info', async (req, res) => {
try {
const { dleAddress, proposalId } = req.body;
if (!dleAddress || proposalId === undefined) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны: dleAddress, proposalId'
});
}
console.log(`[DLE Proposals] Получение информации о предложении ${proposalId} в 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 для чтения информации о предложении
const dleAbi = [
"function proposals(uint256) external view returns (tuple(string description, uint256 duration, bytes operation, uint256 governanceChainId, uint256 startTime, bool executed, uint256 forVotes, uint256 againstVotes))",
"function checkProposalResult(uint256 _proposalId) external view returns (bool)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Читаем информацию о предложении
const proposal = await dle.proposals(proposalId);
const isPassed = await dle.checkProposalResult(proposalId);
// governanceChainId не сохраняется в предложении, используем текущую цепочку
const governanceChainId = 11155111; // Sepolia chain ID
const proposalInfo = {
description: proposal.description,
duration: Number(proposal.duration),
operation: proposal.operation,
governanceChainId: Number(proposal.governanceChainId),
startTime: Number(proposal.startTime),
executed: proposal.executed,
forVotes: Number(proposal.forVotes),
againstVotes: Number(proposal.againstVotes),
isPassed: isPassed
};
console.log(`[DLE Proposals] Информация о предложении получена:`, proposalInfo);
res.json({
success: true,
data: proposalInfo
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении информации о предложении:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении информации о предложении: ' + error.message
});
}
});
// Получить состояние предложения
router.post('/get-proposal-state', async (req, res) => {
try {
const { dleAddress, proposalId } = req.body;
if (!dleAddress || proposalId === undefined) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Proposals] Получение состояния предложения ${proposalId} в DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getProposalState(uint256 _proposalId) public view returns (uint8 state)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем состояние предложения
const state = await dle.getProposalState(proposalId);
console.log(`[DLE Proposals] Состояние предложения ${proposalId}: ${state}`);
res.json({
success: true,
data: {
proposalId: Number(proposalId),
state: Number(state)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении состояния предложения:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении состояния предложения: ' + error.message
});
}
});
// Получить голоса по предложению
router.post('/get-proposal-votes', async (req, res) => {
try {
const { dleAddress, proposalId } = req.body;
if (!dleAddress || proposalId === undefined) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Proposals] Получение голосов по предложению ${proposalId} в DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getProposalVotes(uint256 _proposalId) external view returns (uint256 forVotes, uint256 againstVotes, uint256 totalVotes, uint256 quorumRequired)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем голоса по предложению
const votes = await dle.getProposalVotes(proposalId);
console.log(`[DLE Proposals] Голоса по предложению ${proposalId}:`, votes);
res.json({
success: true,
data: {
proposalId: Number(proposalId),
forVotes: Number(votes.forVotes),
againstVotes: Number(votes.againstVotes),
totalVotes: Number(votes.totalVotes),
quorumRequired: Number(votes.quorumRequired)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении голосов по предложению:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении голосов по предложению: ' + error.message
});
}
});
// Получить количество предложений
router.post('/get-proposals-count', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Proposals] Получение количества предложений для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getProposalsCount() external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем количество предложений
const count = await dle.getProposalsCount();
console.log(`[DLE Proposals] Количество предложений: ${count}`);
res.json({
success: true,
data: {
count: Number(count)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении количества предложений:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении количества предложений: ' + error.message
});
}
});
// Получить список предложений с пагинацией
router.post('/list-proposals', async (req, res) => {
try {
const { dleAddress, offset, limit } = req.body;
if (!dleAddress || offset === undefined || limit === undefined) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Proposals] Получение списка предложений для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем список предложений
const proposals = await dle.listProposals(offset, limit);
console.log(`[DLE Proposals] Список предложений:`, proposals);
res.json({
success: true,
data: {
proposals: proposals.map(p => Number(p)),
offset: Number(offset),
limit: Number(limit)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении списка предложений:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении списка предложений: ' + error.message
});
}
});
// Получить голосующую силу на момент времени
router.post('/get-voting-power-at', async (req, res) => {
try {
const { dleAddress, voter, timepoint } = req.body;
if (!dleAddress || !voter || timepoint === undefined) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Proposals] Получение голосующей силы для ${voter} в DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getVotingPowerAt(address voter, uint256 timepoint) external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем голосующую силу
const votingPower = await dle.getVotingPowerAt(voter, timepoint);
console.log(`[DLE Proposals] Голосующая сила для ${voter}: ${votingPower}`);
res.json({
success: true,
data: {
voter: voter,
timepoint: Number(timepoint),
votingPower: Number(votingPower)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении голосующей силы:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении голосующей силы: ' + error.message
});
}
});
// Получить требуемый кворум на момент времени
router.post('/get-quorum-at', async (req, res) => {
try {
const { dleAddress, timepoint } = req.body;
if (!dleAddress || timepoint === undefined) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Proposals] Получение требуемого кворума для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getQuorumAt(uint256 timepoint) external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем требуемый кворум
const quorum = await dle.getQuorumAt(timepoint);
console.log(`[DLE Proposals] Требуемый кворум: ${quorum}`);
res.json({
success: true,
data: {
timepoint: Number(timepoint),
quorum: Number(quorum)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении требуемого кворума:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении требуемого кворума: ' + error.message
});
}
});
// Исполнить предложение
router.post('/execute-proposal', async (req, res) => {
try {
const { dleAddress, proposalId, userAddress, privateKey } = req.body;
if (!dleAddress || proposalId === undefined || !userAddress || !privateKey) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны, включая приватный ключ'
});
}
console.log(`[DLE Proposals] Исполнение предложения ${proposalId} в DLE: ${dleAddress}`);
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);
const wallet = new ethers.Wallet(privateKey, provider);
const dleAbi = [
"function executeProposal(uint256 _proposalId) external"
];
const dle = new ethers.Contract(dleAddress, dleAbi, wallet);
// Исполняем предложение
const tx = await dle.executeProposal(proposalId);
const receipt = await tx.wait();
console.log(`[DLE Proposals] Предложение исполнено:`, receipt);
res.json({
success: true,
data: {
transactionHash: receipt.hash
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при исполнении предложения:', error);
res.status(500).json({
success: false,
error: 'Ошибка при исполнении предложения: ' + error.message
});
}
});
// Отменить предложение
router.post('/cancel-proposal', async (req, res) => {
try {
const { dleAddress, proposalId, reason, userAddress } = req.body;
if (!dleAddress || proposalId === undefined || !reason || !userAddress) {
return res.status(400).json({
success: false,
error: 'Все поля обязательны'
});
}
console.log(`[DLE Proposals] Отмена предложения ${proposalId} в DLE: ${dleAddress}`);
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);
const dleAbi = [
"function cancelProposal(uint256 _proposalId, string calldata reason) external"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Отменяем предложение
const tx = await dle.cancelProposal(proposalId, reason);
const receipt = await tx.wait();
console.log(`[DLE Proposals] Предложение отменено:`, receipt);
res.json({
success: true,
data: {
transactionHash: receipt.hash
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при отмене предложения:', error);
res.status(500).json({
success: false,
error: 'Ошибка при отмене предложения: ' + error.message
});
}
});
// Получить количество предложений
router.post('/get-proposals-count', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Proposals] Получение количества предложений для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function getProposalsCount() external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
const count = await dle.getProposalsCount();
console.log(`[DLE Proposals] Количество предложений: ${count}`);
res.json({
success: true,
data: {
count: Number(count)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении количества предложений:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении количества предложений: ' + error.message
});
}
});
// Получить список предложений с пагинацией
router.post('/list-proposals', async (req, res) => {
try {
const { dleAddress, offset = 0, limit = 10 } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Proposals] Получение списка предложений для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)",
"function getProposalSummary(uint256 _proposalId) external view returns (uint256 id, string memory description, uint256 forVotes, uint256 againstVotes, bool executed, bool canceled, uint256 deadline, address initiator, uint256 governanceChainId, uint256 snapshotTimepoint, uint256[] memory targets)",
"function getProposalState(uint256 _proposalId) external view returns (uint8 state)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем список ID предложений
const proposalIds = await dle.listProposals(offset, limit);
console.log(`[DLE Proposals] Получены ID предложений:`, proposalIds);
console.log(`[DLE Proposals] Количество ID:`, proposalIds.length);
const proposals = [];
// Получаем детали каждого предложения
console.log(`[DLE Proposals] Начинаем обработку предложений...`);
for (const proposalId of proposalIds) {
try {
const proposal = await dle.getProposalSummary(proposalId);
const state = await dle.getProposalState(proposalId);
proposals.push({
id: Number(proposal.id),
description: proposal.description,
forVotes: Number(proposal.forVotes),
againstVotes: Number(proposal.againstVotes),
executed: proposal.executed,
canceled: proposal.canceled,
deadline: Number(proposal.deadline),
initiator: proposal.initiator,
governanceChainId: Number(proposal.governanceChainId),
snapshotTimepoint: Number(proposal.snapshotTimepoint),
targetChains: proposal.targets.map(chain => Number(chain)),
state: Number(state)
});
} catch (error) {
console.log(`[DLE Proposals] Ошибка при получении деталей предложения ${proposalId}:`, error.message);
// Добавляем базовую информацию о предложении
proposals.push({
id: Number(proposalId),
description: `Предложение #${Number(proposalId)}`,
forVotes: 0,
againstVotes: 0,
executed: false,
canceled: false,
deadline: 0,
initiator: '0x0000000000000000000000000000000000000000',
governanceChainId: 0,
snapshotTimepoint: 0,
targetChains: [],
state: 0
});
}
}
console.log(`[DLE Proposals] Получено предложений: ${proposals.length}`);
res.json({
success: true,
data: {
proposals: proposals,
offset: Number(offset),
limit: Number(limit)
}
});
} catch (error) {
console.error('[DLE Proposals] Ошибка при получении списка предложений:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении списка предложений: ' + error.message
});
}
});
module.exports = router;

211
backend/routes/dleTokens.js Normal file
View File

@@ -0,0 +1,211 @@
/**
* 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');
// Получить баланс токенов
router.post('/get-token-balance', async (req, res) => {
try {
const { dleAddress, account } = req.body;
if (!dleAddress || !account) {
return res.status(400).json({
success: false,
error: 'Адрес DLE и адрес аккаунта обязательны'
});
}
console.log(`[DLE Tokens] Получение баланса токенов для аккаунта: ${account} в DLE: ${dleAddress}`);
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);
const dleAbi = [
"function balanceOf(address account) external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем баланс токенов
const balance = await dle.balanceOf(account);
console.log(`[DLE Tokens] Баланс токенов для ${account}: ${balance}`);
res.json({
success: true,
data: {
account: account,
balance: ethers.formatUnits(balance, 18)
}
});
} catch (error) {
console.error('[DLE Tokens] Ошибка при получении баланса токенов:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении баланса токенов: ' + error.message
});
}
});
// Получить общее предложение токенов
router.post('/get-total-supply', async (req, res) => {
try {
const { dleAddress } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Tokens] Получение общего предложения токенов для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function totalSupply() external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем общее предложение токенов
const totalSupply = await dle.totalSupply();
console.log(`[DLE Tokens] Общее предложение токенов: ${totalSupply}`);
res.json({
success: true,
data: {
totalSupply: ethers.formatUnits(totalSupply, 18)
}
});
} catch (error) {
console.error('[DLE Tokens] Ошибка при получении общего предложения токенов:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении общего предложения токенов: ' + error.message
});
}
});
// Получить держателей токенов
router.post('/get-token-holders', async (req, res) => {
try {
const { dleAddress, offset = 0, limit = 10 } = req.body;
if (!dleAddress) {
return res.status(400).json({
success: false,
error: 'Адрес DLE обязателен'
});
}
console.log(`[DLE Tokens] Получение держателей токенов для DLE: ${dleAddress}`);
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);
const dleAbi = [
"function totalSupply() external view returns (uint256)",
"function balanceOf(address account) external view returns (uint256)"
];
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
// Получаем общее предложение токенов
const totalSupply = await dle.totalSupply();
// Список известных адресов для проверки
const knownAddresses = [
"0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B", // Создатель
"0x15A4ed4759e5762174b300a4Cf51cc17ad967f4d", // Инициатор предложения
"0x2F2F070AA10bD3Ea14949b9953E2040a05421B17", // Сам DLE контракт
"0x0000000000000000000000000000000000000000" // Нулевой адрес
];
const holders = [];
// Проверяем балансы известных адресов
for (const address of knownAddresses) {
try {
const balance = await dle.balanceOf(address);
if (balance > 0) {
const percentage = (Number(balance) / Number(totalSupply)) * 100;
holders.push({
address: address,
balance: ethers.formatUnits(balance, 18),
percentage: percentage
});
}
} catch (error) {
console.log(`[DLE Tokens] Ошибка при получении баланса для ${address}:`, error.message);
}
}
// Сортируем по балансу (убывание)
holders.sort((a, b) => Number(b.balance) - Number(a.balance));
// Применяем пагинацию
const start = Number(offset);
const end = start + Number(limit);
const paginatedHolders = holders.slice(start, end);
console.log(`[DLE Tokens] Найдено держателей токенов: ${holders.length}`);
res.json({
success: true,
data: {
holders: paginatedHolders,
total: holders.length,
offset: Number(offset),
limit: Number(limit)
}
});
} catch (error) {
console.error('[DLE Tokens] Ошибка при получении держателей токенов:', error);
res.status(500).json({
success: false,
error: 'Ошибка при получении держателей токенов: ' + error.message
});
}
});
module.exports = router;