ваше сообщение коммита
This commit is contained in:
@@ -43,6 +43,16 @@ router.get('/nonce', async (req, res) => {
|
||||
return res.status(400).json({ error: 'Address is required' });
|
||||
}
|
||||
|
||||
// Очищаем истекшие nonce перед генерацией нового
|
||||
try {
|
||||
await db.getQuery()(
|
||||
'DELETE FROM nonces WHERE expires_at < NOW()'
|
||||
);
|
||||
logger.info(`[nonce] Cleaned up expired nonces`);
|
||||
} catch (cleanupError) {
|
||||
logger.warn(`[nonce] Error cleaning up expired nonces: ${cleanupError.message}`);
|
||||
}
|
||||
|
||||
// Генерируем случайный nonce
|
||||
const nonce = crypto.randomBytes(16).toString('hex');
|
||||
logger.info(`[nonce] Generated nonce: ${nonce}`);
|
||||
@@ -136,9 +146,9 @@ router.post('/verify', async (req, res) => {
|
||||
console.error('Error reading encryption key:', keyError);
|
||||
}
|
||||
|
||||
// Проверяем nonce в базе данных
|
||||
// Проверяем nonce в базе данных с проверкой времени истечения
|
||||
const nonceResult = await db.getQuery()(
|
||||
'SELECT nonce_encrypted FROM nonces WHERE identity_value_encrypted = encrypt_text($1, $2)',
|
||||
'SELECT nonce_encrypted, expires_at FROM nonces WHERE identity_value_encrypted = encrypt_text($1, $2)',
|
||||
[normalizedAddressLower, encryptionKey]
|
||||
);
|
||||
|
||||
@@ -147,6 +157,14 @@ router.post('/verify', async (req, res) => {
|
||||
return res.status(401).json({ success: false, error: 'Nonce not found' });
|
||||
}
|
||||
|
||||
// Проверяем, не истек ли срок действия nonce
|
||||
const expiresAt = new Date(nonceResult.rows[0].expires_at);
|
||||
const now = new Date();
|
||||
if (now > expiresAt) {
|
||||
logger.error(`[verify] Nonce expired for address: ${normalizedAddressLower}. Expired at: ${expiresAt}, Now: ${now}`);
|
||||
return res.status(401).json({ success: false, error: 'Nonce expired' });
|
||||
}
|
||||
|
||||
// Расшифровываем nonce из базы данных
|
||||
const storedNonce = await db.getQuery()(
|
||||
'SELECT decrypt_text(nonce_encrypted, $1) as nonce FROM nonces WHERE identity_value_encrypted = encrypt_text($2, $1)',
|
||||
@@ -156,9 +174,12 @@ router.post('/verify', async (req, res) => {
|
||||
logger.info(`[verify] Stored nonce from DB: ${storedNonce.rows[0]?.nonce}`);
|
||||
logger.info(`[verify] Nonce from request: ${nonce}`);
|
||||
logger.info(`[verify] Nonce match: ${storedNonce.rows[0]?.nonce === nonce}`);
|
||||
logger.info(`[verify] Stored nonce length: ${storedNonce.rows[0]?.nonce?.length}`);
|
||||
logger.info(`[verify] Request nonce length: ${nonce?.length}`);
|
||||
|
||||
if (storedNonce.rows.length === 0 || storedNonce.rows[0].nonce !== nonce) {
|
||||
logger.error(`[verify] Invalid nonce for address: ${normalizedAddressLower}. Expected: ${storedNonce.rows[0]?.nonce}, Got: ${nonce}`);
|
||||
logger.error(`[verify] Stored nonce type: ${typeof storedNonce.rows[0]?.nonce}, Request nonce type: ${typeof nonce}`);
|
||||
return res.status(401).json({ success: false, error: 'Invalid nonce' });
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,29 @@ const router = express.Router();
|
||||
const { ethers } = require('ethers');
|
||||
const rpcProviderService = require('../services/rpcProviderService');
|
||||
|
||||
// Функция для получения списка сетей из БД для данного DLE
|
||||
async function getSupportedChainIds(dleAddress) {
|
||||
try {
|
||||
const DeployParamsService = require('../services/deployParamsService');
|
||||
const deployParamsService = new DeployParamsService();
|
||||
const deployments = await deployParamsService.getAllDeployments();
|
||||
|
||||
// Находим деплой с данным адресом
|
||||
for (const deployment of deployments) {
|
||||
if (deployment.dleAddress === dleAddress && deployment.supportedChainIds) {
|
||||
console.log(`[Blockchain] Найдены сети для DLE ${dleAddress}:`, deployment.supportedChainIds);
|
||||
return deployment.supportedChainIds;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback к стандартным сетям
|
||||
return [17000, 11155111, 421614, 84532];
|
||||
} catch (error) {
|
||||
console.error(`[Blockchain] Ошибка получения сетей для DLE ${dleAddress}:`, error);
|
||||
return [17000, 11155111, 421614, 84532];
|
||||
}
|
||||
}
|
||||
|
||||
// Чтение данных DLE из блокчейна
|
||||
router.post('/read-dle-info', async (req, res) => {
|
||||
try {
|
||||
@@ -31,7 +54,9 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
|
||||
// Определяем корректную сеть для данного адреса (или используем chainId из запроса)
|
||||
let provider, rpcUrl, targetChainId = req.body.chainId;
|
||||
const candidateChainIds = [11155111, 17000, 421614, 84532];
|
||||
|
||||
// Получаем список сетей из базы данных для данного DLE
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
if (targetChainId) {
|
||||
rpcUrl = await rpcProviderService.getRpcUrlByChainId(Number(targetChainId));
|
||||
if (!rpcUrl) {
|
||||
@@ -43,18 +68,46 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
return res.status(400).json({ success: false, error: `По адресу ${dleAddress} нет контракта в сети ${targetChainId}` });
|
||||
}
|
||||
} else {
|
||||
// Ищем контракт во всех сетях
|
||||
let foundContracts = [];
|
||||
|
||||
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') { provider = prov; rpcUrl = url; targetChainId = cid; break; }
|
||||
if (code && code !== '0x') {
|
||||
// Контракт найден, currentChainId теперь равен block.chainid
|
||||
foundContracts.push({
|
||||
chainId: cid,
|
||||
currentChainId: cid, // currentChainId = block.chainid = cid
|
||||
provider: prov,
|
||||
rpcUrl: url
|
||||
});
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
if (!provider) {
|
||||
|
||||
if (foundContracts.length === 0) {
|
||||
return res.status(400).json({ success: false, error: 'Не удалось найти сеть, где по адресу есть контракт' });
|
||||
}
|
||||
|
||||
// Выбираем первую доступную сеть (currentChainId - это governance chain, не primary)
|
||||
const primaryContract = foundContracts[0];
|
||||
|
||||
if (primaryContract) {
|
||||
// Используем основную сеть для чтения данных
|
||||
provider = primaryContract.provider;
|
||||
rpcUrl = primaryContract.rpcUrl;
|
||||
targetChainId = primaryContract.chainId;
|
||||
} else {
|
||||
// Fallback: берем первый найденный контракт
|
||||
const firstContract = foundContracts[0];
|
||||
provider = firstContract.provider;
|
||||
rpcUrl = firstContract.rpcUrl;
|
||||
targetChainId = firstContract.chainId;
|
||||
}
|
||||
}
|
||||
|
||||
// ABI для чтения данных DLE
|
||||
@@ -75,7 +128,7 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
const dleInfo = await dle.getDLEInfo();
|
||||
const totalSupply = await dle.totalSupply();
|
||||
const quorumPercentage = await dle.quorumPercentage();
|
||||
const currentChainId = await dle.getCurrentChainId();
|
||||
const currentChainId = targetChainId; // currentChainId = block.chainid = targetChainId
|
||||
|
||||
// Читаем логотип
|
||||
let logoURI = '';
|
||||
@@ -205,6 +258,27 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
console.log(`[Blockchain] Ошибка при чтении модулей:`, modulesError.message);
|
||||
}
|
||||
|
||||
// Собираем информацию о всех развернутых сетях
|
||||
const deployedNetworks = [];
|
||||
if (typeof foundContracts !== 'undefined') {
|
||||
for (const contract of foundContracts) {
|
||||
deployedNetworks.push({
|
||||
chainId: contract.chainId,
|
||||
address: dleAddress,
|
||||
currentChainId: contract.currentChainId,
|
||||
isPrimary: false // currentChainId - это governance chain, не primary
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Если chainId был указан в запросе, добавляем только эту сеть
|
||||
deployedNetworks.push({
|
||||
chainId: targetChainId,
|
||||
address: dleAddress,
|
||||
currentChainId: Number(currentChainId),
|
||||
isPrimary: Number(currentChainId) === targetChainId
|
||||
});
|
||||
}
|
||||
|
||||
const blockchainData = {
|
||||
name: dleInfo.name,
|
||||
symbol: dleInfo.symbol,
|
||||
@@ -225,7 +299,8 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
currentChainId: Number(currentChainId),
|
||||
rpcUsed: rpcUrl,
|
||||
participantCount: participantCount,
|
||||
modules: modules // Информация о модулях
|
||||
modules: modules, // Информация о модулях
|
||||
deployedNetworks: deployedNetworks // Информация о всех развернутых сетях
|
||||
};
|
||||
|
||||
console.log(`[Blockchain] Данные DLE прочитаны из блокчейна:`, blockchainData);
|
||||
@@ -260,8 +335,30 @@ router.post('/get-proposals', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение списка предложений для DLE: ${dleAddress}`);
|
||||
|
||||
// Получаем RPC URL для Sepolia
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -345,7 +442,7 @@ router.post('/get-proposals', async (req, res) => {
|
||||
initiator: proposal.initiator,
|
||||
governanceChainId: Number(proposal.governanceChainId),
|
||||
snapshotTimepoint: Number(proposal.snapshotTimepoint),
|
||||
targetChains: proposal.targets.map(chainId => Number(chainId)),
|
||||
targetChains: proposal.targets.map(targetChainId => Number(targetChainId)),
|
||||
isPassed: isPassed,
|
||||
blockNumber: events[i].blockNumber
|
||||
};
|
||||
@@ -400,8 +497,30 @@ router.post('/get-proposal-info', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение информации о предложении ${proposalId} в DLE: ${dleAddress}`);
|
||||
|
||||
// Получаем RPC URL для Sepolia
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -424,7 +543,7 @@ router.post('/get-proposal-info', async (req, res) => {
|
||||
const isPassed = await dle.checkProposalResult(proposalId);
|
||||
|
||||
// governanceChainId не сохраняется в предложении, используем текущую цепочку
|
||||
const governanceChainId = 11155111; // Sepolia chain ID
|
||||
const governanceChainId = targetChainId || 11155111; // Используем найденную сеть или Sepolia по умолчанию
|
||||
|
||||
const proposalInfo = {
|
||||
description: proposal.description,
|
||||
@@ -472,8 +591,30 @@ router.post('/deactivate-dle', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Проверка возможности деактивации DLE: ${dleAddress} пользователем: ${userAddress}`);
|
||||
|
||||
// Получаем RPC URL для Sepolia
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -543,7 +684,30 @@ router.post('/check-deactivation-proposal-result', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Проверка результата предложения деактивации: ${proposalId} для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -598,7 +762,30 @@ router.post('/load-deactivation-proposals', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Загрузка предложений деактивации для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -679,7 +866,30 @@ router.post('/execute-proposal', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Исполнение предложения ${proposalId} в DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -732,7 +942,30 @@ router.post('/cancel-proposal', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Отмена предложения ${proposalId} в DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -794,7 +1027,30 @@ router.post('/get-governance-params', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение параметров управления для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -847,7 +1103,30 @@ router.post('/get-proposal-state', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение состояния предложения ${proposalId} в DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -899,7 +1178,30 @@ router.post('/get-proposal-votes', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение голосов по предложению ${proposalId} в DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -954,7 +1256,30 @@ router.post('/get-proposals-count', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение количества предложений для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1005,7 +1330,30 @@ router.post('/list-proposals', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение списка предложений для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1058,7 +1406,30 @@ router.post('/get-voting-power-at', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение голосующей силы для ${voter} в DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1111,7 +1482,30 @@ router.post('/get-quorum-at', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение требуемого кворума для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1163,7 +1557,30 @@ router.post('/get-token-balance', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение баланса токенов для ${account} в DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1215,7 +1632,30 @@ router.post('/get-total-supply', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение общего предложения токенов для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1266,7 +1706,30 @@ router.post('/is-active', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Проверка активности DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1323,7 +1786,30 @@ router.post('/get-dle-analytics', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение аналитики DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
@@ -1447,7 +1933,30 @@ router.post('/get-dle-history', async (req, res) => {
|
||||
|
||||
console.log(`[Blockchain] Получение истории DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
const candidateChainIds = await getSupportedChainIds(dleAddress);
|
||||
|
||||
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,
|
||||
|
||||
@@ -47,6 +47,28 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
hardhatProcess.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log('✅ Компиляция завершена успешно');
|
||||
|
||||
// Автоматически генерируем ABI для фронтенда
|
||||
try {
|
||||
const { generateABIFile } = require('../scripts/generate-abi');
|
||||
generateABIFile();
|
||||
console.log('✅ ABI файл автоматически обновлен');
|
||||
} catch (abiError) {
|
||||
console.warn('⚠️ Ошибка генерации ABI:', abiError.message);
|
||||
}
|
||||
|
||||
// Автоматически генерируем flattened контракт для верификации
|
||||
try {
|
||||
const { generateFlattened } = require('../scripts/generate-flattened');
|
||||
generateFlattened().then(() => {
|
||||
console.log('✅ Flattened контракт автоматически обновлен');
|
||||
}).catch((flattenError) => {
|
||||
console.warn('⚠️ Ошибка генерации flattened контракта:', flattenError.message);
|
||||
});
|
||||
} catch (flattenError) {
|
||||
console.warn('⚠️ Ошибка генерации flattened контракта:', flattenError.message);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Смарт-контракты скомпилированы успешно',
|
||||
|
||||
@@ -29,7 +29,41 @@ router.post('/get-dle-analytics', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Analytics] Получение аналитики для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
@@ -165,7 +199,41 @@ router.post('/get-dle-history', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Analytics] Получение истории для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
|
||||
@@ -29,8 +29,41 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Core] Чтение данных DLE из блокчейна: ${dleAddress}`);
|
||||
|
||||
// Получаем RPC URL для Sepolia
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [11155111, 421614, 84532, 17000]; // Fallback
|
||||
|
||||
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,
|
||||
@@ -205,11 +238,27 @@ router.post('/get-governance-params', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Core] Получение параметров управления для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Получаем 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 для Sepolia не найден'
|
||||
error: 'RPC URL не найден'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -258,11 +307,27 @@ router.post('/is-active', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Core] Проверка активности DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Получаем 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 для Sepolia не найден'
|
||||
error: 'RPC URL не найден'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -309,8 +374,41 @@ router.post('/deactivate-dle', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Core] Проверка возможности деактивации DLE: ${dleAddress} пользователем: ${userAddress}`);
|
||||
|
||||
// Получаем RPC URL для Sepolia
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [11155111, 421614, 84532, 17000]; // Fallback
|
||||
|
||||
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,
|
||||
|
||||
@@ -30,7 +30,41 @@ router.post('/get-extended-history', async (req, res) => {
|
||||
|
||||
console.log(`[DLE History] Получение расширенной истории для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
|
||||
@@ -575,6 +575,26 @@ router.post('/get-all-modules', async (req, res) => {
|
||||
return networks[chainId] || `Chain ${chainId}`;
|
||||
}
|
||||
|
||||
function getFallbackRpcUrl(chainId) {
|
||||
const fallbackUrls = {
|
||||
11155111: 'https://eth-sepolia.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52',
|
||||
17000: 'https://ethereum-holesky.publicnode.com',
|
||||
421614: 'https://sepolia-rollup.arbitrum.io/rpc',
|
||||
84532: 'https://sepolia.base.org'
|
||||
};
|
||||
return fallbackUrls[chainId] || null;
|
||||
}
|
||||
|
||||
function getEtherscanUrl(chainId) {
|
||||
const etherscanUrls = {
|
||||
11155111: 'https://sepolia.etherscan.io',
|
||||
17000: 'https://holesky.etherscan.io',
|
||||
421614: 'https://sepolia.arbiscan.io',
|
||||
84532: 'https://sepolia.basescan.org'
|
||||
};
|
||||
return etherscanUrls[chainId] || null;
|
||||
}
|
||||
|
||||
function getModuleDescription(moduleType) {
|
||||
const descriptions = {
|
||||
treasury: 'Казначейство DLE - управление финансами, депозиты, выводы, дивиденды',
|
||||
@@ -590,37 +610,57 @@ router.post('/get-all-modules', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Modules] Найдено типов модулей: ${formattedModules.length}`);
|
||||
|
||||
// Получаем поддерживаемые сети из модулей
|
||||
const supportedNetworks = [
|
||||
{
|
||||
chainId: 11155111,
|
||||
networkName: 'Sepolia',
|
||||
rpcUrl: 'https://eth-sepolia.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52',
|
||||
etherscanUrl: 'https://sepolia.etherscan.io',
|
||||
networkIndex: 0
|
||||
},
|
||||
{
|
||||
chainId: 17000,
|
||||
networkName: 'Holesky',
|
||||
rpcUrl: 'https://ethereum-holesky.publicnode.com',
|
||||
etherscanUrl: 'https://holesky.etherscan.io',
|
||||
networkIndex: 1
|
||||
},
|
||||
{
|
||||
chainId: 421614,
|
||||
networkName: 'Arbitrum Sepolia',
|
||||
rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
|
||||
etherscanUrl: 'https://sepolia.arbiscan.io',
|
||||
networkIndex: 2
|
||||
},
|
||||
{
|
||||
chainId: 84532,
|
||||
networkName: 'Base Sepolia',
|
||||
rpcUrl: 'https://sepolia.base.org',
|
||||
etherscanUrl: 'https://sepolia.basescan.org',
|
||||
networkIndex: 3
|
||||
// Получаем поддерживаемые сети из параметров деплоя
|
||||
let supportedNetworks = [];
|
||||
try {
|
||||
const latestParams = await deployParamsService.getLatestDeployParams(1);
|
||||
if (latestParams.length > 0) {
|
||||
const params = latestParams[0];
|
||||
const supportedChainIds = params.supportedChainIds || [];
|
||||
const rpcUrls = params.rpcUrls || params.rpc_urls || {};
|
||||
|
||||
supportedNetworks = supportedChainIds.map((chainId, index) => ({
|
||||
chainId: Number(chainId),
|
||||
networkName: getNetworkName(Number(chainId)),
|
||||
rpcUrl: rpcUrls[chainId] || getFallbackRpcUrl(chainId),
|
||||
etherscanUrl: getEtherscanUrl(chainId),
|
||||
networkIndex: index
|
||||
}));
|
||||
}
|
||||
];
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка получения параметров деплоя:', error);
|
||||
// Fallback для совместимости
|
||||
supportedNetworks = [
|
||||
{
|
||||
chainId: 11155111,
|
||||
networkName: 'Sepolia',
|
||||
rpcUrl: 'https://eth-sepolia.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52',
|
||||
etherscanUrl: 'https://sepolia.etherscan.io',
|
||||
networkIndex: 0
|
||||
},
|
||||
{
|
||||
chainId: 17000,
|
||||
networkName: 'Holesky',
|
||||
rpcUrl: 'https://ethereum-holesky.publicnode.com',
|
||||
etherscanUrl: 'https://holesky.etherscan.io',
|
||||
networkIndex: 1
|
||||
},
|
||||
{
|
||||
chainId: 421614,
|
||||
networkName: 'Arbitrum Sepolia',
|
||||
rpcUrl: 'https://sepolia-rollup.arbitrum.io/rpc',
|
||||
etherscanUrl: 'https://sepolia.arbiscan.io',
|
||||
networkIndex: 2
|
||||
},
|
||||
{
|
||||
chainId: 84532,
|
||||
networkName: 'Base Sepolia',
|
||||
rpcUrl: 'https://sepolia.base.org',
|
||||
etherscanUrl: 'https://sepolia.basescan.org',
|
||||
networkIndex: 3
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
@@ -642,10 +682,57 @@ router.post('/get-all-modules', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Создать предложение о добавлении модуля
|
||||
// Получить deploymentId по адресу DLE
|
||||
router.post('/get-deployment-id', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress } = req.body;
|
||||
|
||||
if (!dleAddress) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Адрес DLE обязателен'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[DLE Modules] Поиск deploymentId для DLE: ${dleAddress}`);
|
||||
|
||||
const DeployParamsService = require('../services/deployParamsService');
|
||||
const deployParamsService = new DeployParamsService();
|
||||
|
||||
// Ищем параметры деплоя по адресу DLE
|
||||
const result = await deployParamsService.getDeployParamsByDleAddress(dleAddress);
|
||||
|
||||
if (!result) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: 'DeploymentId не найден для данного адреса DLE'
|
||||
});
|
||||
}
|
||||
|
||||
await deployParamsService.close();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
deploymentId: result.deployment_id,
|
||||
dleAddress: result.dle_address,
|
||||
deploymentStatus: result.deployment_status
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Modules] Ошибка при получении deploymentId:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Ошибка при получении deploymentId: ' + error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Создать предложение о добавлении модуля (с автоматической оплатой газа)
|
||||
router.post('/create-add-module-proposal', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress, description, duration, moduleId, moduleAddress, chainId } = req.body;
|
||||
const { dleAddress, description, duration, moduleId, moduleAddress, chainId, deploymentId } = req.body;
|
||||
|
||||
if (!dleAddress || !description || !duration || !moduleId || !moduleAddress || !chainId) {
|
||||
return res.status(400).json({
|
||||
@@ -666,14 +753,54 @@ router.post('/create-add-module-proposal', async (req, res) => {
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
|
||||
// Получаем приватный ключ из параметров деплоя
|
||||
let privateKey;
|
||||
if (deploymentId) {
|
||||
const DeployParamsService = require('../services/deployParamsService');
|
||||
const deployParamsService = new DeployParamsService();
|
||||
const params = await deployParamsService.getDeployParams(deploymentId);
|
||||
|
||||
if (!params || !params.privateKey) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Приватный ключ не найден в параметрах деплоя'
|
||||
});
|
||||
}
|
||||
|
||||
privateKey = params.privateKey;
|
||||
await deployParamsService.close();
|
||||
} else {
|
||||
// Fallback к переменной окружения
|
||||
privateKey = process.env.PRIVATE_KEY;
|
||||
if (!privateKey) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Приватный ключ не найден. Укажите deploymentId или установите PRIVATE_KEY'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Создаем кошелек
|
||||
const wallet = new ethers.Wallet(privateKey, provider);
|
||||
console.log(`[DLE Modules] Используем кошелек: ${wallet.address}`);
|
||||
|
||||
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 dle = new ethers.Contract(dleAddress, dleAbi, wallet);
|
||||
|
||||
// Подготавливаем данные для транзакции (не отправляем)
|
||||
const txData = await dle.createAddModuleProposal.populateTransaction(
|
||||
// Отправляем транзакцию автоматически
|
||||
console.log(`[DLE Modules] Отправляем транзакцию создания предложения...`);
|
||||
console.log(`[DLE Modules] Параметры:`, {
|
||||
description,
|
||||
duration,
|
||||
moduleId,
|
||||
moduleAddress,
|
||||
chainId
|
||||
});
|
||||
|
||||
const tx = await dle.createAddModuleProposal(
|
||||
description,
|
||||
duration,
|
||||
moduleId,
|
||||
@@ -681,16 +808,130 @@ router.post('/create-add-module-proposal', async (req, res) => {
|
||||
chainId
|
||||
);
|
||||
|
||||
console.log(`[DLE Modules] Данные транзакции подготовлены:`, txData);
|
||||
console.log(`[DLE Modules] Транзакция отправлена: ${tx.hash}`);
|
||||
console.log(`[DLE Modules] Ожидаем подтверждения...`);
|
||||
|
||||
// Ждем подтверждения
|
||||
const receipt = await tx.wait();
|
||||
|
||||
// Пробуем получить proposalId из возвращаемого значения транзакции
|
||||
let proposalIdFromReturn = null;
|
||||
try {
|
||||
// Если функция возвращает значение, оно должно быть в receipt
|
||||
if (receipt.logs && receipt.logs.length > 0) {
|
||||
console.log(`[DLE Modules] Ищем ProposalCreated в ${receipt.logs.length} логах транзакции...`);
|
||||
|
||||
// Ищем событие с возвращаемым значением
|
||||
for (let i = 0; i < receipt.logs.length; i++) {
|
||||
const log = receipt.logs[i];
|
||||
console.log(`[DLE Modules] Лог ${i}:`, {
|
||||
address: log.address,
|
||||
topics: log.topics,
|
||||
data: log.data
|
||||
});
|
||||
|
||||
try {
|
||||
const parsedLog = dle.interface.parseLog(log);
|
||||
console.log(`[DLE Modules] Парсинг лога ${i}:`, parsedLog);
|
||||
|
||||
if (parsedLog && parsedLog.name === 'ProposalCreated') {
|
||||
proposalIdFromReturn = parsedLog.args.proposalId.toString();
|
||||
console.log(`[DLE Modules] ✅ Получен proposalId из события: ${proposalIdFromReturn}`);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`[DLE Modules] Ошибка парсинга лога ${i}:`, e.message);
|
||||
// Пробуем альтернативный способ - ищем по топикам
|
||||
if (log.topics && log.topics.length > 0) {
|
||||
// ProposalCreated имеет сигнатуру: ProposalCreated(uint256,address,string)
|
||||
// Первый топик - это хеш сигнатуры события
|
||||
const proposalCreatedTopic = ethers.id("ProposalCreated(uint256,address,string)");
|
||||
if (log.topics[0] === proposalCreatedTopic) {
|
||||
console.log(`[DLE Modules] Найден топик ProposalCreated, извлекаем proposalId из данных...`);
|
||||
// proposalId находится в indexed параметрах (топиках)
|
||||
if (log.topics.length > 1) {
|
||||
proposalIdFromReturn = BigInt(log.topics[1]).toString();
|
||||
console.log(`[DLE Modules] ✅ Извлечен proposalId из топика: ${proposalIdFromReturn}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`[DLE Modules] Ошибка при получении proposalId из возвращаемого значения:`, e.message);
|
||||
}
|
||||
console.log(`[DLE Modules] Транзакция подтверждена:`, {
|
||||
hash: receipt.hash,
|
||||
blockNumber: receipt.blockNumber,
|
||||
gasUsed: receipt.gasUsed.toString(),
|
||||
logsCount: receipt.logs.length,
|
||||
status: receipt.status
|
||||
});
|
||||
|
||||
// Используем proposalId из события, если он найден
|
||||
let proposalId = proposalIdFromReturn;
|
||||
|
||||
// Если не найден в событии, пробуем другие способы
|
||||
if (!proposalId) {
|
||||
console.log(`[DLE Modules] Анализируем ${receipt.logs.length} логов для поиска ProposalCreated...`);
|
||||
|
||||
if (receipt.logs && receipt.logs.length > 0) {
|
||||
// Ищем событие ProposalCreated
|
||||
for (let i = 0; i < receipt.logs.length; i++) {
|
||||
const log = receipt.logs[i];
|
||||
console.log(`[DLE Modules] Лог ${i}:`, {
|
||||
address: log.address,
|
||||
topics: log.topics,
|
||||
data: log.data
|
||||
});
|
||||
|
||||
try {
|
||||
const parsedLog = dle.interface.parseLog(log);
|
||||
console.log(`[DLE Modules] Парсинг лога ${i}:`, parsedLog);
|
||||
|
||||
if (parsedLog && parsedLog.name === 'ProposalCreated') {
|
||||
proposalId = parsedLog.args.proposalId.toString();
|
||||
console.log(`[DLE Modules] ✅ Найден ProposalCreated с ID: ${proposalId}`);
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`[DLE Modules] Ошибка парсинга лога ${i}:`, e.message);
|
||||
// Пробуем альтернативный способ - ищем по топикам
|
||||
if (log.topics && log.topics.length > 0) {
|
||||
// ProposalCreated имеет сигнатуру: ProposalCreated(uint256,address,string)
|
||||
// Первый топик - это хеш сигнатуры события
|
||||
const proposalCreatedTopic = ethers.id("ProposalCreated(uint256,address,string)");
|
||||
if (log.topics[0] === proposalCreatedTopic) {
|
||||
console.log(`[DLE Modules] Найден топик ProposalCreated, извлекаем proposalId из данных...`);
|
||||
// proposalId находится в indexed параметрах (топиках)
|
||||
if (log.topics.length > 1) {
|
||||
proposalId = BigInt(log.topics[1]).toString();
|
||||
console.log(`[DLE Modules] ✅ Извлечен proposalId из топика: ${proposalId}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!proposalId) {
|
||||
console.warn(`[DLE Modules] ⚠️ Не удалось извлечь proposalId из логов транзакции`);
|
||||
console.warn(`[DLE Modules] ⚠️ Это критическая проблема - без proposalId нельзя исполнить предложение!`);
|
||||
} else {
|
||||
console.log(`[DLE Modules] ✅ Успешно получен proposalId: ${proposalId}`);
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
to: dleAddress,
|
||||
data: txData.data,
|
||||
value: "0x0",
|
||||
gasLimit: "0x1e8480", // 2,000,000 gas
|
||||
message: "Подготовлены данные для создания предложения о добавлении модуля. Отправьте транзакцию через MetaMask."
|
||||
transactionHash: receipt.hash,
|
||||
proposalId: proposalId,
|
||||
gasUsed: receipt.gasUsed.toString(),
|
||||
message: `Предложение о добавлении модуля успешно создано! ID: ${proposalId || 'неизвестно'}`
|
||||
}
|
||||
});
|
||||
|
||||
@@ -717,7 +958,41 @@ router.post('/create-remove-module-proposal', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Modules] Создание предложения об удалении модуля: ${moduleId} для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
@@ -1255,8 +1530,9 @@ async function createStandardJsonInput(contractName, moduleAddress, dleAddress,
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 200
|
||||
runs: 0
|
||||
},
|
||||
viaIR: true,
|
||||
evmVersion: "paris",
|
||||
outputSelection: {
|
||||
"*": {
|
||||
@@ -1265,7 +1541,7 @@ async function createStandardJsonInput(contractName, moduleAddress, dleAddress,
|
||||
},
|
||||
libraries: {},
|
||||
remappings: [
|
||||
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/"
|
||||
"@openzeppelin/contracts/=@openzeppelin/contracts/"
|
||||
]
|
||||
}
|
||||
};
|
||||
@@ -1904,8 +2180,9 @@ router.post('/deploy-module-all-networks', async (req, res) => {
|
||||
const provider = new ethers.JsonRpcProvider(network.rpcUrl);
|
||||
const wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
// Получаем текущий nonce
|
||||
const currentNonce = await wallet.getNonce();
|
||||
// Используем NonceManager для правильного управления nonce
|
||||
const { nonceManager } = require('../utils/nonceManager');
|
||||
const currentNonce = await nonceManager.getNonce(wallet.address, network.rpcUrl, network.chainId);
|
||||
console.log(`[DLE Modules] Текущий nonce для сети ${network.chainId}: ${currentNonce}`);
|
||||
|
||||
// Получаем фабрику контракта
|
||||
@@ -2057,7 +2334,7 @@ router.post('/verify-dle-all-networks', async (req, res) => {
|
||||
const supportedChainIds = Array.isArray(saved?.networks)
|
||||
? saved.networks.map(n => Number(n.chainId)).filter(v => !isNaN(v))
|
||||
: (saved?.governanceSettings?.supportedChainIds || []);
|
||||
const currentChainId = Number(saved?.governanceSettings?.currentChainId || network.chainId);
|
||||
const currentChainId = Number(saved?.governanceSettings?.currentChainId || 1); // governance chain, не network.chainId
|
||||
|
||||
// Создаем стандартный JSON input для верификации
|
||||
const standardJsonInput = {
|
||||
@@ -2070,7 +2347,7 @@ router.post('/verify-dle-all-networks', async (req, res) => {
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 1
|
||||
runs: 0
|
||||
},
|
||||
viaIR: true,
|
||||
outputSelection: {
|
||||
@@ -2123,7 +2400,7 @@ router.post('/verify-dle-all-networks', async (req, res) => {
|
||||
const initPartners = Array.isArray(found?.initialPartners) ? found.initialPartners : [];
|
||||
const initAmounts = Array.isArray(found?.initialAmounts) ? found.initialAmounts : [];
|
||||
const scIds = Array.isArray(found?.networks) ? found.networks.map(n => Number(n.chainId)).filter(v => !isNaN(v)) : supportedChainIds;
|
||||
const currentCid = Number(found?.governanceSettings?.currentChainId || found?.networks?.[0]?.chainId || network.chainId);
|
||||
const currentCid = Number(found?.governanceSettings?.currentChainId || 1); // governance chain, не network.chainId
|
||||
const encoded = ethers.AbiCoder.defaultAbiCoder().encode(
|
||||
['tuple(string,string,string,string,uint256,string,uint256,uint256,address[],uint256[],uint256[])', 'uint256', 'address'],
|
||||
[[name, symbol, location, coordinates, jurisdiction, oktmo, kpp, quorumPercentage, initPartners, initAmounts.map(a => BigInt(a)), scIds], BigInt(currentCid), initializer]
|
||||
|
||||
@@ -1,442 +1,124 @@
|
||||
/**
|
||||
* 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');
|
||||
const deployParamsService = require('../services/deployParamsService');
|
||||
|
||||
// Получить поддерживаемые сети
|
||||
router.post('/get-supported-chains', async (req, res) => {
|
||||
/**
|
||||
* Получить адрес контракта в указанной сети для мультичейн голосования
|
||||
* POST /api/dle-core/get-multichain-contracts
|
||||
*/
|
||||
router.post('/get-multichain-contracts', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress } = req.body;
|
||||
const { originalContract, targetChainId } = req.body;
|
||||
|
||||
if (!dleAddress) {
|
||||
console.log('🔍 [MULTICHAIN] Поиск контракта для мультичейн голосования:', {
|
||||
originalContract,
|
||||
targetChainId
|
||||
});
|
||||
|
||||
if (!originalContract || !targetChainId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Адрес DLE обязателен'
|
||||
error: 'Не указан originalContract или targetChainId'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[DLE Multichain] Получение поддерживаемых сетей для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
if (!rpcUrl) {
|
||||
|
||||
// Ищем контракт в указанной сети
|
||||
// Для мультичейн контрактов с одинаковым адресом (детерминированный деплой)
|
||||
// или контракты в разных сетях с разными адресами
|
||||
|
||||
// Сначала проверяем, есть ли контракт с таким же адресом в целевой сети
|
||||
const contractsInTargetNetwork = await deployParamsService.getContractsByChainId(targetChainId);
|
||||
|
||||
console.log('📊 [MULTICHAIN] Контракты в целевой сети:', contractsInTargetNetwork);
|
||||
|
||||
// Ищем контракт в целевой сети (все контракты в targetChainId уже отфильтрованы)
|
||||
const targetContract = contractsInTargetNetwork[0]; // Берем первый контракт в целевой сети
|
||||
|
||||
if (targetContract) {
|
||||
console.log('✅ [MULTICHAIN] Найден контракт в целевой сети:', targetContract.dleAddress);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
contractAddress: targetContract.dleAddress,
|
||||
chainId: targetChainId,
|
||||
source: 'database'
|
||||
});
|
||||
}
|
||||
|
||||
// Если не найден контракт в целевой сети, проверяем мультичейн развертывание
|
||||
// с одинаковым адресом (CREATE2)
|
||||
const { ethers } = require('ethers');
|
||||
|
||||
// Получаем RPC URL из параметров деплоя
|
||||
let rpcUrl;
|
||||
try {
|
||||
// Получаем последние параметры деплоя
|
||||
const latestParams = await deployParamsService.getLatestDeployParams(1);
|
||||
if (latestParams.length > 0) {
|
||||
const params = latestParams[0];
|
||||
const rpcUrls = params.rpcUrls || params.rpc_urls || {};
|
||||
rpcUrl = rpcUrls[targetChainId];
|
||||
}
|
||||
|
||||
// Если не найден в параметрах, используем fallback
|
||||
if (!rpcUrl) {
|
||||
const fallbackConfigs = {
|
||||
'11155111': 'https://1rpc.io/sepolia',
|
||||
'17000': 'https://ethereum-holesky.publicnode.com',
|
||||
'421614': 'https://sepolia-rollup.arbitrum.io/rpc',
|
||||
'84532': 'https://sepolia.base.org'
|
||||
};
|
||||
rpcUrl = fallbackConfigs[targetChainId];
|
||||
}
|
||||
|
||||
if (!rpcUrl) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: `Неподдерживаемая сеть: ${targetChainId}`
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка получения RPC URL:', error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'RPC URL для Sepolia не найден'
|
||||
error: 'Ошибка получения конфигурации сети'
|
||||
});
|
||||
}
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
|
||||
const dleAbi = [
|
||||
"function getSupportedChainCount() external view returns (uint256)",
|
||||
"function getSupportedChainId(uint256 _index) external view returns (uint256)"
|
||||
];
|
||||
|
||||
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
||||
|
||||
// Получаем количество поддерживаемых сетей
|
||||
const chainCount = await dle.getSupportedChainCount();
|
||||
|
||||
// Получаем ID каждой сети
|
||||
const supportedChains = [];
|
||||
for (let i = 0; i < Number(chainCount); i++) {
|
||||
const chainId = await dle.getSupportedChainId(i);
|
||||
supportedChains.push(chainId);
|
||||
}
|
||||
|
||||
console.log(`[DLE Multichain] Поддерживаемые сети:`, supportedChains);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
chains: supportedChains.map(chainId => Number(chainId))
|
||||
try {
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
const contractCode = await provider.getCode(originalContract);
|
||||
|
||||
if (contractCode && contractCode !== '0x') {
|
||||
console.log('✅ [MULTICHAIN] Контракт существует в целевой сети с тем же адресом (CREATE2)');
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
contractAddress: originalContract,
|
||||
chainId: targetChainId,
|
||||
source: 'blockchain'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Multichain] Ошибка при получении поддерживаемых сетей:', error);
|
||||
res.status(500).json({
|
||||
} catch (blockchainError) {
|
||||
console.warn('⚠️ [MULTICHAIN] Ошибка проверки контракта в блокчейне:', blockchainError.message);
|
||||
}
|
||||
|
||||
// Контракт не найден
|
||||
console.log('❌ [MULTICHAIN] Контракт не найден в целевой сети');
|
||||
|
||||
return res.json({
|
||||
success: false,
|
||||
error: 'Ошибка при получении поддерживаемых сетей: ' + error.message
|
||||
error: 'Контракт не найден в целевой сети'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ [MULTICHAIN] Ошибка поиска мультичейн контракта:', error);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Внутренняя ошибка сервера'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Проверить поддержку сети
|
||||
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;
|
||||
module.exports = router;
|
||||
346
backend/routes/dleMultichainExecution.js
Normal file
346
backend/routes/dleMultichainExecution.js
Normal file
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* 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');
|
||||
const DeployParamsService = require('../services/deployParamsService');
|
||||
|
||||
/**
|
||||
* Получить информацию о мультиконтрактном предложении
|
||||
* @route POST /api/dle-multichain/get-proposal-multichain-info
|
||||
*/
|
||||
router.post('/get-proposal-multichain-info', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress, proposalId, governanceChainId } = req.body;
|
||||
|
||||
if (!dleAddress || proposalId === undefined || !governanceChainId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Адрес DLE, ID предложения и ID сети голосования обязательны'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[DLE Multichain] Получение информации о предложении ${proposalId} для DLE: ${dleAddress}`);
|
||||
|
||||
// Получаем RPC URL для сети голосования
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(governanceChainId);
|
||||
if (!rpcUrl) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: `RPC URL для сети ${governanceChainId} не найден`
|
||||
});
|
||||
}
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
|
||||
const dleAbi = [
|
||||
"function proposals(uint256) external view returns (uint256 id, string memory description, uint256 forVotes, uint256 againstVotes, bool executed, bool canceled, uint256 deadline, address initiator, bytes memory operation, uint256 governanceChainId, uint256 snapshotTimepoint, uint256[] memory targetChains)",
|
||||
"function getProposalState(uint256 _proposalId) external view returns (uint8 state)",
|
||||
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
|
||||
"function getSupportedChainCount() external view returns (uint256)",
|
||||
"function getSupportedChainId(uint256 _index) external view returns (uint256)"
|
||||
];
|
||||
|
||||
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
||||
|
||||
// Получаем данные предложения
|
||||
const proposal = await dle.proposals(proposalId);
|
||||
const state = await dle.getProposalState(proposalId);
|
||||
const result = await dle.checkProposalResult(proposalId);
|
||||
|
||||
// Получаем поддерживаемые сети
|
||||
const chainCount = await dle.getSupportedChainCount();
|
||||
const supportedChains = [];
|
||||
for (let i = 0; i < chainCount; i++) {
|
||||
const chainId = await dle.getSupportedChainId(i);
|
||||
supportedChains.push(Number(chainId));
|
||||
}
|
||||
|
||||
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,
|
||||
operation: proposal.operation,
|
||||
governanceChainId: Number(proposal.governanceChainId),
|
||||
targetChains: proposal.targetChains.map(chain => Number(chain)),
|
||||
snapshotTimepoint: Number(proposal.snapshotTimepoint),
|
||||
state: Number(state),
|
||||
isPassed: result.passed,
|
||||
quorumReached: result.quorumReached,
|
||||
supportedChains: supportedChains,
|
||||
canExecuteInTargetChains: result.passed && result.quorumReached && !proposal.executed && !proposal.canceled
|
||||
};
|
||||
|
||||
console.log(`[DLE Multichain] Информация о предложении получена:`, proposalInfo);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: proposalInfo
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Multichain] Ошибка при получении информации о предложении:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Ошибка при получении информации о предложении: ' + error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Исполнить предложение во всех целевых сетях
|
||||
* @route POST /api/dle-multichain/execute-in-all-target-chains
|
||||
*/
|
||||
router.post('/execute-in-all-target-chains', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress, proposalId, deploymentId, userAddress } = req.body;
|
||||
|
||||
if (!dleAddress || proposalId === undefined || !deploymentId || !userAddress) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Все поля обязательны'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[DLE Multichain] Исполнение предложения ${proposalId} во всех целевых сетях для DLE: ${dleAddress}`);
|
||||
|
||||
// Получаем параметры деплоя
|
||||
const deployParamsService = new DeployParamsService();
|
||||
const deployParams = await deployParamsService.getDeployParams(deploymentId);
|
||||
|
||||
if (!deployParams || !deployParams.privateKey) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Приватный ключ не найден в параметрах деплоя'
|
||||
});
|
||||
}
|
||||
|
||||
// Получаем информацию о предложении
|
||||
const proposalInfoResponse = await fetch(`${req.protocol}://${req.get('host')}/api/dle-multichain/get-proposal-multichain-info`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
dleAddress,
|
||||
proposalId,
|
||||
governanceChainId: deployParams.currentChainId
|
||||
})
|
||||
});
|
||||
|
||||
const proposalInfo = await proposalInfoResponse.json();
|
||||
|
||||
if (!proposalInfo.success) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Не удалось получить информацию о предложении'
|
||||
});
|
||||
}
|
||||
|
||||
const { targetChains, canExecuteInTargetChains } = proposalInfo.data;
|
||||
|
||||
if (!canExecuteInTargetChains) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Предложение не готово к исполнению в целевых сетях'
|
||||
});
|
||||
}
|
||||
|
||||
if (targetChains.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'У предложения нет целевых сетей для исполнения'
|
||||
});
|
||||
}
|
||||
|
||||
// Исполняем в каждой целевой сети
|
||||
const executionResults = [];
|
||||
|
||||
for (const targetChainId of targetChains) {
|
||||
try {
|
||||
console.log(`[DLE Multichain] Исполнение в сети ${targetChainId}`);
|
||||
|
||||
const result = await executeProposalInChain(
|
||||
dleAddress,
|
||||
proposalId,
|
||||
targetChainId,
|
||||
deployParams.privateKey,
|
||||
userAddress
|
||||
);
|
||||
|
||||
executionResults.push({
|
||||
chainId: targetChainId,
|
||||
success: true,
|
||||
transactionHash: result.transactionHash
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[DLE Multichain] Ошибка исполнения в сети ${targetChainId}:`, error.message);
|
||||
executionResults.push({
|
||||
chainId: targetChainId,
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const successCount = executionResults.filter(r => r.success).length;
|
||||
const totalCount = executionResults.length;
|
||||
|
||||
console.log(`[DLE Multichain] Исполнение завершено: ${successCount}/${totalCount} успешно`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
proposalId,
|
||||
targetChains,
|
||||
executionResults,
|
||||
summary: {
|
||||
total: totalCount,
|
||||
successful: successCount,
|
||||
failed: totalCount - successCount
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Multichain] Ошибка при исполнении во всех целевых сетях:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Ошибка при исполнении во всех целевых сетях: ' + error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Исполнить предложение в конкретной целевой сети
|
||||
* @route POST /api/dle-multichain/execute-in-target-chain
|
||||
*/
|
||||
router.post('/execute-in-target-chain', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress, proposalId, targetChainId, deploymentId, userAddress } = req.body;
|
||||
|
||||
if (!dleAddress || proposalId === undefined || !targetChainId || !deploymentId || !userAddress) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Все поля обязательны'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[DLE Multichain] Исполнение предложения ${proposalId} в сети ${targetChainId} для DLE: ${dleAddress}`);
|
||||
|
||||
// Получаем параметры деплоя
|
||||
const deployParamsService = new DeployParamsService();
|
||||
const deployParams = await deployParamsService.getDeployParams(deploymentId);
|
||||
|
||||
if (!deployParams || !deployParams.privateKey) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Приватный ключ не найден в параметрах деплоя'
|
||||
});
|
||||
}
|
||||
|
||||
// Исполняем в целевой сети
|
||||
const result = await executeProposalInChain(
|
||||
dleAddress,
|
||||
proposalId,
|
||||
targetChainId,
|
||||
deployParams.privateKey,
|
||||
userAddress
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
proposalId,
|
||||
targetChainId,
|
||||
transactionHash: result.transactionHash,
|
||||
blockNumber: result.blockNumber
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Multichain] Ошибка при исполнении в целевой сети:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Ошибка при исполнении в целевой сети: ' + error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Вспомогательная функция для исполнения предложения в конкретной сети
|
||||
*/
|
||||
async function executeProposalInChain(dleAddress, proposalId, chainId, privateKey, userAddress) {
|
||||
// Получаем RPC URL для целевой сети
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(chainId);
|
||||
if (!rpcUrl) {
|
||||
throw new Error(`RPC URL для сети ${chainId} не найден`);
|
||||
}
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
const wallet = new ethers.Wallet(privateKey, provider);
|
||||
|
||||
const dleAbi = [
|
||||
"function executeProposalBySignatures(uint256 _proposalId, address[] calldata signers, bytes[] calldata signatures) external"
|
||||
];
|
||||
|
||||
const dle = new ethers.Contract(dleAddress, dleAbi, wallet);
|
||||
|
||||
// Для простоты используем подпись от одного адреса (кошелька с приватным ключом)
|
||||
// В реальности нужно собрать подписи от держателей токенов
|
||||
const signers = [wallet.address];
|
||||
const signatures = []; // TODO: Реализовать сбор подписей
|
||||
|
||||
// Временная заглушка - используем прямое исполнение если это возможно
|
||||
// В реальности нужно реализовать сбор подписей от держателей токенов
|
||||
try {
|
||||
// Пытаемся исполнить напрямую (если это сеть голосования)
|
||||
const directExecuteAbi = [
|
||||
"function executeProposal(uint256 _proposalId) external"
|
||||
];
|
||||
|
||||
const directDle = new ethers.Contract(dleAddress, directExecuteAbi, wallet);
|
||||
const tx = await directDle.executeProposal(proposalId);
|
||||
const receipt = await tx.wait();
|
||||
|
||||
return {
|
||||
transactionHash: receipt.hash,
|
||||
blockNumber: receipt.blockNumber
|
||||
};
|
||||
|
||||
} catch (directError) {
|
||||
// Если прямое исполнение невозможно, используем подписи
|
||||
if (signatures.length === 0) {
|
||||
throw new Error('Необходимо собрать подписи от держателей токенов для исполнения в целевой сети');
|
||||
}
|
||||
|
||||
const tx = await dle.executeProposalBySignatures(proposalId, signers, signatures);
|
||||
const receipt = await tx.wait();
|
||||
|
||||
return {
|
||||
transactionHash: receipt.hash,
|
||||
blockNumber: receipt.blockNumber
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
||||
@@ -29,137 +29,297 @@ router.post('/get-proposals', async (req, res) => {
|
||||
|
||||
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 getProposalState(uint256 _proposalId) external view returns (uint8 state)",
|
||||
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
|
||||
"function proposals(uint256) external view returns (uint256 id, string memory description, uint256 forVotes, uint256 againstVotes, bool executed, bool canceled, uint256 deadline, address initiator, bytes memory operation, uint256 governanceChainId, uint256 snapshotTimepoint)",
|
||||
"function quorumPercentage() external view returns (uint256)",
|
||||
"function getPastTotalSupply(uint256 timepoint) external view returns (uint256)",
|
||||
"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++) {
|
||||
// Получаем поддерживаемые сети DLE из контракта
|
||||
let supportedChains = [];
|
||||
try {
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
try {
|
||||
const proposalId = events[i].args.proposalId;
|
||||
console.log(`[DLE Proposals] Читаем предложение ID: ${proposalId}`);
|
||||
// Получаем поддерживаемые сети из параметров деплоя
|
||||
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) {
|
||||
console.log(`[DLE Proposals] Не удалось найти сеть для адреса ${dleAddress}`);
|
||||
// Fallback к известным сетям
|
||||
supportedChains = [11155111, 17000, 421614, 84532];
|
||||
console.log(`[DLE Proposals] Используем fallback сети:`, supportedChains);
|
||||
return;
|
||||
}
|
||||
if (rpcUrl) {
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
const dleAbi = [
|
||||
"function getSupportedChainCount() external view returns (uint256)",
|
||||
"function getSupportedChainId(uint256 _index) external view returns (uint256)"
|
||||
];
|
||||
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
||||
|
||||
// Пробуем несколько раз для новых предложений
|
||||
let proposalState, isPassed, quorumReached, forVotes, againstVotes, quorumRequired;
|
||||
let retryCount = 0;
|
||||
const maxRetries = 1;
|
||||
const chainCount = await dle.getSupportedChainCount();
|
||||
console.log(`[DLE Proposals] Количество поддерживаемых сетей: ${chainCount}`);
|
||||
|
||||
while (retryCount < maxRetries) {
|
||||
for (let i = 0; i < Number(chainCount); i++) {
|
||||
const chainId = await dle.getSupportedChainId(i);
|
||||
supportedChains.push(Number(chainId));
|
||||
}
|
||||
|
||||
console.log(`[DLE Proposals] Поддерживаемые сети из контракта:`, supportedChains);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[DLE Proposals] Ошибка получения поддерживаемых сетей из контракта:`, error.message);
|
||||
// Fallback к известным сетям
|
||||
supportedChains = [11155111, 17000, 421614, 84532];
|
||||
console.log(`[DLE Proposals] Используем fallback сети:`, supportedChains);
|
||||
}
|
||||
|
||||
const allProposals = [];
|
||||
|
||||
// Ищем предложения во всех поддерживаемых сетях
|
||||
for (const chainId of supportedChains) {
|
||||
try {
|
||||
console.log(`[DLE Proposals] Поиск предложений в сети ${chainId}...`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(chainId);
|
||||
if (!rpcUrl) {
|
||||
console.log(`[DLE Proposals] RPC URL для сети ${chainId} не найден, пропускаем`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
|
||||
// ABI для чтения предложений (используем getProposalSummary для мультиконтрактов)
|
||||
const dleAbi = [
|
||||
"function getProposalState(uint256 _proposalId) external view returns (uint8 state)",
|
||||
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
|
||||
"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 targetChains)",
|
||||
"function quorumPercentage() external view returns (uint256)",
|
||||
"function getPastTotalSupply(uint256 timepoint) external view returns (uint256)",
|
||||
"function totalSupply() external view returns (uint256)",
|
||||
"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 в сети ${chainId}: ${events.length}`);
|
||||
console.log(`[DLE Proposals] Диапазон блоков: ${fromBlock} - ${currentBlock}`);
|
||||
|
||||
// Читаем информацию о каждом предложении
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
try {
|
||||
proposalState = await dle.getProposalState(proposalId);
|
||||
const result = await dle.checkProposalResult(proposalId);
|
||||
isPassed = result.passed;
|
||||
quorumReached = result.quorumReached;
|
||||
const proposalId = events[i].args.proposalId;
|
||||
console.log(`[DLE Proposals] Читаем предложение ID: ${proposalId}`);
|
||||
|
||||
// Пробуем несколько раз для новых предложений
|
||||
let proposalState, isPassed, quorumReached, forVotes, againstVotes, quorumRequired, currentTotalSupply, quorumPct;
|
||||
let retryCount = 0;
|
||||
const maxRetries = 1;
|
||||
|
||||
while (retryCount < maxRetries) {
|
||||
try {
|
||||
proposalState = await dle.getProposalState(proposalId);
|
||||
const result = await dle.checkProposalResult(proposalId);
|
||||
isPassed = result.passed;
|
||||
quorumReached = result.quorumReached;
|
||||
|
||||
// Получаем данные о голосах из структуры Proposal (включая мультиконтрактные поля)
|
||||
try {
|
||||
const proposalData = await dle.getProposalSummary(proposalId);
|
||||
forVotes = Number(proposalData.forVotes);
|
||||
againstVotes = Number(proposalData.againstVotes);
|
||||
|
||||
// Вычисляем требуемый кворум
|
||||
quorumPct = Number(await dle.quorumPercentage());
|
||||
const pastSupply = Number(await dle.getPastTotalSupply(proposalData.snapshotTimepoint));
|
||||
quorumRequired = Math.floor((pastSupply * quorumPct) / 100);
|
||||
|
||||
// Получаем текущий totalSupply для отображения
|
||||
currentTotalSupply = Number(await dle.totalSupply());
|
||||
|
||||
console.log(`[DLE Proposals] Кворум для предложения ${proposalId}:`, {
|
||||
quorumPercentage: quorumPct,
|
||||
pastSupply: pastSupply,
|
||||
quorumRequired: quorumRequired,
|
||||
quorumPercentageFormatted: `${quorumPct}%`,
|
||||
snapshotTimepoint: proposalData.snapshotTimepoint,
|
||||
pastSupplyFormatted: `${(pastSupply / 10**18).toFixed(2)} DLE`,
|
||||
quorumRequiredFormatted: `${(quorumRequired / 10**18).toFixed(2)} DLE`
|
||||
});
|
||||
} catch (voteError) {
|
||||
console.log(`[DLE Proposals] Ошибка получения голосов для предложения ${proposalId}:`, voteError.message);
|
||||
forVotes = 0;
|
||||
againstVotes = 0;
|
||||
quorumRequired = 0;
|
||||
currentTotalSupply = 0;
|
||||
quorumPct = 0;
|
||||
}
|
||||
|
||||
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(proposalId),
|
||||
description: events[i].args.description,
|
||||
state: Number(proposalState),
|
||||
isPassed: isPassed,
|
||||
quorumReached: quorumReached,
|
||||
forVotes: Number(forVotes),
|
||||
againstVotes: Number(againstVotes),
|
||||
quorumRequired: Number(quorumRequired),
|
||||
initiator: events[i].args.initiator
|
||||
});
|
||||
|
||||
// Фильтруем предложения по времени - только за последние 30 дней
|
||||
const block = await provider.getBlock(events[i].blockNumber);
|
||||
const proposalTime = block.timestamp;
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
const thirtyDaysAgo = currentTime - (30 * 24 * 60 * 60); // 30 дней назад
|
||||
|
||||
if (proposalTime < thirtyDaysAgo) {
|
||||
console.log(`[DLE Proposals] Пропускаем старое предложение ${proposalId} (${new Date(proposalTime * 1000).toISOString()})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Показываем все предложения, включая выполненные и отмененные
|
||||
// Согласно контракту: 0=Pending, 1=Succeeded, 2=Defeated, 3=Executed, 4=Canceled, 5=ReadyForExecution
|
||||
// Убрали фильтрацию выполненных и отмененных предложений для отображения в UI
|
||||
|
||||
// Создаем уникальный ID, включающий chainId
|
||||
const uniqueId = `${chainId}-${proposalId}`;
|
||||
|
||||
// Получаем мультиконтрактные данные из proposalData (если доступны)
|
||||
let operation = null;
|
||||
let governanceChainId = null;
|
||||
let targetChains = [];
|
||||
let decodedOperation = null;
|
||||
let operationDescription = null;
|
||||
|
||||
// Получаем данные о голосах из структуры Proposal
|
||||
try {
|
||||
const proposalData = await dle.proposals(proposalId);
|
||||
forVotes = Number(proposalData.forVotes);
|
||||
againstVotes = Number(proposalData.againstVotes);
|
||||
const proposalData = await dle.getProposalSummary(proposalId);
|
||||
governanceChainId = Number(proposalData.governanceChainId);
|
||||
targetChains = proposalData.targetChains.map(chain => Number(chain));
|
||||
|
||||
// Вычисляем требуемый кворум
|
||||
const quorumPct = Number(await dle.quorumPercentage());
|
||||
const pastSupply = Number(await dle.getPastTotalSupply(proposalData.snapshotTimepoint));
|
||||
quorumRequired = Math.floor((pastSupply * quorumPct) / 100);
|
||||
} catch (voteError) {
|
||||
console.log(`[DLE Proposals] Ошибка получения голосов для предложения ${proposalId}:`, voteError.message);
|
||||
forVotes = 0;
|
||||
againstVotes = 0;
|
||||
quorumRequired = 0;
|
||||
// Получаем operation из отдельного вызова (если нужно)
|
||||
// operation не возвращается в getProposalSummary, но это не критично для мультиконтрактов
|
||||
operation = null; // Пока не реализовано
|
||||
|
||||
// Декодируем операцию (если доступна)
|
||||
if (operation && operation !== '0x') {
|
||||
const { decodeOperation, formatOperation } = require('../utils/operationDecoder');
|
||||
decodedOperation = decodeOperation(operation);
|
||||
operationDescription = formatOperation(decodedOperation);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[DLE Proposals] Не удалось получить мультиконтрактные данные для предложения ${proposalId}:`, error.message);
|
||||
}
|
||||
|
||||
const proposalInfo = {
|
||||
id: Number(proposalId),
|
||||
uniqueId: uniqueId,
|
||||
description: events[i].args.description,
|
||||
state: Number(proposalState),
|
||||
isPassed: isPassed,
|
||||
quorumReached: quorumReached,
|
||||
forVotes: Number(forVotes),
|
||||
againstVotes: Number(againstVotes),
|
||||
quorumRequired: Number(quorumRequired),
|
||||
totalSupply: Number(currentTotalSupply || 0), // Добавляем totalSupply
|
||||
contractQuorumPercentage: Number(quorumPct), // Добавляем процент кворума из контракта
|
||||
initiator: events[i].args.initiator,
|
||||
blockNumber: events[i].blockNumber,
|
||||
transactionHash: events[i].transactionHash,
|
||||
chainId: chainId, // Добавляем информацию о сети
|
||||
timestamp: proposalTime,
|
||||
createdAt: new Date(proposalTime * 1000).toISOString(),
|
||||
executed: Number(proposalState) === 3, // 3 = Executed
|
||||
canceled: Number(proposalState) === 4, // 4 = Canceled
|
||||
// Мультиконтрактные поля
|
||||
operation: operation,
|
||||
governanceChainId: governanceChainId,
|
||||
targetChains: targetChains,
|
||||
isMultichain: targetChains && targetChains.length > 0,
|
||||
decodedOperation: decodedOperation,
|
||||
operationDescription: operationDescription
|
||||
};
|
||||
|
||||
// Проверяем, нет ли уже такого предложения (по уникальному ID)
|
||||
const existingProposal = allProposals.find(p => p.uniqueId === uniqueId);
|
||||
if (!existingProposal) {
|
||||
allProposals.push(proposalInfo);
|
||||
} else {
|
||||
console.log(`[DLE Proposals] Пропускаем дубликат предложения ${uniqueId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[DLE Proposals] Ошибка при чтении предложения ${i}:`, error.message);
|
||||
|
||||
// Если это ошибка декодирования, возможно предложение еще не полностью записано
|
||||
if (error.message.includes('could not decode result data')) {
|
||||
console.log(`[DLE Proposals] Предложение ${i} еще не полностью синхронизировано, пропускаем`);
|
||||
continue;
|
||||
}
|
||||
|
||||
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(proposalId),
|
||||
description: events[i].args.description,
|
||||
state: Number(proposalState),
|
||||
isPassed: isPassed,
|
||||
quorumReached: quorumReached,
|
||||
forVotes: Number(forVotes),
|
||||
againstVotes: Number(againstVotes),
|
||||
quorumRequired: Number(quorumRequired),
|
||||
initiator: events[i].args.initiator
|
||||
});
|
||||
console.log(`[DLE Proposals] Найдено предложений в сети ${chainId}: ${events.length}`);
|
||||
|
||||
const proposalInfo = {
|
||||
id: Number(proposalId),
|
||||
description: events[i].args.description,
|
||||
state: Number(proposalState),
|
||||
isPassed: isPassed,
|
||||
quorumReached: quorumReached,
|
||||
forVotes: Number(forVotes),
|
||||
againstVotes: Number(againstVotes),
|
||||
quorumRequired: Number(quorumRequired),
|
||||
initiator: events[i].args.initiator,
|
||||
blockNumber: events[i].blockNumber,
|
||||
transactionHash: events[i].transactionHash
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Продолжаем с следующим предложением
|
||||
console.log(`[DLE Proposals] Ошибка при поиске предложений в сети ${chainId}:`, error.message);
|
||||
// Продолжаем с следующей сетью
|
||||
}
|
||||
}
|
||||
|
||||
// Сортируем по ID предложения (новые сверху)
|
||||
proposals.sort((a, b) => b.id - a.id);
|
||||
// Сортируем по времени создания (новые сверху), затем по ID
|
||||
allProposals.sort((a, b) => {
|
||||
if (a.timestamp !== b.timestamp) {
|
||||
return b.timestamp - a.timestamp;
|
||||
}
|
||||
return b.id - a.id;
|
||||
});
|
||||
|
||||
console.log(`[DLE Proposals] Найдено предложений: ${proposals.length}`);
|
||||
console.log(`[DLE Proposals] Найдено предложений: ${allProposals.length}`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
proposals: proposals,
|
||||
totalCount: proposals.length
|
||||
proposals: allProposals,
|
||||
totalCount: allProposals.length
|
||||
}
|
||||
});
|
||||
|
||||
@@ -186,8 +346,41 @@ router.post('/get-proposal-info', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Proposals] Получение информации о предложении ${proposalId} в DLE: ${dleAddress}`);
|
||||
|
||||
// Получаем RPC URL для Sepolia
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
@@ -864,6 +1057,9 @@ router.post('/vote-proposal', async (req, res) => {
|
||||
|
||||
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
||||
|
||||
// Пропускаем проверку hasVoted - функция не существует в контракте
|
||||
console.log(`[DLE Proposals] Пропускаем проверку hasVoted - полагаемся на смарт-контракт`);
|
||||
|
||||
// Подготавливаем данные для транзакции (не отправляем)
|
||||
const txData = await dle.vote.populateTransaction(proposalId, support);
|
||||
|
||||
@@ -889,6 +1085,53 @@ router.post('/vote-proposal', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Проверить статус голосования пользователя
|
||||
router.post('/check-vote-status', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress, proposalId, voterAddress } = req.body;
|
||||
|
||||
if (!dleAddress || proposalId === undefined || !voterAddress) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Необходимы dleAddress, proposalId и voterAddress'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[DLE Proposals] Проверка статуса голосования для ${voterAddress} по предложению ${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);
|
||||
|
||||
// Функция hasVoted не существует в контракте DLE
|
||||
console.log(`[DLE Proposals] Функция hasVoted не поддерживается в контракте DLE`);
|
||||
|
||||
const hasVoted = false; // Всегда возвращаем false, так как функция не существует
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
hasVoted: hasVoted,
|
||||
voterAddress: voterAddress,
|
||||
proposalId: proposalId
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Proposals] Ошибка при проверке статуса голосования:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Ошибка при проверке статуса голосования: ' + error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Endpoint для отслеживания подтверждения транзакций голосования
|
||||
router.post('/track-vote-transaction', async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -29,7 +29,41 @@ router.post('/get-token-balance', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Tokens] Получение баланса токенов для аккаунта: ${account} в DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
@@ -81,7 +115,41 @@ router.post('/get-total-supply', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Tokens] Получение общего предложения токенов для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
@@ -132,7 +200,41 @@ router.post('/get-token-holders', async (req, res) => {
|
||||
|
||||
console.log(`[DLE Tokens] Получение держателей токенов для DLE: ${dleAddress}`);
|
||||
|
||||
const rpcUrl = await rpcProviderService.getRpcUrlByChainId(11155111);
|
||||
// Определяем корректную сеть для данного адреса
|
||||
let rpcUrl, targetChainId;
|
||||
let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback
|
||||
|
||||
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,
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const DLEV2Service = require('../services/dleV2Service');
|
||||
const dleV2Service = new DLEV2Service();
|
||||
const UnifiedDeploymentService = require('../services/unifiedDeploymentService');
|
||||
const unifiedDeploymentService = new UnifiedDeploymentService();
|
||||
const logger = require('../utils/logger');
|
||||
const auth = require('../middleware/auth');
|
||||
const path = require('path');
|
||||
@@ -38,7 +38,7 @@ async function executeDeploymentInBackground(deploymentId, dleParams) {
|
||||
deploymentTracker.addLog(deploymentId, '🚀 Начинаем деплой DLE контракта', 'info');
|
||||
|
||||
// Выполняем деплой с передачей deploymentId для WebSocket обновлений
|
||||
const result = await dleV2Service.createDLE(dleParams, deploymentId);
|
||||
const result = await unifiedDeploymentService.createDLE(dleParams, deploymentId);
|
||||
|
||||
// Завершаем успешно
|
||||
deploymentTracker.completeDeployment(deploymentId, result.data);
|
||||
@@ -114,7 +114,7 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) =>
|
||||
*/
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const dles = dleV2Service.getAllDLEs();
|
||||
const dles = await unifiedDeploymentService.getAllDeployments();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
@@ -490,13 +490,8 @@ router.get('/verify/status/:address', auth.requireAuth, async (req, res) => {
|
||||
router.post('/verify/refresh/:address', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { address } = req.params;
|
||||
let { etherscanApiKey } = req.body || {};
|
||||
if (!etherscanApiKey) {
|
||||
try {
|
||||
const { getSecret } = require('../services/secretStore');
|
||||
etherscanApiKey = await getSecret('ETHERSCAN_V2_API_KEY');
|
||||
} catch(_) {}
|
||||
}
|
||||
const ApiKeyManager = require('../utils/apiKeyManager');
|
||||
const etherscanApiKey = ApiKeyManager.getEtherscanApiKey({}, req.body);
|
||||
const data = verificationStore.read(address);
|
||||
if (!data || !data.chains) return res.json({ success: true, data });
|
||||
|
||||
@@ -504,7 +499,7 @@ router.post('/verify/refresh/:address', auth.requireAuth, auth.requireAdmin, asy
|
||||
const needResubmit = Object.values(data.chains).some(c => !c.guid || /Missing or unsupported chainid/i.test(c.status || ''));
|
||||
if (needResubmit && etherscanApiKey) {
|
||||
// Найти карточку DLE
|
||||
const list = dleV2Service.getAllDLEs();
|
||||
const list = unifiedDeploymentService.getAllDLEs();
|
||||
const card = list.find(x => x?.dleAddress && x.dleAddress.toLowerCase() === address.toLowerCase());
|
||||
if (card) {
|
||||
const deployParams = {
|
||||
@@ -520,11 +515,11 @@ router.post('/verify/refresh/:address', auth.requireAuth, auth.requireAdmin, asy
|
||||
initialPartners: Array.isArray(card.initialPartners) ? card.initialPartners : [],
|
||||
initialAmounts: Array.isArray(card.initialAmounts) ? card.initialAmounts : [],
|
||||
supportedChainIds: Array.isArray(card.networks) ? card.networks.map(n => n.chainId).filter(Boolean) : (card.governanceSettings?.supportedChainIds || []),
|
||||
currentChainId: card.governanceSettings?.currentChainId || (Array.isArray(card.networks) && card.networks[0]?.chainId) || 1
|
||||
currentChainId: card.governanceSettings?.currentChainId || 1 // governance chain, не первая сеть
|
||||
};
|
||||
const deployResult = { success: true, data: { dleAddress: card.dleAddress, networks: card.networks || [] } };
|
||||
try {
|
||||
await dleV2Service.autoVerifyAcrossChains({ deployParams, deployResult, apiKey: etherscanApiKey });
|
||||
await unifiedDeploymentService.autoVerifyAcrossChains({ deployParams, deployResult, apiKey: etherscanApiKey });
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
@@ -552,12 +547,14 @@ router.post('/verify/refresh/:address', auth.requireAuth, auth.requireAdmin, asy
|
||||
router.post('/verify/resubmit/:address', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { address } = req.params;
|
||||
const { etherscanApiKey } = req.body || {};
|
||||
if (!etherscanApiKey && !process.env.ETHERSCAN_API_KEY) {
|
||||
const ApiKeyManager = require('../utils/apiKeyManager');
|
||||
const etherscanApiKey = ApiKeyManager.getEtherscanApiKey({}, req.body);
|
||||
|
||||
if (!etherscanApiKey) {
|
||||
return res.status(400).json({ success: false, message: 'etherscanApiKey обязателен' });
|
||||
}
|
||||
// Найти карточку DLE по адресу
|
||||
const list = dleV2Service.getAllDLEs();
|
||||
const list = unifiedDeploymentService.getAllDLEs();
|
||||
const card = list.find(x => x?.dleAddress && x.dleAddress.toLowerCase() === address.toLowerCase());
|
||||
if (!card) return res.status(404).json({ success: false, message: 'Карточка DLE не найдена' });
|
||||
|
||||
@@ -575,13 +572,13 @@ router.post('/verify/resubmit/:address', auth.requireAuth, auth.requireAdmin, as
|
||||
initialPartners: Array.isArray(card.initialPartners) ? card.initialPartners : [],
|
||||
initialAmounts: Array.isArray(card.initialAmounts) ? card.initialAmounts : [],
|
||||
supportedChainIds: Array.isArray(card.networks) ? card.networks.map(n => n.chainId).filter(Boolean) : (card.governanceSettings?.supportedChainIds || []),
|
||||
currentChainId: card.governanceSettings?.currentChainId || (Array.isArray(card.networks) && card.networks[0]?.chainId) || 1
|
||||
currentChainId: card.governanceSettings?.currentChainId || 1 // governance chain, не первая сеть
|
||||
};
|
||||
|
||||
// Сформировать deployResult из карточки
|
||||
const deployResult = { success: true, data: { dleAddress: card.dleAddress, networks: card.networks || [] } };
|
||||
|
||||
await dleV2Service.autoVerifyAcrossChains({ deployParams, deployResult, apiKey: etherscanApiKey });
|
||||
await unifiedDeploymentService.autoVerifyAcrossChains({ deployParams, deployResult, apiKey: etherscanApiKey });
|
||||
const updated = verificationStore.read(address);
|
||||
return res.json({ success: true, data: updated });
|
||||
} catch (e) {
|
||||
@@ -597,7 +594,7 @@ router.post('/precheck', auth.requireAuth, auth.requireAdmin, async (req, res) =
|
||||
if (!Array.isArray(supportedChainIds) || supportedChainIds.length === 0) {
|
||||
return res.status(400).json({ success: false, message: 'Не переданы сети для проверки' });
|
||||
}
|
||||
const result = await dleV2Service.checkBalances(supportedChainIds, privateKey);
|
||||
const result = await unifiedDeploymentService.checkBalances(supportedChainIds, privateKey);
|
||||
return res.json({ success: true, data: result });
|
||||
} catch (e) {
|
||||
return res.status(500).json({ success: false, message: e.message });
|
||||
|
||||
@@ -60,8 +60,10 @@ router.post('/deploy-module-from-db', async (req, res) => {
|
||||
process.env.PRIVATE_KEY = params.privateKey || params.private_key;
|
||||
}
|
||||
|
||||
if (params.etherscanApiKey || params.etherscan_api_key) {
|
||||
process.env.ETHERSCAN_API_KEY = params.etherscanApiKey || params.etherscan_api_key;
|
||||
const ApiKeyManager = require('../utils/apiKeyManager');
|
||||
const etherscanKey = ApiKeyManager.getAndSetEtherscanApiKey(params);
|
||||
|
||||
if (etherscanKey) {
|
||||
}
|
||||
|
||||
// Запускаем деплой модулей через скрипт
|
||||
|
||||
Reference in New Issue
Block a user