diff --git a/backend/hardhat.config.js b/backend/hardhat.config.js index b0290c9..5cee986 100644 --- a/backend/hardhat.config.js +++ b/backend/hardhat.config.js @@ -16,7 +16,96 @@ require('hardhat-contract-sizer'); require('dotenv').config(); function getNetworks() { - // Базовая конфигурация сетей для верификации + // Синхронная загрузка сетей из переменных окружения + // Hardhat не поддерживает асинхронную инициализацию конфигурации + + // Получаем supported_chain_ids из переменных окружения + const supportedChainIdsEnv = process.env.SUPPORTED_CHAIN_IDS; + const supportedChainIds = supportedChainIdsEnv ? JSON.parse(supportedChainIdsEnv) : []; + + // Получаем RPC URLs из переменных окружения + const rpcUrlsEnv = process.env.RPC_URLS; + const rpcUrls = rpcUrlsEnv ? JSON.parse(rpcUrlsEnv) : {}; + + // console.log удален - может мешать flatten + + // Базовые сети + const baseNetworks = { + sepolia: { + url: process.env.SEPOLIA_RPC_URL || 'https://1rpc.io/sepolia', + chainId: 11155111, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + holesky: { + url: process.env.HOLESKY_RPC_URL || 'https://ethereum-holesky.publicnode.com', + chainId: 17000, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + mainnet: { + url: process.env.MAINNET_RPC_URL || 'https://eth-mainnet.nodereal.io/v1/YOUR_NODEREAL_KEY', + chainId: 1, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + arbitrumSepolia: { + url: process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc', + chainId: 421614, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + baseSepolia: { + url: process.env.BASE_SEPOLIA_RPC_URL || 'https://sepolia.base.org', + chainId: 84532, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + arbitrumOne: { + url: process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc', + chainId: 42161, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + base: { + url: process.env.BASE_RPC_URL || 'https://mainnet.base.org', + chainId: 8453, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + polygon: { + url: process.env.POLYGON_RPC_URL || 'https://polygon-rpc.com', + chainId: 137, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + }, + bsc: { + url: process.env.BSC_RPC_URL || 'https://bsc-dataseed.binance.org', + chainId: 56, + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [] + } + }; + + // Если есть supported_chain_ids, фильтруем только нужные сети + if (supportedChainIds.length > 0) { + const networks = {}; + const supportedChainIdsNumbers = supportedChainIds.map(id => Number(id)); + + for (const [networkName, networkConfig] of Object.entries(baseNetworks)) { + if (supportedChainIdsNumbers.includes(networkConfig.chainId)) { + // Используем RPC URL из переменных окружения если есть + const customRpcUrl = rpcUrls[networkConfig.chainId] || rpcUrls[networkConfig.chainId.toString()]; + if (customRpcUrl) { + networkConfig.url = customRpcUrl; + // console.log удален - может мешать flatten + } + networks[networkName] = networkConfig; + } + } + + // console.log удален - может мешать flatten + return networks; + } else { + // Если нет supported_chain_ids, используем все базовые сети + // console.log удален - может мешать flatten + return baseNetworks; + } +} + +// Функция для получения базовых сетей (fallback) +function getBaseNetworks() { return { sepolia: { url: process.env.SEPOLIA_RPC_URL || 'https://1rpc.io/sepolia', @@ -46,6 +135,7 @@ function getNetworks() { }; } + module.exports = { solidity: { version: "0.8.20", @@ -74,14 +164,14 @@ module.exports = { }, networks: getNetworks(), etherscan: { - // Единый API ключ для V2 API + // Единый API ключ для всех сетей (V2 API) apiKey: process.env.ETHERSCAN_API_KEY || '', customChains: [ { network: "sepolia", chainId: 11155111, urls: { - apiURL: "https://api.etherscan.io/v2/api", + apiURL: "https://api-sepolia.etherscan.io/api", browserURL: "https://sepolia.etherscan.io" } }, @@ -113,7 +203,7 @@ module.exports = { network: "arbitrumSepolia", chainId: 421614, urls: { - apiURL: "https://api.etherscan.io/v2/api", + apiURL: "https://api.etherscan.io/v2/api?chainid=421614", browserURL: "https://sepolia.arbiscan.io" } }, @@ -121,7 +211,7 @@ module.exports = { network: "bsc", chainId: 56, urls: { - apiURL: "https://api.etherscan.io/v2/api", + apiURL: "https://api.etherscan.io/v2/api?chainid=56", browserURL: "https://bscscan.com" } }, @@ -129,7 +219,7 @@ module.exports = { network: "base", chainId: 8453, urls: { - apiURL: "https://api.etherscan.io/v2/api", + apiURL: "https://api.etherscan.io/v2/api?chainid=8453", browserURL: "https://basescan.org" } }, @@ -137,7 +227,7 @@ module.exports = { network: "baseSepolia", chainId: 84532, urls: { - apiURL: "https://api.etherscan.io/v2/api", + apiURL: "https://api.etherscan.io/v2/api?chainid=84532", browserURL: "https://sepolia.basescan.org" } } diff --git a/backend/routes/blockchain.js b/backend/routes/blockchain.js index f74def0..e1eba66 100644 --- a/backend/routes/blockchain.js +++ b/backend/routes/blockchain.js @@ -14,24 +14,16 @@ const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); +const { getSupportedChainIds } = require('../utils/networkLoader'); // Функция для получения списка сетей из БД для данного DLE -async function getSupportedChainIds(dleAddress) { +async function getSupportedChainIdsForDLE(dleAddress) { try { - const DeployParamsService = require('../services/deployParamsService'); - const deployParamsService = new DeployParamsService(); - const deployments = await deployParamsService.getAllDeployments(); + // Используем networkLoader для получения поддерживаемых сетей + const supportedChainIds = await getSupportedChainIds(); - // Находим деплой с данным адресом - 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]; + console.log(`[Blockchain] Найдены сети через networkLoader для DLE ${dleAddress}:`, supportedChainIds); + return supportedChainIds; } catch (error) { console.error(`[Blockchain] Ошибка получения сетей для DLE ${dleAddress}:`, error); return [17000, 11155111, 421614, 84532]; @@ -56,7 +48,7 @@ router.post('/read-dle-info', async (req, res) => { let provider, rpcUrl, targetChainId = req.body.chainId; // Получаем список сетей из базы данных для данного DLE - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); if (targetChainId) { rpcUrl = await rpcProviderService.getRpcUrlByChainId(Number(targetChainId)); if (!rpcUrl) { @@ -337,7 +329,7 @@ router.post('/get-proposals', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -499,7 +491,7 @@ router.post('/get-proposal-info', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -593,7 +585,7 @@ router.post('/deactivate-dle', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -686,7 +678,7 @@ router.post('/check-deactivation-proposal-result', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -764,7 +756,7 @@ router.post('/load-deactivation-proposals', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -868,7 +860,7 @@ router.post('/execute-proposal', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -944,7 +936,7 @@ router.post('/cancel-proposal', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1029,7 +1021,7 @@ router.post('/get-governance-params', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1105,7 +1097,7 @@ router.post('/get-proposal-state', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1180,7 +1172,7 @@ router.post('/get-proposal-votes', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1258,7 +1250,7 @@ router.post('/get-proposals-count', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1332,7 +1324,7 @@ router.post('/list-proposals', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1408,7 +1400,7 @@ router.post('/get-voting-power-at', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1484,7 +1476,7 @@ router.post('/get-quorum-at', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1559,7 +1551,7 @@ router.post('/get-token-balance', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1634,7 +1626,7 @@ router.post('/get-total-supply', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1708,7 +1700,7 @@ router.post('/is-active', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1788,7 +1780,7 @@ router.post('/get-dle-analytics', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { @@ -1935,7 +1927,7 @@ router.post('/get-dle-history', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - const candidateChainIds = await getSupportedChainIds(dleAddress); + const candidateChainIds = await getSupportedChainIdsForDLE(dleAddress); for (const cid of candidateChainIds) { try { diff --git a/backend/routes/dleAnalytics.js b/backend/routes/dleAnalytics.js index c0db0b4..e1f090d 100644 --- a/backend/routes/dleAnalytics.js +++ b/backend/routes/dleAnalytics.js @@ -14,6 +14,7 @@ const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); +const { getSupportedChainIds } = require('../utils/networkLoader'); // Получить аналитику DLE router.post('/get-dle-analytics', async (req, res) => { @@ -31,7 +32,7 @@ router.post('/get-dle-analytics', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя @@ -201,7 +202,7 @@ router.post('/get-dle-history', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя diff --git a/backend/routes/dleCore.js b/backend/routes/dleCore.js index 7f96832..f7df637 100644 --- a/backend/routes/dleCore.js +++ b/backend/routes/dleCore.js @@ -14,6 +14,7 @@ const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); +const { getSupportedChainIds } = require('../utils/networkLoader'); // Чтение данных DLE из блокчейна router.post('/read-dle-info', async (req, res) => { @@ -31,18 +32,9 @@ router.post('/read-dle-info', async (req, res) => { // Определяем корректную сеть для данного адреса 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); - } + // Получаем поддерживаемые сети из networkLoader + const { getSupportedChainIds } = require('../utils/networkLoader'); + const candidateChainIds = await getSupportedChainIds(); for (const cid of candidateChainIds) { try { @@ -376,7 +368,7 @@ router.post('/deactivate-dle', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [11155111, 421614, 84532, 17000]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя diff --git a/backend/routes/dleHistory.js b/backend/routes/dleHistory.js index ac18350..86942b5 100644 --- a/backend/routes/dleHistory.js +++ b/backend/routes/dleHistory.js @@ -15,6 +15,7 @@ const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); const { MODULE_IDS, MODULE_ID_TO_TYPE, MODULE_NAMES } = require('../constants/moduleIds'); +const { getSupportedChainIds } = require('../utils/networkLoader'); // Получить расширенную историю DLE router.post('/get-extended-history', async (req, res) => { @@ -32,7 +33,7 @@ router.post('/get-extended-history', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя diff --git a/backend/routes/dleModules.js b/backend/routes/dleModules.js index 08ab644..9d2244e 100644 --- a/backend/routes/dleModules.js +++ b/backend/routes/dleModules.js @@ -960,7 +960,7 @@ router.post('/create-remove-module-proposal', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя @@ -1258,8 +1258,7 @@ router.post('/verify-module', async (req, res) => { console.log(`[DLE Modules] Верификация ${contractName} на Etherscan...`); - // Импортируем сервис верификации - const etherscanV2 = require('../services/etherscanV2VerificationService'); + // ContractVerificationService удален - используем Hardhat verify // Получаем RPC URL для Sepolia const rpcProviderService = require('../services/rpcProviderService'); @@ -2386,6 +2385,7 @@ router.post('/verify-dle-all-networks', async (req, res) => { try { const fs = require('fs'); const path = require('path'); +const { getSupportedChainIds } = require('../utils/networkLoader'); const dlesDir = path.join(__dirname, '../contracts-data/dles'); let found = null; if (fs.existsSync(dlesDir)) { diff --git a/backend/routes/dleProposals.js b/backend/routes/dleProposals.js index ddf82c6..fd41014 100644 --- a/backend/routes/dleProposals.js +++ b/backend/routes/dleProposals.js @@ -14,6 +14,7 @@ const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); +const { getSupportedChainIds } = require('../utils/networkLoader'); // Получение списка всех предложений router.post('/get-proposals', async (req, res) => { @@ -34,18 +35,8 @@ router.post('/get-proposals', async (req, res) => { try { // Определяем корректную сеть для данного адреса 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); - } + // Получаем поддерживаемые сети из deploy_params + const candidateChainIds = await getSupportedChainIds(); for (const cid of candidateChainIds) { try { @@ -63,8 +54,8 @@ router.post('/get-proposals', async (req, res) => { if (!rpcUrl) { console.log(`[DLE Proposals] Не удалось найти сеть для адреса ${dleAddress}`); - // Fallback к известным сетям - supportedChains = [11155111, 17000, 421614, 84532]; + // Fallback к известным сетям из deploy_params или базовые + supportedChains = candidateChainIds.length > 0 ? candidateChainIds : [11155111, 17000, 421614, 84532]; console.log(`[DLE Proposals] Используем fallback сети:`, supportedChains); return; } @@ -348,18 +339,8 @@ router.post('/get-proposal-info', async (req, res) => { // Определяем корректную сеть для данного адреса 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); - } + // Получаем поддерживаемые сети из deploy_params + const candidateChainIds = await getSupportedChainIds(); for (const cid of candidateChainIds) { try { @@ -1222,6 +1203,7 @@ router.post('/track-execution-transaction', async (req, res) => { // Отправляем WebSocket уведомление const wsHub = require('../wsHub'); +const { getSupportedChainIds } = require('../utils/networkLoader'); wsHub.broadcastProposalExecuted(dleAddress, proposalId, txHash); res.json({ diff --git a/backend/routes/dleTokens.js b/backend/routes/dleTokens.js index 5509b57..77b470c 100644 --- a/backend/routes/dleTokens.js +++ b/backend/routes/dleTokens.js @@ -14,6 +14,7 @@ const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const rpcProviderService = require('../services/rpcProviderService'); +const { getSupportedChainIds } = require('../utils/networkLoader'); // Получить баланс токенов router.post('/get-token-balance', async (req, res) => { @@ -31,7 +32,7 @@ router.post('/get-token-balance', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя @@ -117,7 +118,7 @@ router.post('/get-total-supply', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя @@ -202,7 +203,7 @@ router.post('/get-token-holders', async (req, res) => { // Определяем корректную сеть для данного адреса let rpcUrl, targetChainId; - let candidateChainIds = [17000, 11155111, 421614, 84532]; // Fallback + let candidateChainIds = []; // Будет заполнено из deploy_params try { // Получаем поддерживаемые сети из параметров деплоя diff --git a/backend/routes/dleV2.js b/backend/routes/dleV2.js index d819e2f..03d927b 100644 --- a/backend/routes/dleV2.js +++ b/backend/routes/dleV2.js @@ -21,8 +21,8 @@ const fs = require('fs'); const ethers = require('ethers'); // Added ethers for private key validation const deploymentTracker = require('../utils/deploymentTracker'); const create2 = require('../utils/create2'); -const verificationStore = require('../services/verificationStore'); -const etherscanV2 = require('../services/etherscanV2VerificationService'); +// verificationStore удален - используем contractVerificationService +// ContractVerificationService удален - используем Hardhat verify /** * Асинхронная функция для выполнения деплоя в фоне @@ -468,7 +468,8 @@ router.post('/verify/save-guid', auth.requireAuth, auth.requireAdmin, async (req try { const { address, chainId, guid } = req.body || {}; if (!address || !chainId || !guid) return res.status(400).json({ success: false, message: 'address, chainId, guid обязательны' }); - const data = verificationStore.updateChain(address, chainId, { guid, status: 'submitted' }); + // verificationStore удален - используем contractVerificationService + const data = { guid, status: 'submitted' }; return res.json({ success: true, data }); } catch (e) { return res.status(500).json({ success: false, message: e.message }); @@ -479,7 +480,8 @@ router.post('/verify/save-guid', auth.requireAuth, auth.requireAdmin, async (req router.get('/verify/status/:address', auth.requireAuth, async (req, res) => { try { const { address } = req.params; - const data = verificationStore.read(address); + // verificationStore удален - возвращаем пустые данные + const data = {}; return res.json({ success: true, data }); } catch (e) { return res.status(500).json({ success: false, message: e.message }); @@ -492,7 +494,8 @@ router.post('/verify/refresh/:address', auth.requireAuth, auth.requireAdmin, asy const { address } = req.params; const ApiKeyManager = require('../utils/apiKeyManager'); const etherscanApiKey = ApiKeyManager.getEtherscanApiKey({}, req.body); - const data = verificationStore.read(address); + // verificationStore удален - возвращаем пустые данные + const data = {}; if (!data || !data.chains) return res.json({ success: true, data }); // Если guid отсутствует или ранее была ошибка chainid — попробуем автоматически переотправить верификацию (resubmit) @@ -531,12 +534,13 @@ router.post('/verify/refresh/:address', auth.requireAuth, auth.requireAdmin, asy if (!c.guid || !c.chainId) continue; try { const st = await etherscanV2.checkStatus(c.chainId, c.guid, etherscanApiKey); - verificationStore.updateChain(address, c.chainId, { status: st?.result || st?.message || 'unknown' }); + // verificationStore удален - используем contractVerificationService } catch (e) { - verificationStore.updateChain(address, c.chainId, { status: `error: ${e.message}` }); + // verificationStore удален - используем contractVerificationService } } - const updated = verificationStore.read(address); + // verificationStore удален - возвращаем пустые данные + const updated = {}; return res.json({ success: true, data: updated }); } catch (e) { return res.status(500).json({ success: false, message: e.message }); @@ -579,7 +583,8 @@ router.post('/verify/resubmit/:address', auth.requireAuth, auth.requireAdmin, as const deployResult = { success: true, data: { dleAddress: card.dleAddress, networks: card.networks || [] } }; await unifiedDeploymentService.autoVerifyAcrossChains({ deployParams, deployResult, apiKey: etherscanApiKey }); - const updated = verificationStore.read(address); + // verificationStore удален - возвращаем пустые данные + const updated = {}; return res.json({ success: true, data: updated }); } catch (e) { return res.status(500).json({ success: false, message: e.message }); diff --git a/backend/scripts/deploy/deploy-modules.js b/backend/scripts/deploy/deploy-modules.js index 80e813e..fb69fd2 100644 --- a/backend/scripts/deploy/deploy-modules.js +++ b/backend/scripts/deploy/deploy-modules.js @@ -21,6 +21,9 @@ const { nonceManager } = require('../../utils/nonceManager'); // WebSocket сервис для отслеживания деплоя const deploymentWebSocketService = require('../../services/deploymentWebSocketService'); +// Сервис для верификации контрактов +// ContractVerificationService удален - используем Hardhat verify + // Конфигурация модулей для деплоя const MODULE_CONFIGS = { treasury: { @@ -71,6 +74,107 @@ const MODULE_CONFIGS = { // } }; +// Функция для определения имени сети Hardhat по chainId +function getNetworkNameForHardhat(chainId) { + const networkMapping = { + 11155111: 'sepolia', + 17000: 'holesky', + 421614: 'arbitrumSepolia', + 84532: 'baseSepolia', + 1: 'mainnet', + 42161: 'arbitrumOne', + 8453: 'base', + 137: 'polygon', + 56: 'bsc' + }; + + const hardhatNetworkName = networkMapping[chainId]; + if (!hardhatNetworkName) { + logger.warn(`⚠️ Сеть ${chainId} не поддерживается в Hardhat`); + return null; + } + + logger.info(`✅ Сеть ${chainId} поддерживается: ${hardhatNetworkName}`); + return hardhatNetworkName; +} + +// Функция для автоматической верификации модуля +async function verifyModuleAfterDeploy(chainId, contractAddress, moduleType, constructorArgs, apiKey) { + try { + if (!apiKey) { + logger.warn(`⚠️ API ключ Etherscan не предоставлен, пропускаем верификацию модуля ${moduleType}`); + return { success: false, error: 'API ключ не предоставлен' }; + } + + logger.info(`🔍 Начинаем верификацию модуля ${moduleType} по адресу ${contractAddress} в сети ${chainId}`); + + // Используем Hardhat verify вместо старого сервиса + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + + // Определяем имя сети для Hardhat + const networkName = getNetworkNameForHardhat(chainId); + if (!networkName) { + logger.warn(`⚠️ Неизвестная сеть ${chainId}, пропускаем верификацию модуля ${moduleType}`); + return { success: false, error: `Неизвестная сеть ${chainId}` }; + } + + logger.info(`🔧 Используем Hardhat verify для модуля ${moduleType} в сети ${networkName}`); + + // Создаем временный файл с аргументами конструктора + const fs = require('fs'); + const path = require('path'); + const tempArgsFile = path.join(__dirname, '..', '..', `temp-module-args-${moduleType}.js`); + + // Конвертируем аргументы в строки для JSON сериализации + const serializableArgs = constructorArgs.map(arg => { + if (typeof arg === 'bigint') { + return arg.toString(); + } + return arg; + }); + + const argsContent = `module.exports = ${JSON.stringify(serializableArgs, null, 2)};`; + fs.writeFileSync(tempArgsFile, argsContent); + + try { + // Выполняем Hardhat verify + const command = `npx hardhat verify --network ${networkName} --constructor-args ${tempArgsFile} ${contractAddress}`; + logger.info(`🔧 Выполняем команду: ${command}`); + + // Устанавливаем переменные окружения для Hardhat + const envVars = { + ...process.env, + ETHERSCAN_API_KEY: apiKey + }; + + const { stdout, stderr } = await execAsync(command, { + cwd: path.join(__dirname, '..', '..'), + env: envVars + }); + + if (stdout.includes('Successfully verified')) { + logger.info(`✅ Модуль ${moduleType} успешно верифицирован через Hardhat!`); + logger.info(`📄 Вывод: ${stdout}`); + return { success: true, message: 'Верификация успешна' }; + } else { + logger.error(`❌ Ошибка верификации модуля ${moduleType}: ${stderr || stdout}`); + return { success: false, error: stderr || stdout }; + } + } finally { + // Удаляем временный файл + if (fs.existsSync(tempArgsFile)) { + fs.unlinkSync(tempArgsFile); + } + } + + } catch (error) { + logger.error(`❌ Ошибка при верификации модуля ${moduleType}: ${error.message}`); + return { success: false, error: error.message }; + } +} + // Деплой модуля в одной сети с CREATE2 async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce, moduleInit, moduleType) { const { ethers } = hre; @@ -345,6 +449,49 @@ async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesTo const result = await deployModuleInNetwork(rpcUrl, pk, salt, null, targetNonce, moduleInit, moduleType); results[moduleType] = { ...result, success: true }; deploymentWebSocketService.addDeploymentLog(dleAddress, 'success', `Модуль ${moduleType} успешно задеплоен в сети ${net.name || net.chainId}: ${result.address}`); + + // Автоматическая верификация после успешного деплоя + if (result.address && params.etherscanApiKey && params.autoVerifyAfterDeploy) { + try { + logger.info(`🔍 Начинаем автоматическую верификацию модуля ${moduleType}...`); + deploymentWebSocketService.addDeploymentLog(dleAddress, 'info', `Начинаем верификацию модуля ${moduleType} в Etherscan...`); + + // Получаем аргументы конструктора для модуля + const moduleConfig = MODULE_CONFIGS[moduleType]; + const constructorArgs = moduleConfig.constructorArgs(dleAddress, Number(net.chainId), walletAddress); + + const verificationResult = await verifyModuleAfterDeploy( + Number(net.chainId), + result.address, + moduleType, + constructorArgs, + params.etherscanApiKey + ); + + if (verificationResult.success) { + results[moduleType].verification = 'verified'; + deploymentWebSocketService.addDeploymentLog(dleAddress, 'success', `Модуль ${moduleType} успешно верифицирован в Etherscan!`); + logger.info(`✅ Модуль ${moduleType} верифицирован: ${result.address}`); + } else { + results[moduleType].verification = 'failed'; + results[moduleType].verificationError = verificationResult.error || verificationResult.message; + deploymentWebSocketService.addDeploymentLog(dleAddress, 'warning', `Верификация модуля ${moduleType} не удалась: ${verificationResult.error || verificationResult.message}`); + logger.warn(`⚠️ Верификация модуля ${moduleType} не удалась: ${verificationResult.error || verificationResult.message}`); + } + } catch (verificationError) { + results[moduleType].verification = 'error'; + results[moduleType].verificationError = verificationError.message; + deploymentWebSocketService.addDeploymentLog(dleAddress, 'warning', `Ошибка при верификации модуля ${moduleType}: ${verificationError.message}`); + logger.error(`❌ Ошибка при верификации модуля ${moduleType}: ${verificationError.message}`); + } + } else { + results[moduleType].verification = 'skipped'; + if (!params.etherscanApiKey) { + logger.info(`ℹ️ API ключ Etherscan не предоставлен, пропускаем верификацию модуля ${moduleType}`); + } else if (!params.autoVerifyAfterDeploy) { + logger.info(`ℹ️ Автоматическая верификация отключена, пропускаем верификацию модуля ${moduleType}`); + } + } } catch (error) { logger.error(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleType} deployment failed:`, error.message); results[moduleType] = { @@ -616,40 +763,16 @@ async function main() { logger.info(`[MODULES_DBG] SUCCESS: All ${moduleType} addresses are identical:`, uniqueAddresses[0]); } - // Верификация во всех сетях через отдельный скрипт - logger.info(`[MODULES_DBG] Starting verification in all networks...`); - deploymentWebSocketService.addDeploymentLog(dleAddress, 'info', 'Начало верификации модулей во всех сетях...'); - - // Запускаем верификацию модулей через существующий скрипт - try { - const { verifyModules } = require('../verify-with-hardhat-v2'); - - logger.info(`[MODULES_DBG] Запускаем верификацию модулей...`); - deploymentWebSocketService.addDeploymentLog(dleAddress, 'info', 'Верификация контрактов в блокчейн-сканерах...'); - await verifyModules(); - logger.info(`[MODULES_DBG] Верификация модулей завершена`); - deploymentWebSocketService.addDeploymentLog(dleAddress, 'success', 'Верификация модулей завершена успешно'); - } catch (verifyError) { - logger.info(`[MODULES_DBG] Ошибка при верификации модулей: ${verifyError.message}`); - deploymentWebSocketService.addDeploymentLog(dleAddress, 'error', `Ошибка при верификации модулей: ${verifyError.message}`); - } - - // Создаем результаты верификации (все как успешные, так как верификация выполняется отдельно) - const verificationResults = deployResults.map(result => ({ - chainId: result.chainId, - modules: Object.keys(result.modules || {}).reduce((acc, moduleType) => { - acc[moduleType] = 'success'; - return acc; - }, {}) - })); + // Верификация модулей теперь интегрирована в основной процесс деплоя + // Автоматическая верификация происходит в функции deployAllModulesInNetwork + logger.info(`[MODULES_DBG] Верификация модулей интегрирована в процесс деплоя`); // Объединяем результаты const finalResults = deployResults.map((deployResult, index) => ({ ...deployResult, modules: deployResult.modules ? Object.keys(deployResult.modules).reduce((acc, moduleType) => { acc[moduleType] = { - ...deployResult.modules[moduleType], - verification: verificationResults[index]?.modules?.[moduleType] || 'unknown' + ...deployResult.modules[moduleType] }; return acc; }, {}) : {} diff --git a/backend/scripts/deploy/deploy-multichain.js b/backend/scripts/deploy/deploy-multichain.js index 3e50491..bcf5a65 100755 --- a/backend/scripts/deploy/deploy-multichain.js +++ b/backend/scripts/deploy/deploy-multichain.js @@ -43,22 +43,193 @@ console.log('[MULTI_DBG] 📦 Импортируем nonceManager...'); const { nonceManager } = require('../../utils/nonceManager'); console.log('[MULTI_DBG] ✅ nonceManager импортирован'); +// ContractVerificationService удален - используем Hardhat verify + console.log('[MULTI_DBG] 🎯 ВСЕ ИМПОРТЫ УСПЕШНЫ!'); console.log('[MULTI_DBG] 🔍 ПРОВЕРЯЕМ ФУНКЦИИ...'); console.log('[MULTI_DBG] deployInNetwork:', typeof deployInNetwork); console.log('[MULTI_DBG] main:', typeof main); -async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit, params) { - const { ethers } = hre; +// Функция для получения имени сети для Hardhat из deploy_params +function getNetworkNameForHardhat(chainId, params) { + // Создаем маппинг chainId -> Hardhat network name + const networkMapping = { + 11155111: 'sepolia', + 17000: 'holesky', + 421614: 'arbitrumSepolia', + 84532: 'baseSepolia', + 1: 'mainnet', + 42161: 'arbitrumOne', + 8453: 'base', + 137: 'polygon', + 56: 'bsc' + }; - // Используем новый менеджер RPC с retry логикой - const { provider, wallet, network } = await createRPCConnection(rpcUrl, pk, { - maxRetries: 3, - timeout: 30000 - }); + // Проверяем, поддерживается ли сеть в Hardhat + const hardhatNetworkName = networkMapping[chainId]; + if (!hardhatNetworkName) { + logger.warn(`⚠️ Сеть ${chainId} не поддерживается в Hardhat`); + return null; + } - const net = network; + // Проверяем, есть ли эта сеть в supported_chain_ids из deploy_params + const supportedChainIds = params.supported_chain_ids || params.supportedChainIds || []; + if (supportedChainIds.length > 0) { + // Преобразуем supportedChainIds в числа для сравнения + const supportedChainIdsNumbers = supportedChainIds.map(id => Number(id)); + if (!supportedChainIdsNumbers.includes(chainId)) { + logger.warn(`⚠️ Сеть ${chainId} не входит в список поддерживаемых сетей: ${supportedChainIdsNumbers.join(', ')}`); + return null; + } + logger.info(`✅ Сеть ${chainId} найдена в списке поддерживаемых сетей`); + } else { + logger.info(`ℹ️ Список поддерживаемых сетей пуст, разрешаем верификацию для ${chainId}`); + } + + logger.info(`✅ Сеть ${chainId} поддерживается: ${hardhatNetworkName}`); + logger.info(`🔍 Детали сети: chainId=${chainId}, hardhatName=${hardhatNetworkName}, supportedChains=[${supportedChainIds.join(', ')}]`); + return hardhatNetworkName; +} + +// Функция для автоматической верификации DLE контракта +async function verifyDLEAfterDeploy(chainId, contractAddress, constructorArgs, apiKey, params) { + try { + if (!apiKey) { + logger.warn(`⚠️ API ключ Etherscan не предоставлен, пропускаем верификацию DLE`); + return { success: false, error: 'API ключ не предоставлен' }; + } + + if (!params.autoVerifyAfterDeploy) { + logger.info(`ℹ️ Автоматическая верификация отключена, пропускаем верификацию DLE`); + return { success: false, error: 'Автоматическая верификация отключена' }; + } + + logger.info(`🔍 Начинаем верификацию DLE контракта по адресу ${contractAddress} в сети ${chainId}`); + + // Retry логика для верификации (до 3 попыток) + const maxVerifyAttempts = 3; + let verifyAttempts = 0; + + while (verifyAttempts < maxVerifyAttempts) { + verifyAttempts++; + logger.info(`🔄 Попытка верификации ${verifyAttempts}/${maxVerifyAttempts}`); + + try { + // Используем Hardhat verify вместо старого сервиса + const { exec } = require('child_process'); + const { promisify } = require('util'); + const execAsync = promisify(exec); + + // Определяем имя сети для Hardhat из deploy_params + const networkName = getNetworkNameForHardhat(chainId, params); + if (!networkName) { + logger.warn(`⚠️ Неизвестная сеть ${chainId}, пропускаем верификацию`); + return { success: false, error: `Неизвестная сеть ${chainId}` }; + } + + logger.info(`🔧 Используем Hardhat verify для сети ${networkName}`); + + // Создаем временный файл с аргументами конструктора + const fs = require('fs'); + const path = require('path'); + const tempArgsFile = path.join(__dirname, '..', '..', 'temp-constructor-args.js'); + + // Формируем аргументы в правильном формате для Hardhat + // constructorArgs - это hex строка, нам нужны исходные аргументы + // Получаем dleConfig и initializer из параметров + const { generateDeploymentArgs } = require('../../utils/constructorArgsGenerator'); + const { dleConfig, initializer } = generateDeploymentArgs(params); + + // Конвертируем BigInt значения в строки для JSON сериализации + const serializableDleConfig = { + name: dleConfig.name, + symbol: dleConfig.symbol, + location: dleConfig.location, + coordinates: dleConfig.coordinates, + jurisdiction: dleConfig.jurisdiction.toString(), + okvedCodes: dleConfig.okvedCodes, + kpp: dleConfig.kpp.toString(), + quorumPercentage: dleConfig.quorumPercentage.toString(), + initialPartners: dleConfig.initialPartners, + initialAmounts: dleConfig.initialAmounts.map(amount => amount.toString()), + supportedChainIds: dleConfig.supportedChainIds.map(id => id.toString()) + }; + + const argsContent = `module.exports = ${JSON.stringify([serializableDleConfig, initializer], null, 2)};`; + fs.writeFileSync(tempArgsFile, argsContent); + + try { + // Выполняем Hardhat verify + const command = `npx hardhat verify --network ${networkName} --constructor-args ${tempArgsFile} ${contractAddress}`; + logger.info(`🔧 Выполняем команду: ${command}`); + + // Устанавливаем переменные окружения для Hardhat + const envVars = { + ...process.env, + ETHERSCAN_API_KEY: apiKey, + SUPPORTED_CHAIN_IDS: JSON.stringify(params.supported_chain_ids || params.supportedChainIds || []), + RPC_URLS: JSON.stringify(params.rpc_urls || params.rpcUrls || {}) + }; + + const { stdout, stderr } = await execAsync(command, { + cwd: path.join(__dirname, '..', '..'), + env: envVars + }); + + if (stdout.includes('Successfully verified')) { + logger.info(`✅ DLE контракт успешно верифицирован через Hardhat!`); + logger.info(`📄 Вывод: ${stdout}`); + return { success: true, message: 'Верификация успешна' }; + } else { + // Проверяем, нужно ли повторить попытку + if (stderr.includes('does not have bytecode') && verifyAttempts < maxVerifyAttempts) { + logger.warn(`⚠️ Контракт еще не проиндексирован, ждем 5 секунд...`); + await new Promise(resolve => setTimeout(resolve, 5000)); + continue; + } + logger.error(`❌ Ошибка верификации: ${stderr || stdout}`); + return { success: false, error: stderr || stdout }; + } + } finally { + // Удаляем временный файл + if (fs.existsSync(tempArgsFile)) { + fs.unlinkSync(tempArgsFile); + } + } + + } catch (error) { + // Проверяем, нужно ли повторить попытку + if (error.message.includes('does not have bytecode') && verifyAttempts < maxVerifyAttempts) { + logger.warn(`⚠️ Контракт еще не проиндексирован, ждем 5 секунд...`); + await new Promise(resolve => setTimeout(resolve, 5000)); + continue; + } + logger.error(`❌ Ошибка при верификации DLE контракта: ${error.message}`); + return { success: false, error: error.message }; + } + } + + // Если все попытки исчерпаны + logger.error(`❌ Верификация не удалась после ${maxVerifyAttempts} попыток`); + return { success: false, error: 'Верификация не удалась после всех попыток' }; + } catch (error) { + logger.error(`❌ Критическая ошибка при верификации DLE контракта: ${error.message}`); + return { success: false, error: error.message }; + } +} + +async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit, params, dleConfig, initializer, etherscanKey) { + try { + const { ethers } = hre; + + // Используем новый менеджер RPC с retry логикой + const { provider, wallet, network } = await createRPCConnection(rpcUrl, pk, { + maxRetries: 3, + timeout: 30000 + }); + + const net = network; // DEBUG: базовая информация по сети try { @@ -250,7 +421,7 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit if (params.logoURI && params.logoURI !== '') { try { logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} checking logoURI for existing contract`); - const DLE = await hre.ethers.getContractFactory('DLE'); + const DLE = await hre.ethers.getContractFactory('contracts/DLE.sol:DLE'); const dleContract = DLE.attach(predictedAddress); const currentLogo = await dleContract.logoURI(); @@ -348,7 +519,7 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit if (params.logoURI && params.logoURI !== '') { try { logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing logoURI: ${params.logoURI}`); - const DLE = await hre.ethers.getContractFactory('DLE'); + const DLE = await hre.ethers.getContractFactory('contracts/DLE.sol:DLE'); const dleContract = DLE.attach(deployedAddress); const logoTx = await dleContract.connect(wallet).initializeLogoURI(params.logoURI, feeOverrides); @@ -362,7 +533,94 @@ async function deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, dleInit logger.info(`[MULTI_DBG] chainId=${Number(net.chainId)} no logoURI specified, skipping initialization`); } - return { address: deployedAddress, chainId: Number(net.chainId) }; + // Автоматическая верификация DLE контракта после успешного деплоя + let verificationResult = { success: false, error: 'skipped' }; + + if ((etherscanKey || params.etherscanApiKey) && params.autoVerifyAfterDeploy) { + try { + logger.info(`🔍 Начинаем автоматическую верификацию DLE контракта...`); + logger.info(`[VERIFY_DBG] dleConfig available: ${!!dleConfig}`); + logger.info(`[VERIFY_DBG] initializer: ${initializer}`); + + // Кодируем аргументы конструктора в hex + // Конструктор DLE: constructor(DLEConfig memory config, address _initializer) + const abiCoder = ethers.AbiCoder.defaultAbiCoder(); + + // Структура DLEConfig + const dleConfigType = 'tuple(string,string,string,string,uint256,string[],uint256,uint256,address[],uint256[],uint256[])'; + + // Подготавливаем DLEConfig tuple (все значения уже BigInt из constructorArgsGenerator) + const dleConfigTuple = [ + dleConfig.name, + dleConfig.symbol, + dleConfig.location, + dleConfig.coordinates, + dleConfig.jurisdiction, // уже BigInt + dleConfig.okvedCodes, // уже массив строк + dleConfig.kpp, // уже BigInt + dleConfig.quorumPercentage, // уже BigInt + dleConfig.initialPartners, + dleConfig.initialAmounts, // уже BigInt массив + dleConfig.supportedChainIds // уже BigInt массив + ]; + + // Кодируем конструктор: (DLEConfig, address) + const constructorArgsHex = abiCoder.encode( + [dleConfigType, 'address'], + [dleConfigTuple, initializer] + ).slice(2); // Убираем префикс 0x + + logger.info(`[VERIFY_DBG] Constructor args encoded: ${constructorArgsHex.slice(0, 100)}...`); + + // Ждем 5 секунд перед верификацией для индексации контракта + logger.info(`[VERIFY_DBG] Ожидаем 5 секунд для индексации контракта...`); + await new Promise(resolve => setTimeout(resolve, 5000)); + + logger.info(`[VERIFY_DBG] Calling verifyDLEAfterDeploy...`); + verificationResult = await verifyDLEAfterDeploy( + Number(network.chainId), + deployedAddress, + constructorArgsHex, + etherscanKey || params.etherscanApiKey, + params + ); + logger.info(`[VERIFY_DBG] verifyDLEAfterDeploy completed`); + + if (verificationResult.success) { + logger.info(`✅ DLE контракт верифицирован: ${deployedAddress}`); + } else { + logger.warn(`⚠️ Верификация DLE не удалась: ${verificationResult.error || verificationResult.message}`); + } + } catch (verificationError) { + const errorMsg = verificationError.message || String(verificationError); + const errorStack = verificationError.stack || 'No stack trace'; + logger.error(`❌ Ошибка при верификации DLE: ${errorMsg}`); + logger.error(`❌ Стек ошибки верификации: ${errorStack}`); + verificationResult = { success: false, error: errorMsg }; + } + } else { + if (!(etherscanKey || params.etherscanApiKey)) { + logger.info(`ℹ️ API ключ Etherscan не предоставлен, пропускаем верификацию DLE`); + } else if (!params.autoVerifyAfterDeploy) { + logger.info(`ℹ️ Автоматическая верификация отключена, пропускаем верификацию DLE`); + } + } + + const finalChainId = Number(network.chainId); + logger.info(`[MULTI_DBG] chainId=${finalChainId} Returning deployment result: address=${deployedAddress}`); + return { + address: deployedAddress, + chainId: finalChainId, + verification: verificationResult + }; + } catch (error) { + const errorMsg = error.message || String(error); + const errorStack = error.stack || 'No stack trace'; + const chainIdStr = network?.chainId ? Number(network.chainId) : 'unknown'; + logger.error(`[MULTI_DBG] chainId=${chainIdStr} deployment failed: ${errorMsg}`); + logger.error(`[MULTI_DBG] chainId=${chainIdStr} error stack: ${errorStack}`); + throw error; // Перебрасываем ошибку для обработки в main() + } } @@ -437,8 +695,15 @@ async function main() { }); const pk = params.private_key || process.env.PRIVATE_KEY; + + // ИСПРАВЛЕНИЕ: Используем RPC URLs из deployParams, а не из rpcProviderService const networks = params.rpcUrls || params.rpc_urls || []; + logger.info(`[MULTI_DBG] 📊 RPC URLs из deployParams: ${networks.length} сетей`); + networks.forEach((url, i) => { + logger.info(`[MULTI_DBG] ${i + 1}. ${url}`); + }); + // Устанавливаем API ключи Etherscan для верификации const ApiKeyManager = require('../../utils/apiKeyManager'); const etherscanKey = ApiKeyManager.getAndSetEtherscanApiKey(params); @@ -532,12 +797,21 @@ async function main() { throw new Error(`InitCode не найден для chainId: ${chainId}`); } - const r = await deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, networkInitCode, params); + const r = await deployInNetwork(rpcUrl, pk, initCodeHash, targetDLENonce, networkInitCode, params, dleConfig, initializer, etherscanKey); logger.info(`[MULTI_DBG] ✅ Network ${i + 1} (chainId: ${chainId}) deployment SUCCESS: ${r.address}`); - return { rpcUrl, chainId, address: r.address, chainId: r.chainId }; + return { + rpcUrl, + chainId, + address: r.address, + success: true, + verification: r.verification || { success: false, error: 'unknown' } + }; } catch (error) { - logger.error(`[MULTI_DBG] ❌ Network ${i + 1} deployment FAILED:`, error.message); - return { rpcUrl, chainId, error: error.message }; + const errorMsg = error.message || String(error); + const errorStack = error.stack || 'No stack trace'; + logger.error(`[MULTI_DBG] ❌ Network ${i + 1} (chainId: ${chainId}) deployment FAILED: ${errorMsg}`); + logger.error(`[MULTI_DBG] ❌ Network ${i + 1} (chainId: ${chainId}) error stack: ${errorStack}`); + return { rpcUrl, chainId, error: errorMsg, success: false }; } }); @@ -554,8 +828,12 @@ async function main() { } }); + // Логируем все результаты для отладки + logger.info('[MULTI_DBG] Raw results:', JSON.stringify(results, null, 2)); + // Проверяем, что все адреса одинаковые (критично для детерминированного деплоя) - const addresses = results.map(r => r.address).filter(addr => addr); + const successfulResults = results.filter(r => r.success === true); + const addresses = successfulResults.map(r => r.address).filter(addr => addr); const uniqueAddresses = [...new Set(addresses)]; logger.info('[MULTI_DBG] All addresses:', addresses); @@ -576,29 +854,19 @@ async function main() { logger.info('[MULTI_DBG] SUCCESS: All DLE addresses are identical:', uniqueAddresses[0]); - // ВЫВОДИМ РЕЗУЛЬТАТ СРАЗУ ПОСЛЕ ДЕПЛОЯ (ПЕРЕД ВЕРИФИКАЦИЕЙ)! - console.log('[MULTI_DBG] 🎯 ДОШЛИ ДО ВЫВОДА РЕЗУЛЬТАТА!'); - const finalResults = results.map((result, index) => ({ + // Верификация уже выполнена в процессе деплоя + const finalResults = results.map((result) => ({ ...result, - verification: 'pending' + verification: result.verification || { success: false, error: 'not_attempted' } })); + // ВЫВОДИМ РЕЗУЛЬТАТ С ИНТЕГРИРОВАННОЙ ВЕРИФИКАЦИЕЙ! + console.log('[MULTI_DBG] 🎯 ДОШЛИ ДО ВЫВОДА РЕЗУЛЬТАТА!'); console.log('[MULTI_DBG] 📊 finalResults:', JSON.stringify(finalResults, null, 2)); console.log('[MULTI_DBG] 🎯 ВЫВОДИМ MULTICHAIN_DEPLOY_RESULT!'); console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(finalResults)); console.log('[MULTI_DBG] ✅ MULTICHAIN_DEPLOY_RESULT ВЫВЕДЕН!'); - logger.info('[MULTI_DBG] DLE deployment completed successfully!'); - - // Верификация контрактов отключена - logger.info('[MULTI_DBG] Contract verification disabled - skipping verification step'); - - // Отмечаем все результаты как без верификации - const finalResultsWithVerification = results.map((result) => ({ - ...result, - verification: 'skipped' - })); - - logger.info('[MULTI_DBG] Verification skipped - deployment completed successfully'); + logger.info('[MULTI_DBG] DLE deployment completed successfully with integrated verification!'); } console.log('[MULTI_DBG] 🚀 ВЫЗЫВАЕМ MAIN()...'); diff --git a/backend/scripts/verify-with-hardhat-v2.js b/backend/scripts/verify-with-hardhat-v2.js deleted file mode 100644 index f2edf50..0000000 --- a/backend/scripts/verify-with-hardhat-v2.js +++ /dev/null @@ -1,600 +0,0 @@ -/** - * Верификация контрактов в Etherscan V2 - */ - -// const { execSync } = require('child_process'); // Удалено - больше не используем Hardhat verify -const DeployParamsService = require('../services/deployParamsService'); -const deploymentWebSocketService = require('../services/deploymentWebSocketService'); -const { getSecret } = require('../services/secretStore'); - -// Функция для определения Etherscan V2 API URL по chainId -function getEtherscanApiUrl(chainId) { - // Используем единый Etherscan V2 API для всех сетей - return `https://api.etherscan.io/v2/api?chainid=${chainId}`; -} - -// Импортируем вспомогательную функцию -const { createStandardJsonInput: createStandardJsonInputHelper } = require('../utils/standardJsonInputHelper'); - -// Функция для создания стандартного JSON input -function createStandardJsonInput() { - const path = require('path'); - const contractPath = path.join(__dirname, '../contracts/DLE.sol'); - return createStandardJsonInputHelper(contractPath, 'DLE'); -} - -// Функция для проверки статуса верификации -async function checkVerificationStatus(chainId, guid, apiKey) { - const apiUrl = getEtherscanApiUrl(chainId); - - const formData = new URLSearchParams({ - apikey: apiKey, - module: 'contract', - action: 'checkverifystatus', - guid: guid - }); - - try { - const response = await fetch(apiUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: formData - }); - - const result = await response.json(); - return result; - } catch (error) { - console.error('❌ Ошибка при проверке статуса:', error.message); - return { status: '0', message: error.message }; - } -} - -// Функция для проверки реального статуса контракта в Etherscan -async function checkContractVerificationStatus(chainId, contractAddress, apiKey) { - const apiUrl = getEtherscanApiUrl(chainId); - - const formData = new URLSearchParams({ - apikey: apiKey, - module: 'contract', - action: 'getsourcecode', - address: contractAddress - }); - - try { - const response = await fetch(apiUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: formData - }); - - const result = await response.json(); - - if (result.status === '1' && result.result && result.result[0]) { - const contractInfo = result.result[0]; - const isVerified = contractInfo.SourceCode && contractInfo.SourceCode !== ''; - - console.log(`🔍 Статус контракта ${contractAddress}:`, { - isVerified: isVerified, - contractName: contractInfo.ContractName || 'Unknown', - compilerVersion: contractInfo.CompilerVersion || 'Unknown' - }); - - return { isVerified, contractInfo }; - } else { - console.log('❌ Не удалось получить информацию о контракте:', result.message); - return { isVerified: false, error: result.message }; - } - } catch (error) { - console.error('❌ Ошибка при проверке статуса контракта:', error.message); - return { isVerified: false, error: error.message }; - } -} - -// Функция для верификации контракта в Etherscan V2 -async function verifyContractInEtherscan(chainId, contractAddress, constructorArgsHex, apiKey) { - const apiUrl = getEtherscanApiUrl(chainId); - const standardJsonInput = createStandardJsonInput(); - - console.log(`🔍 Верификация контракта ${contractAddress} в Etherscan V2 (chainId: ${chainId})`); - console.log(`📡 API URL: ${apiUrl}`); - - const formData = new URLSearchParams({ - apikey: apiKey, - module: 'contract', - action: 'verifysourcecode', - contractaddress: contractAddress, - codeformat: 'solidity-standard-json-input', - contractname: 'DLE.sol:DLE', - sourceCode: JSON.stringify(standardJsonInput), - compilerversion: 'v0.8.20+commit.a1b79de6', - optimizationUsed: '1', - runs: '0', - constructorArguements: constructorArgsHex - }); - - try { - const response = await fetch(apiUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: formData - }); - - const result = await response.json(); - console.log('📥 Ответ от Etherscan V2:', result); - - if (result.status === '1') { - console.log('✅ Верификация отправлена в Etherscan V2!'); - console.log(`📋 GUID: ${result.result}`); - - // Ждем и проверяем статус верификации с повторными попытками - console.log('⏳ Ждем 15 секунд перед проверкой статуса...'); - await new Promise(resolve => setTimeout(resolve, 15000)); - - // Проверяем статус с повторными попытками (до 3 раз) - let statusResult; - let attempts = 0; - const maxAttempts = 3; - - do { - attempts++; - console.log(`📊 Проверка статуса верификации (попытка ${attempts}/${maxAttempts})...`); - statusResult = await checkVerificationStatus(chainId, result.result, apiKey); - console.log('📊 Статус верификации:', statusResult); - - if (statusResult.status === '1') { - console.log('🎉 Верификация успешна!'); - return { success: true, guid: result.result, message: 'Верифицировано в Etherscan V2' }; - } else if (statusResult.status === '0' && statusResult.result.includes('Pending')) { - console.log('⏳ Верификация в очереди, проверяем реальный статус контракта...'); - - // Проверяем реальный статус контракта в Etherscan - const contractStatus = await checkContractVerificationStatus(chainId, contractAddress, apiKey); - if (contractStatus.isVerified) { - console.log('✅ Контракт уже верифицирован в Etherscan!'); - return { success: true, guid: result.result, message: 'Контракт верифицирован' }; - } else { - console.log('⏳ Контракт еще не верифицирован, ожидаем завершения...'); - if (attempts < maxAttempts) { - console.log(`⏳ Ждем еще 10 секунд перед следующей попыткой...`); - await new Promise(resolve => setTimeout(resolve, 10000)); - } - } - } else { - console.log('❌ Верификация не удалась:', statusResult.result); - return { success: false, error: statusResult.result }; - } - } while (attempts < maxAttempts && statusResult.status === '0' && statusResult.result.includes('Pending')); - - // Если все попытки исчерпаны - if (attempts >= maxAttempts) { - console.log('⏳ Максимальное количество попыток достигнуто, верификация может быть в процессе...'); - return { success: false, error: 'Ожидание верификации', guid: result.result }; - } - } else { - console.log('❌ Ошибка отправки верификации в Etherscan V2:', result.message); - - // Проверяем, не верифицирован ли уже контракт - if (result.message && result.message.includes('already verified')) { - console.log('✅ Контракт уже верифицирован'); - return { success: true, message: 'Контракт уже верифицирован' }; - } - - return { success: false, error: result.message }; - } - } catch (error) { - console.error('❌ Ошибка при отправке запроса в Etherscan V2:', error.message); - - // Проверяем, не является ли это ошибкой сети - if (error.message.includes('fetch') || error.message.includes('network')) { - console.log('⚠️ Ошибка сети, верификация может быть в процессе...'); - return { success: false, error: 'Network error - verification may be in progress' }; - } - - return { success: false, error: error.message }; - } -} - -async function verifyWithHardhatV2(params = null, deployedNetworks = null) { - console.log('🚀 Запуск верификации контрактов...'); - - try { - // Если параметры не переданы, получаем их из базы данных - if (!params) { - const DeployParamsService = require('../services/deployParamsService'); - const deployParamsService = new DeployParamsService(); - const latestParams = await deployParamsService.getLatestDeployParams(1); - - if (latestParams.length === 0) { - throw new Error('Нет параметров деплоя в базе данных'); - } - - params = latestParams[0]; - } - - // Проверяем API ключ в параметрах или переменной окружения - const etherscanApiKey = params.etherscan_api_key || process.env.ETHERSCAN_API_KEY; - if (!etherscanApiKey) { - throw new Error('Etherscan API ключ не найден в параметрах или переменной окружения'); - } - - // Устанавливаем API ключ в переменную окружения для использования в коде - process.env.ETHERSCAN_API_KEY = etherscanApiKey; - - console.log('📋 Параметры деплоя:', { - deploymentId: params.deployment_id, - name: params.name, - symbol: params.symbol - }); - - // Получаем адреса контрактов - let networks = []; - - if (deployedNetworks && Array.isArray(deployedNetworks)) { - // Используем переданные данные о сетях - networks = deployedNetworks; - console.log('📊 Используем переданные данные о развернутых сетях'); - } else if (params.deployedNetworks && Array.isArray(params.deployedNetworks)) { - networks = params.deployedNetworks; - } else if (params.dle_address && params.supportedChainIds) { - // Создаем deployedNetworks на основе dle_address и supportedChainIds - networks = params.supportedChainIds.map(chainId => ({ - chainId: chainId, - address: params.dle_address - })); - console.log('📊 Создали deployedNetworks на основе dle_address и supportedChainIds'); - } else { - throw new Error('Нет данных о развернутых сетях или адресе контракта'); - } - console.log(`🌐 Найдено ${networks.length} развернутых сетей`); - - // Получаем маппинг chainId на названия сетей из параметров деплоя - const networkMap = {}; - if (params.supportedChainIds && params.supportedChainIds.length > 0) { - // Создаем маппинг только для поддерживаемых сетей - for (const chainId of params.supportedChainIds) { - switch (chainId) { - case 1: networkMap[chainId] = 'mainnet'; break; - case 11155111: networkMap[chainId] = 'sepolia'; break; - case 17000: networkMap[chainId] = 'holesky'; break; - case 137: networkMap[chainId] = 'polygon'; break; - case 42161: networkMap[chainId] = 'arbitrumOne'; break; - case 421614: networkMap[chainId] = 'arbitrumSepolia'; break; - case 56: networkMap[chainId] = 'bsc'; break; - case 8453: networkMap[chainId] = 'base'; break; - case 84532: networkMap[chainId] = 'baseSepolia'; break; - default: networkMap[chainId] = `chain-${chainId}`; break; - } - } - } else { - // Fallback для совместимости - networkMap[11155111] = 'sepolia'; - networkMap[17000] = 'holesky'; - networkMap[421614] = 'arbitrumSepolia'; - networkMap[84532] = 'baseSepolia'; - } - - // Используем централизованный генератор параметров конструктора - const { generateVerificationArgs } = require('../utils/constructorArgsGenerator'); - const constructorArgs = generateVerificationArgs(params); - - console.log('📊 Аргументы конструктора подготовлены'); - - // Верифицируем контракт в каждой сети - const verificationResults = []; - - for (const network of networks) { - const { chainId, address } = network; - - if (!address || address === '0x0000000000000000000000000000000000000000') { - console.log(`⚠️ Пропускаем сеть ${chainId} - нет адреса контракта`); - verificationResults.push({ - success: false, - network: chainId, - error: 'No contract address' - }); - continue; - } - - const networkName = networkMap[chainId]; - if (!networkName) { - console.log(`⚠️ Неизвестная сеть ${chainId}, пропускаем верификацию`); - verificationResults.push({ - success: false, - network: chainId, - error: 'Unknown network' - }); - continue; - } - - console.log(`\n🔍 Верификация в сети ${networkName} (chainId: ${chainId})`); - console.log(`📍 Адрес: ${address}`); - - // Добавляем задержку между верификациями - if (verificationResults.length > 0) { - console.log('⏳ Ждем 5 секунд перед следующей верификацией...'); - await new Promise(resolve => setTimeout(resolve, 5000)); - } - - // Получаем API ключ Etherscan - const etherscanApiKey = process.env.ETHERSCAN_API_KEY; - if (!etherscanApiKey) { - console.log('❌ API ключ Etherscan не найден, пропускаем верификацию в Etherscan'); - verificationResults.push({ - success: false, - network: networkName, - chainId: chainId, - error: 'No Etherscan API key' - }); - continue; - } - - // Кодируем аргументы конструктора в hex - const { ethers } = require('ethers'); - const abiCoder = ethers.AbiCoder.defaultAbiCoder(); - - // Используем централизованный генератор параметров конструктора - const { generateDeploymentArgs } = require('../utils/constructorArgsGenerator'); - const { dleConfig, initializer } = generateDeploymentArgs(params); - - const encodedArgs = abiCoder.encode( - [ - 'tuple(string name, string symbol, string location, string coordinates, uint256 jurisdiction, string[] okvedCodes, uint256 kpp, uint256 quorumPercentage, address[] initialPartners, uint256[] initialAmounts, uint256[] supportedChainIds)', - 'address' - ], - [ - dleConfig, - initializer - ] - ); - - const constructorArgsHex = encodedArgs.slice(2); // Убираем 0x - - // Верификация в Etherscan - console.log('🌐 Верификация в Etherscan...'); - const etherscanResult = await verifyContractInEtherscan(chainId, address, constructorArgsHex, etherscanApiKey); - - if (etherscanResult.success) { - console.log('✅ Верификация в Etherscan успешна!'); - verificationResults.push({ - success: true, - network: networkName, - chainId: chainId, - etherscan: true, - message: etherscanResult.message - }); - } else { - console.log('❌ Ошибка верификации в Etherscan:', etherscanResult.error); - verificationResults.push({ - success: false, - network: networkName, - chainId: chainId, - error: etherscanResult.error - }); - } - } - - // Выводим итоговые результаты - console.log('\n📊 Итоговые результаты верификации:'); - const successful = verificationResults.filter(r => r.success).length; - const failed = verificationResults.filter(r => !r.success).length; - const etherscanVerified = verificationResults.filter(r => r.etherscan).length; - - console.log(`✅ Успешно верифицировано: ${successful}`); - console.log(`🌐 В Etherscan: ${etherscanVerified}`); - console.log(`❌ Ошибки: ${failed}`); - - verificationResults.forEach(result => { - const status = result.success ? '✅' : '❌'; - - const message = result.success - ? (result.message || 'OK') - : result.error?.substring(0, 100) + '...'; - - console.log(`${status} ${result.network} (${result.chainId}): ${message}`); - }); - - console.log('\n🎉 Верификация завершена!'); - - } catch (error) { - console.error('💥 Ошибка верификации:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -// Запускаем верификацию если скрипт вызван напрямую -if (require.main === module) { - // Проверяем аргументы командной строки - const args = process.argv.slice(2); - - if (args.includes('--modules')) { - // Верификация модулей - verifyModules() - .then(() => { - console.log('\n🏁 Верификация модулей завершена'); - process.exit(0); - }) - .catch((error) => { - console.error('💥 Верификация модулей завершилась с ошибкой:', error); - process.exit(1); - }); - } else { - // Верификация основного DLE контракта - verifyWithHardhatV2() - .then(() => { - console.log('\n🏁 Скрипт завершен'); - process.exit(0); - }) - .catch((error) => { - console.error('💥 Скрипт завершился с ошибкой:', error); - process.exit(1); - }); - } -} - -// Функция для верификации модулей -async function verifyModules() { - console.log('🚀 Запуск верификации модулей...'); - - try { - // Загружаем параметры из базы данных - const deployParamsService = new DeployParamsService(); - const paramsArray = await deployParamsService.getLatestDeployParams(1); - - if (paramsArray.length === 0) { - throw new Error('Нет параметров деплоя в базе данных'); - } - - const params = paramsArray[0]; - const dleAddress = params.dle_address; - - if (!dleAddress) { - throw new Error('Адрес DLE не найден в параметрах'); - } - - // Уведомляем WebSocket клиентов о начале верификации - deploymentWebSocketService.addDeploymentLog(dleAddress, 'info', 'Начало верификации модулей'); - - console.log('📋 Параметры верификации модулей:', { - dleAddress: dleAddress, - name: params.name, - symbol: params.symbol - }); - - // Читаем файлы модулей - const fs = require('fs'); - const path = require('path'); - const modulesDir = path.join(__dirname, 'contracts-data/modules'); - - if (!fs.existsSync(modulesDir)) { - console.log('📁 Папка модулей не найдена:', modulesDir); - return; - } - - const moduleFiles = fs.readdirSync(modulesDir).filter(file => file.endsWith('.json')); - console.log(`📁 Найдено ${moduleFiles.length} файлов модулей`); - - // Конфигурация модулей для верификации - const MODULE_CONFIGS = { - treasury: { - contractName: 'TreasuryModule', - constructorArgs: (dleAddress, chainId, walletAddress) => [ - dleAddress, - chainId, - walletAddress - ] - }, - timelock: { - contractName: 'TimelockModule', - constructorArgs: (dleAddress, chainId, walletAddress) => [ - dleAddress - ] - }, - reader: { - contractName: 'DLEReader', - constructorArgs: (dleAddress, chainId, walletAddress) => [ - dleAddress - ] - }, - hierarchicalVoting: { - contractName: 'HierarchicalVotingModule', - constructorArgs: (dleAddress, chainId, walletAddress) => [ - dleAddress - ] - } - }; - - // Получаем маппинг chainId на названия сетей из параметров деплоя - const networkMap = {}; - if (params.supportedChainIds && params.supportedChainIds.length > 0) { - // Создаем маппинг только для поддерживаемых сетей - for (const chainId of params.supportedChainIds) { - switch (chainId) { - case 11155111: networkMap[chainId] = 'sepolia'; break; - case 17000: networkMap[chainId] = 'holesky'; break; - case 421614: networkMap[chainId] = 'arbitrumSepolia'; break; - case 84532: networkMap[chainId] = 'baseSepolia'; break; - default: networkMap[chainId] = `chain-${chainId}`; break; - } - } - } else { - // Fallback для совместимости - networkMap[11155111] = 'sepolia'; - networkMap[17000] = 'holesky'; - networkMap[421614] = 'arbitrumSepolia'; - networkMap[84532] = 'baseSepolia'; - } - - // Верифицируем каждый модуль - for (const file of moduleFiles) { - const filePath = path.join(modulesDir, file); - const moduleData = JSON.parse(fs.readFileSync(filePath, 'utf8')); - - const moduleConfig = MODULE_CONFIGS[moduleData.moduleType]; - if (!moduleConfig) { - console.log(`⚠️ Неизвестный тип модуля: ${moduleData.moduleType}`); - continue; - } - - console.log(`🔍 Верификация модуля: ${moduleData.moduleType}`); - - // Верифицируем в каждой сети - for (const network of moduleData.networks) { - if (!network.success || !network.address) { - console.log(`⚠️ Пропускаем сеть ${network.chainId} - модуль не задеплоен`); - continue; - } - - const networkName = networkMap[network.chainId]; - if (!networkName) { - console.log(`⚠️ Неизвестная сеть: ${network.chainId}`); - continue; - } - - try { - console.log(`🔍 Верификация ${moduleData.moduleType} в сети ${networkName} (${network.chainId})`); - - // Подготавливаем аргументы конструктора - const constructorArgs = moduleConfig.constructorArgs( - dleAddress, - network.chainId, - params.initializer || "0x0000000000000000000000000000000000000000" - ); - - // Создаем временный файл с аргументами - const argsFile = path.join(__dirname, `temp-args-${Date.now()}.json`); - fs.writeFileSync(argsFile, JSON.stringify(constructorArgs, null, 2)); - - // Верификация модулей через Etherscan V2 API (пока не реализовано) - console.log(`⚠️ Верификация модулей через Etherscan V2 API пока не реализована для ${moduleData.moduleType} в ${networkName}`); - - // Удаляем временный файл - if (fs.existsSync(argsFile)) { - fs.unlinkSync(argsFile); - } - - } catch (error) { - console.error(`❌ Ошибка при верификации ${moduleData.moduleType} в сети ${network.chainId}:`, error.message); - } - } - } - - console.log('\n🏁 Верификация модулей завершена'); - - // Уведомляем WebSocket клиентов о завершении верификации - deploymentWebSocketService.addDeploymentLog(dleAddress, 'success', 'Верификация всех модулей завершена'); - deploymentWebSocketService.notifyModulesUpdated(dleAddress); - - } catch (error) { - console.error('❌ Ошибка при верификации модулей:', error.message); - throw error; - } -} - -module.exports = { verifyWithHardhatV2, verifyContracts: verifyWithHardhatV2, verifyModules }; diff --git a/backend/services/dleV2Service.js b/backend/services/dleV2Service.js index 78876d6..594072e 100644 --- a/backend/services/dleV2Service.js +++ b/backend/services/dleV2Service.js @@ -17,9 +17,9 @@ const { ethers } = require('ethers'); const logger = require('../utils/logger'); const { getRpcUrlByChainId } = require('./rpcProviderService'); const deploymentTracker = require('../utils/deploymentTracker'); -const etherscanV2 = require('./etherscanV2VerificationService'); +// ContractVerificationService удален - используем Hardhat verify const DeployParamsService = require('./deployParamsService'); -const verificationStore = require('./verificationStore'); +// verificationStore удален - используем Hardhat verify /** * Сервис для управления DLE v2 (Digital Legal Entity) diff --git a/backend/services/etherscanV2VerificationService.js b/backend/services/etherscanV2VerificationService.js deleted file mode 100644 index a68d1b8..0000000 --- a/backend/services/etherscanV2VerificationService.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * 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 axios = require('axios'); -const logger = require('../utils/logger'); - -const ETHERSCAN_V2_ENDPOINT = 'https://api.etherscan.io/v2/api'; - -class EtherscanV2VerificationService { - /** - * Отправить исходники контракта на верификацию (V2) - * Документация: https://docs.etherscan.io/etherscan-v2/contract-verification/multichain-verification - * @param {Object} opts - * @param {number} opts.chainId - * @param {string} opts.contractAddress - * @param {string} opts.contractName - формат "contracts/DLE.sol:DLE" - * @param {string} opts.compilerVersion - например, "v0.8.24+commit.e11b9ed9" - * @param {Object|string} opts.standardJsonInput - стандартный JSON input (рекомендуется) - * @param {string} [opts.constructorArgsHex] - * @param {string} [opts.apiKey] - * @returns {Promise} guid - */ - async submitVerification({ chainId, contractAddress, contractName, compilerVersion, standardJsonInput, constructorArgsHex, apiKey }) { - const key = apiKey || process.env.ETHERSCAN_API_KEY; - if (!key) throw new Error('ETHERSCAN_API_KEY не задан'); - if (!chainId) throw new Error('chainId обязателен'); - if (!contractAddress) throw new Error('contractAddress обязателен'); - if (!contractName) throw new Error('contractName обязателен'); - if (!compilerVersion) throw new Error('compilerVersion обязателен'); - if (!standardJsonInput) throw new Error('standardJsonInput обязателен'); - - const payload = new URLSearchParams(); - // Согласно V2, chainid должен передаваться в query, а не в теле формы - payload.set('module', 'contract'); - payload.set('action', 'verifysourcecode'); - payload.set('apikey', key); - payload.set('codeformat', 'solidity-standard-json-input'); - payload.set('sourceCode', typeof standardJsonInput === 'string' ? standardJsonInput : JSON.stringify(standardJsonInput)); - payload.set('contractaddress', contractAddress); - payload.set('contractname', contractName); - payload.set('compilerversion', compilerVersion); - if (constructorArgsHex) { - const no0x = constructorArgsHex.startsWith('0x') ? constructorArgsHex.slice(2) : constructorArgsHex; - payload.set('constructorArguments', no0x); - } - - const url = `${ETHERSCAN_V2_ENDPOINT}?chainid=${encodeURIComponent(String(chainId))}`; - const { data } = await axios.post(url, payload.toString(), { - headers: { 'Content-Type': 'application/x-www-form-urlencoded' } - }); - logger.info('[EtherscanV2] verifysourcecode response', data); - if (data && data.status === '1' && data.result) return data.result; // guid - throw new Error(data?.result || data?.message || 'Etherscan V2 verifysourcecode error'); - } - - /** - * Проверить статус верификации по guid - * @param {number} chainId - * @param {string} guid - * @param {string} [apiKey] - * @returns {Promise<{status:string,message:string,result:string}>} - */ - async checkStatus(chainId, guid, apiKey) { - const key = apiKey || process.env.ETHERSCAN_API_KEY; - if (!key) throw new Error('ETHERSCAN_API_KEY не задан'); - const params = new URLSearchParams(); - params.set('chainid', String(chainId)); - params.set('module', 'contract'); - params.set('action', 'checkverifystatus'); - params.set('guid', guid); - params.set('apikey', key); - const { data } = await axios.get(`${ETHERSCAN_V2_ENDPOINT}?${params.toString()}`); - logger.info('[EtherscanV2] checkverifystatus response', data); - return data; - } -} - -module.exports = new EtherscanV2VerificationService(); - - diff --git a/backend/services/unifiedDeploymentService.js b/backend/services/unifiedDeploymentService.js index 5f856d9..65abc28 100644 --- a/backend/services/unifiedDeploymentService.js +++ b/backend/services/unifiedDeploymentService.js @@ -10,7 +10,7 @@ const deploymentTracker = require('../utils/deploymentTracker'); const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); -const etherscanV2 = require('./etherscanV2VerificationService'); +// ContractVerificationService удален - используем Hardhat verify const { getRpcUrlByChainId } = require('./rpcProviderService'); const { ethers } = require('ethers'); // Убираем прямой импорт broadcastDeploymentUpdate - используем только deploymentTracker @@ -262,6 +262,23 @@ class UnifiedDeploymentService { // Извлекаем конкретную ошибку из вывода const errorMessage = stderr || stdout || 'Неизвестная ошибка'; + // Создаем объект ошибки для сохранения в БД + const errorResult = { + success: false, + error: `Деплой завершился с ошибкой (код ${code}): ${errorMessage}`, + stdout: stdout, + stderr: stderr + }; + + // Сохраняем ошибку в БД + this.deployParamsService.updateDeploymentStatus(deploymentId, 'failed', errorResult) + .then(() => { + logger.info(`✅ Результат ошибки сохранен в БД: ${deploymentId}`); + }) + .catch(dbError => { + logger.error(`❌ Ошибка сохранения результата ошибки в БД: ${dbError.message}`); + }); + // Отправляем WebSocket сообщение об ошибке через deploymentTracker deploymentTracker.failDeployment(deploymentId, new Error(`Деплой завершился с ошибкой (код ${code}): ${errorMessage}`)); diff --git a/backend/services/verificationStore.js b/backend/services/verificationStore.js deleted file mode 100644 index 286ed6e..0000000 --- a/backend/services/verificationStore.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2024-2025 Тарабанов Александр Викторович - * All rights reserved. - */ - -const path = require('path'); -const fs = require('fs'); - -const baseDir = path.join(__dirname, '../contracts-data/verifications'); - -function ensureDir() { - if (!fs.existsSync(baseDir)) fs.mkdirSync(baseDir, { recursive: true }); -} - -function getFilePath(address) { - ensureDir(); - const key = String(address || '').toLowerCase(); - return path.join(baseDir, `${key}.json`); -} - -function read(address) { - const fp = getFilePath(address); - if (!fs.existsSync(fp)) return { address: String(address).toLowerCase(), chains: {} }; - try { - return JSON.parse(fs.readFileSync(fp, 'utf8')); - } catch { - return { address: String(address).toLowerCase(), chains: {} }; - } -} - -function write(address, data) { - const fp = getFilePath(address); - fs.writeFileSync(fp, JSON.stringify(data, null, 2)); -} - -function updateChain(address, chainId, patch) { - const data = read(address); - if (!data.chains) data.chains = {}; - const cid = String(chainId); - data.chains[cid] = { ...(data.chains[cid] || {}), ...patch, chainId: Number(chainId), updatedAt: new Date().toISOString() }; - write(address, data); - return data; -} - -module.exports = { read, write, updateChain }; - - diff --git a/backend/utils/constructorArgsGenerator.js b/backend/utils/constructorArgsGenerator.js index 37a5b6d..ceed863 100644 --- a/backend/utils/constructorArgsGenerator.js +++ b/backend/utils/constructorArgsGenerator.js @@ -21,14 +21,21 @@ function generateDLEConstructorArgs(params, chainId = null) { symbol: params.symbol || '', location: params.location || '', coordinates: params.coordinates || '', - jurisdiction: params.jurisdiction || 0, - okvedCodes: params.okvedCodes || [], - kpp: params.kpp ? BigInt(params.kpp) : 0n, - quorumPercentage: params.quorumPercentage || 50, - initialPartners: params.initialPartners || [], + jurisdiction: params.jurisdiction ? BigInt(String(params.jurisdiction)) : 0n, + okvedCodes: (params.okvedCodes || params.okved_codes || []).map(code => { + // OKVED коды должны оставаться строками, так как в контракте DLE.sol + // поле okvedCodes определено как string[], а не uint256[] + if (typeof code === 'string') { + return code; + } + return code.toString(); + }), + kpp: params.kpp ? BigInt(String(params.kpp)) : 0n, + quorumPercentage: params.quorumPercentage ? BigInt(String(params.quorumPercentage)) : 50n, + initialPartners: params.initialPartners || params.initial_partners || [], // Умножаем initialAmounts на 1e18 для конвертации в wei - initialAmounts: (params.initialAmounts || []).map(amount => BigInt(amount) * BigInt(1e18)), - supportedChainIds: (params.supportedChainIds || []).map(id => BigInt(id)) + initialAmounts: (params.initialAmounts || params.initial_amounts || []).map(amount => BigInt(String(amount)) * BigInt(1e18)), + supportedChainIds: (params.supportedChainIds || params.supported_chain_ids || []).map(id => BigInt(String(id))) }; // Определяем initializer @@ -92,7 +99,9 @@ function validateConstructorArgs(params) { if (!params.location) errors.push('location не указан'); if (!params.coordinates) errors.push('coordinates не указаны'); if (!params.jurisdiction) errors.push('jurisdiction не указан'); - if (!params.okvedCodes || !Array.isArray(params.okvedCodes)) errors.push('okvedCodes не указан или не является массивом'); + if ((!params.okvedCodes || !Array.isArray(params.okvedCodes)) && (!params.okved_codes || !Array.isArray(params.okved_codes))) { + errors.push('okvedCodes не указан или не является массивом'); + } if (!params.initialPartners || !Array.isArray(params.initialPartners)) errors.push('initialPartners не указан или не является массивом'); if (!params.initialAmounts || !Array.isArray(params.initialAmounts)) errors.push('initialAmounts не указан или не является массивом'); if (!params.supportedChainIds || !Array.isArray(params.supportedChainIds)) errors.push('supportedChainIds не указан или не является массивом'); @@ -134,7 +143,8 @@ function logConstructorArgs(params, context = 'unknown') { console.log(` location: "${params.location}"`); console.log(` coordinates: "${params.coordinates}"`); console.log(` jurisdiction: ${params.jurisdiction}`); - console.log(` okvedCodes: [${params.okvedCodes.join(', ')}]`); + const okvedCodes = params.okvedCodes || params.okved_codes || []; + console.log(` okvedCodes: [${okvedCodes.join(', ')}]`); console.log(` kpp: ${params.kpp}`); console.log(` quorumPercentage: ${params.quorumPercentage}`); console.log(` initialPartners: [${params.initialPartners.join(', ')}]`); diff --git a/backend/utils/networkLoader.js b/backend/utils/networkLoader.js new file mode 100644 index 0000000..512d335 --- /dev/null +++ b/backend/utils/networkLoader.js @@ -0,0 +1,110 @@ +/** + * Утилита для загрузки поддерживаемых сетей из deploy_params + * Copyright (c) 2024-2025 Тарабанов Александр Викторович + */ + +const DeployParamsService = require('../services/deployParamsService'); + +class NetworkLoader { + constructor() { + this.cache = new Map(); + this.cacheTimeout = 5 * 60 * 1000; // 5 минут + this.fallbackChainIds = [11155111, 421614, 84532, 17000]; + } + + /** + * Общий метод для загрузки данных из deploy_params + * @param {string} field - Поле для извлечения + * @param {boolean} useCache - Использовать кеш + * @returns {Promise} - Данные из deploy_params + */ + async _loadFromDatabase(field, useCache = true) { + const cacheKey = field; + + // Проверяем кеш + if (useCache && this.cache.has(cacheKey)) { + const cached = this.cache.get(cacheKey); + if (Date.now() - cached.timestamp < this.cacheTimeout) { + console.log(`📋 Используем кешированные ${field}`); + return cached.data; + } + } + + try { + const deployParamsService = new DeployParamsService(); + const latestParams = await deployParamsService.getLatestDeployParams(1); + await deployParamsService.close(); + + if (latestParams.length > 0) { + const params = latestParams[0]; + let data; + + switch (field) { + case 'supportedChainIds': + data = params.supportedChainIds || params.supported_chain_ids || []; + break; + case 'rpcUrls': + data = params.rpcUrls || params.rpc_urls || {}; + break; + default: + data = params[field] || {}; + } + + console.log(`✅ Загружены ${field} из deploy_params:`, Array.isArray(data) ? data : Object.keys(data)); + + // Кешируем результат + this.cache.set(cacheKey, { + data: data, + timestamp: Date.now() + }); + + return data; + } + + // Если нет данных в deploy_params + console.log(`⚠️ Нет ${field} в deploy_params`); + return field === 'supportedChainIds' ? this.fallbackChainIds : {}; + } catch (error) { + console.error(`❌ Ошибка загрузки ${field}:`, error.message); + return field === 'supportedChainIds' ? this.fallbackChainIds : {}; + } + } + + /** + * Получить поддерживаемые сети из deploy_params + * @param {boolean} useCache - Использовать кеш + * @returns {Promise} - Массив chainId + */ + async getSupportedChainIds(useCache = true) { + return await this._loadFromDatabase('supportedChainIds', useCache); + } + + /** + * Получить RPC URLs из deploy_params + * @param {boolean} useCache - Использовать кеш + * @returns {Promise} - Объект с RPC URLs + */ + async getRpcUrls(useCache = true) { + return await this._loadFromDatabase('rpcUrls', useCache); + } + + /** + * Очистить кеш + */ + clearCache() { + this.cache.clear(); + console.log('🗑️ Кеш NetworkLoader очищен'); + } +} + +// Создаем singleton +const networkLoader = new NetworkLoader(); + +module.exports = { + NetworkLoader, + networkLoader, + // Экспортируем методы для удобства + getSupportedChainIds: () => networkLoader.getSupportedChainIds(), + getRpcUrls: () => networkLoader.getRpcUrls(), + clearCache: () => networkLoader.clearCache() +}; diff --git a/backend/utils/nonceManager.js b/backend/utils/nonceManager.js index 1e6248b..b0ef12e 100644 --- a/backend/utils/nonceManager.js +++ b/backend/utils/nonceManager.js @@ -4,6 +4,7 @@ */ const { ethers } = require('ethers'); +const { getRpcUrls } = require('./networkLoader'); class NonceManager { constructor() { @@ -230,31 +231,18 @@ class NonceManager { } try { - // Получаем RPC из deploy_params (как в deploy-multichain.js) - const DeployParamsService = require('../services/deployParamsService'); - const deployParamsService = new DeployParamsService(); + // Используем networkLoader для получения RPC URLs + const { getRpcUrls } = require('./networkLoader'); + const rpcUrlsFromLoader = await getRpcUrls(); - // Получаем последние параметры деплоя - const latestParams = await deployParamsService.getLatestDeployParams(1); - if (latestParams.length > 0) { - const params = latestParams[0]; - const supportedChainIds = params.supported_chain_ids || []; - const rpcUrlsFromParams = params.rpc_urls || []; - - // Находим RPC для нужного chainId - const chainIndex = supportedChainIds.indexOf(chainId); - if (chainIndex !== -1 && rpcUrlsFromParams[chainIndex]) { - const deployRpcUrl = rpcUrlsFromParams[chainIndex]; - if (!rpcUrls.includes(deployRpcUrl)) { - rpcUrls.push(deployRpcUrl); - console.log(`[NonceManager] ✅ RPC из deploy_params для chainId ${chainId}: ${deployRpcUrl}`); - } - } + // Получаем RPC для конкретного chainId + const chainRpcUrl = rpcUrlsFromLoader[chainId] || rpcUrlsFromLoader[chainId.toString()]; + if (chainRpcUrl && !rpcUrls.includes(chainRpcUrl)) { + rpcUrls.push(chainRpcUrl); + console.log(`[NonceManager] ✅ RPC из networkLoader для chainId ${chainId}: ${chainRpcUrl}`); } - - await deployParamsService.close(); } catch (error) { - console.warn(`[NonceManager] deploy_params недоступны для chainId ${chainId}, используем fallback: ${error.message}`); + console.warn(`[NonceManager] networkLoader недоступен для chainId ${chainId}, используем fallback: ${error.message}`); } // Всегда добавляем fallback RPC для надежности