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

This commit is contained in:
2025-09-25 03:02:31 +03:00
parent 792282cd75
commit 7b2f6937c8
34 changed files with 2900 additions and 2570 deletions

View File

@@ -1,31 +0,0 @@
{
"moduleType": "reader",
"dleAddress": "0x4e2A2B5FcA4edaBb537710D9682C40C3dc3e8dE2",
"networks": [
{
"chainId": 11155111,
"rpcUrl": "https://eth-sepolia.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52",
"address": null,
"verification": "unknown"
},
{
"chainId": 17000,
"rpcUrl": "https://ethereum-holesky.publicnode.com",
"address": null,
"verification": "unknown"
},
{
"chainId": 421614,
"rpcUrl": "https://sepolia-rollup.arbitrum.io/rpc",
"address": null,
"verification": "unknown"
},
{
"chainId": 84532,
"rpcUrl": "https://sepolia.base.org",
"address": null,
"verification": "unknown"
}
],
"deployTimestamp": "2025-09-22T23:19:13.695Z"
}

View File

@@ -1,31 +0,0 @@
{
"moduleType": "timelock",
"dleAddress": "0x4e2A2B5FcA4edaBb537710D9682C40C3dc3e8dE2",
"networks": [
{
"chainId": 11155111,
"rpcUrl": "https://eth-sepolia.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52",
"address": null,
"verification": "unknown"
},
{
"chainId": 17000,
"rpcUrl": "https://ethereum-holesky.publicnode.com",
"address": null,
"verification": "unknown"
},
{
"chainId": 421614,
"rpcUrl": "https://sepolia-rollup.arbitrum.io/rpc",
"address": null,
"verification": "unknown"
},
{
"chainId": 84532,
"rpcUrl": "https://sepolia.base.org",
"address": null,
"verification": "unknown"
}
],
"deployTimestamp": "2025-09-22T23:19:13.054Z"
}

View File

@@ -1,31 +0,0 @@
{
"moduleType": "treasury",
"dleAddress": "0x4e2A2B5FcA4edaBb537710D9682C40C3dc3e8dE2",
"networks": [
{
"chainId": 11155111,
"rpcUrl": "https://eth-sepolia.nodereal.io/v1/56dec8028bae4f26b76099a42dae2b52",
"address": null,
"verification": "unknown"
},
{
"chainId": 17000,
"rpcUrl": "https://ethereum-holesky.publicnode.com",
"address": null,
"verification": "unknown"
},
{
"chainId": 421614,
"rpcUrl": "https://sepolia-rollup.arbitrum.io/rpc",
"address": null,
"verification": "unknown"
},
{
"chainId": 84532,
"rpcUrl": "https://sepolia.base.org",
"address": null,
"verification": "unknown"
}
],
"deployTimestamp": "2025-09-22T23:19:11.085Z"
}

View File

@@ -0,0 +1,207 @@
/**
* Отладочный скрипт для мониторинга файлов в процессе деплоя
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
*/
const fs = require('fs');
const path = require('path');
console.log('🔍 ОТЛАДОЧНЫЙ МОНИТОР: Отслеживание файлов current-params.json');
console.log('=' .repeat(70));
class FileMonitor {
constructor() {
this.watchedFiles = new Map();
this.isMonitoring = false;
}
startMonitoring() {
console.log('🚀 Запуск мониторинга файлов...');
this.isMonitoring = true;
const deployDir = path.join(__dirname, './deploy');
const tempDir = path.join(__dirname, '../temp');
// Мониторим директории
this.watchDirectory(deployDir, 'deploy');
this.watchDirectory(tempDir, 'temp');
// Проверяем существующие файлы
this.checkExistingFiles();
console.log('✅ Мониторинг запущен. Нажмите Ctrl+C для остановки.');
}
watchDirectory(dirPath, label) {
if (!fs.existsSync(dirPath)) {
console.log(`📁 Директория ${label} не существует: ${dirPath}`);
return;
}
console.log(`📁 Мониторим директорию ${label}: ${dirPath}`);
try {
const watcher = fs.watch(dirPath, (eventType, filename) => {
if (filename && filename.includes('current-params')) {
const filePath = path.join(dirPath, filename);
const timestamp = new Date().toISOString();
console.log(`\n🔔 ${timestamp} - ${label.toUpperCase()}:`);
console.log(` Событие: ${eventType}`);
console.log(` Файл: ${filename}`);
console.log(` Путь: ${filePath}`);
if (eventType === 'rename' && filename) {
// Файл создан или удален
setTimeout(() => {
const exists = fs.existsSync(filePath);
console.log(` Статус: ${exists ? 'СУЩЕСТВУЕТ' : 'УДАЛЕН'}`);
if (exists) {
try {
const stats = fs.statSync(filePath);
console.log(` Размер: ${stats.size} байт`);
console.log(` Изменен: ${stats.mtime}`);
} catch (statError) {
console.log(` Ошибка получения статистики: ${statError.message}`);
}
}
}, 100);
}
}
});
this.watchedFiles.set(dirPath, watcher);
console.log(`✅ Мониторинг ${label} запущен`);
} catch (watchError) {
console.log(`❌ Ошибка мониторинга ${label}: ${watchError.message}`);
}
}
checkExistingFiles() {
console.log('\n🔍 Проверка существующих файлов...');
const pathsToCheck = [
path.join(__dirname, './deploy/current-params.json'),
path.join(__dirname, '../temp'),
path.join(__dirname, './deploy')
];
pathsToCheck.forEach(checkPath => {
try {
if (fs.existsSync(checkPath)) {
const stats = fs.statSync(checkPath);
if (stats.isFile()) {
console.log(`📄 Файл найден: ${checkPath}`);
console.log(` Размер: ${stats.size} байт`);
console.log(` Создан: ${stats.birthtime}`);
console.log(` Изменен: ${stats.mtime}`);
} else if (stats.isDirectory()) {
console.log(`📁 Директория найдена: ${checkPath}`);
const files = fs.readdirSync(checkPath);
const currentParamsFiles = files.filter(f => f.includes('current-params'));
if (currentParamsFiles.length > 0) {
console.log(` Файлы current-params: ${currentParamsFiles.join(', ')}`);
} else {
console.log(` Файлы current-params: не найдены`);
}
}
} else {
console.log(`Не найден: ${checkPath}`);
}
} catch (error) {
console.log(`⚠️ Ошибка проверки ${checkPath}: ${error.message}`);
}
});
}
stopMonitoring() {
console.log('\n🛑 Остановка мониторинга...');
this.isMonitoring = false;
this.watchedFiles.forEach((watcher, path) => {
try {
watcher.close();
console.log(`✅ Мониторинг остановлен: ${path}`);
} catch (error) {
console.log(`❌ Ошибка остановки мониторинга ${path}: ${error.message}`);
}
});
this.watchedFiles.clear();
console.log('✅ Мониторинг полностью остановлен');
}
// Метод для периодической проверки
startPeriodicCheck(intervalMs = 5000) {
console.log(`⏰ Запуск периодической проверки (каждые ${intervalMs}ms)...`);
const checkInterval = setInterval(() => {
if (!this.isMonitoring) {
clearInterval(checkInterval);
return;
}
this.performPeriodicCheck();
}, intervalMs);
return checkInterval;
}
performPeriodicCheck() {
const timestamp = new Date().toISOString();
console.log(`\n${timestamp} - Периодическая проверка:`);
const filesToCheck = [
path.join(__dirname, './deploy/current-params.json'),
path.join(__dirname, './deploy'),
path.join(__dirname, '../temp')
];
filesToCheck.forEach(filePath => {
try {
if (fs.existsSync(filePath)) {
const stats = fs.statSync(filePath);
if (stats.isFile()) {
console.log(` 📄 ${path.basename(filePath)}: ${stats.size} байт`);
} else if (stats.isDirectory()) {
const files = fs.readdirSync(filePath);
const currentParamsFiles = files.filter(f => f.includes('current-params'));
console.log(` 📁 ${path.basename(filePath)}: ${files.length} файлов, current-params: ${currentParamsFiles.length}`);
}
} else {
console.log(`${path.basename(filePath)}: не существует`);
}
} catch (error) {
console.log(` ⚠️ ${path.basename(filePath)}: ошибка ${error.message}`);
}
});
}
}
// Создаем экземпляр монитора
const monitor = new FileMonitor();
// Обработка сигналов завершения
process.on('SIGINT', () => {
console.log('\n🛑 Получен сигнал SIGINT...');
monitor.stopMonitoring();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\n🛑 Получен сигнал SIGTERM...');
monitor.stopMonitoring();
process.exit(0);
});
// Запускаем мониторинг
monitor.startMonitoring();
monitor.startPeriodicCheck(3000); // Проверка каждые 3 секунды
console.log('\n💡 Инструкции:');
console.log(' - Запустите этот скрипт в отдельном терминале');
console.log(' - Затем запустите деплой DLE в другом терминале');
console.log(' - Наблюдайте за изменениями файлов в реальном времени');
console.log(' - Нажмите Ctrl+C для остановки мониторинга');

View File

@@ -0,0 +1,617 @@
/**
* 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
*/
/* eslint-disable no-console */
const hre = require('hardhat');
const path = require('path');
const fs = require('fs');
// Подбираем безопасные gas/fee для разных сетей (включая L2)
async function getFeeOverrides(provider, { minPriorityGwei = 1n, minFeeGwei = 20n } = {}) {
const fee = await provider.getFeeData();
const overrides = {};
const minPriority = (await (async () => hre.ethers.parseUnits(minPriorityGwei.toString(), 'gwei'))());
const minFee = (await (async () => hre.ethers.parseUnits(minFeeGwei.toString(), 'gwei'))());
if (fee.maxFeePerGas) {
overrides.maxFeePerGas = fee.maxFeePerGas < minFee ? minFee : fee.maxFeePerGas;
overrides.maxPriorityFeePerGas = (fee.maxPriorityFeePerGas && fee.maxPriorityFeePerGas > 0n)
? fee.maxPriorityFeePerGas
: minPriority;
} else if (fee.gasPrice) {
overrides.gasPrice = fee.gasPrice < minFee ? minFee : fee.gasPrice;
}
return overrides;
}
// Конфигурация модулей для деплоя
const MODULE_CONFIGS = {
treasury: {
contractName: 'TreasuryModule',
constructorArgs: (dleAddress, chainId, walletAddress) => [
dleAddress, // _dleContract
chainId, // _chainId
walletAddress // _emergencyAdmin
],
verificationArgs: (dleAddress, chainId, walletAddress) => [
dleAddress, // _dleContract
chainId, // _chainId
walletAddress // _emergencyAdmin
]
},
timelock: {
contractName: 'TimelockModule',
constructorArgs: (dleAddress) => [
dleAddress // _dleContract
],
verificationArgs: (dleAddress) => [
dleAddress // _dleContract
]
},
reader: {
contractName: 'DLEReader',
constructorArgs: (dleAddress) => [
dleAddress // _dleContract
],
verificationArgs: (dleAddress) => [
dleAddress // _dleContract
]
}
// Здесь можно легко добавлять новые модули:
// newModule: {
// contractName: 'NewModule',
// constructorArgs: (dleAddress, ...otherArgs) => [dleAddress, ...otherArgs],
// verificationArgs: (dleAddress, ...otherArgs) => [dleAddress, ...otherArgs]
// }
};
// Деплой модуля в одной сети с CREATE2
async function deployModuleInNetwork(rpcUrl, pk, salt, initCodeHash, targetNonce, moduleInit, moduleType) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
const net = await provider.getNetwork();
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} deploying ${moduleType}...`);
// 1) Выравнивание nonce до targetNonce нулевыми транзакциями (если нужно)
let current = await provider.getTransactionCount(wallet.address, 'pending');
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} current nonce=${current} target=${targetNonce}`);
if (current > targetNonce) {
throw new Error(`Current nonce ${current} > targetNonce ${targetNonce} on chainId=${Number(net.chainId)}`);
}
if (current < targetNonce) {
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} aligning nonce from ${current} to ${targetNonce} (${targetNonce - current} transactions needed)`);
// Используем burn address для более надежных транзакций
const burnAddress = "0x000000000000000000000000000000000000dEaD";
while (current < targetNonce) {
const overrides = await getFeeOverrides(provider);
let gasLimit = 21000; // минимальный gas для обычной транзакции
let sent = false;
let lastErr = null;
for (let attempt = 0; attempt < 3 && !sent; attempt++) {
try {
const txReq = {
to: burnAddress,
value: 0n,
nonce: current,
gasLimit,
...overrides
};
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} sending filler tx nonce=${current} attempt=${attempt + 1}`);
const txFill = await wallet.sendTransaction(txReq);
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx sent, hash=${txFill.hash}, waiting for confirmation...`);
await txFill.wait();
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx nonce=${current} confirmed, hash=${txFill.hash}`);
sent = true;
} catch (e) {
lastErr = e;
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} filler tx nonce=${current} attempt=${attempt + 1} failed: ${e?.message || e}`);
if (String(e?.message || '').toLowerCase().includes('intrinsic gas too low') && attempt < 2) {
gasLimit = 50000;
continue;
}
if (String(e?.message || '').toLowerCase().includes('nonce too low') && attempt < 2) {
current = await provider.getTransactionCount(wallet.address, 'pending');
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} updated nonce to ${current}`);
continue;
}
throw e;
}
}
if (!sent) {
console.error(`[MODULES_DBG] chainId=${Number(net.chainId)} failed to send filler tx for nonce=${current}`);
throw lastErr || new Error('filler tx failed');
}
current++;
}
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} nonce alignment completed, current nonce=${current}`);
} else {
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} nonce already aligned at ${current}`);
}
// 2) Деплой модуля напрямую на согласованном nonce
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} deploying ${moduleType} directly with nonce=${targetNonce}`);
const feeOverrides = await getFeeOverrides(provider);
let gasLimit;
try {
// Оцениваем газ для деплоя модуля
const est = await wallet.estimateGas({ data: moduleInit, ...feeOverrides }).catch(() => null);
// Рассчитываем доступный gasLimit из баланса
const balance = await provider.getBalance(wallet.address, 'latest');
const effPrice = feeOverrides.maxFeePerGas || feeOverrides.gasPrice || 0n;
const reserve = hre.ethers.parseEther('0.005');
const maxByBalance = effPrice > 0n && balance > reserve ? (balance - reserve) / effPrice : 1_000_000n;
const fallbackGas = maxByBalance > 2_000_000n ? 2_000_000n : (maxByBalance < 500_000n ? 500_000n : maxByBalance);
gasLimit = est ? (est + est / 5n) : fallbackGas;
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} estGas=${est?.toString?.()||'null'} effGasPrice=${effPrice?.toString?.()||'0'} maxByBalance=${maxByBalance.toString()} chosenGasLimit=${gasLimit.toString()}`);
} catch (_) {
gasLimit = 1_000_000n;
}
// Вычисляем предсказанный адрес модуля
const predictedAddress = ethers.getCreateAddress({
from: wallet.address,
nonce: targetNonce
});
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} predicted ${moduleType} address=${predictedAddress}`);
// Проверяем, не развернут ли уже контракт
const existingCode = await provider.getCode(predictedAddress);
if (existingCode && existingCode !== '0x') {
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleType} already exists at predictedAddress, skip deploy`);
return { address: predictedAddress, chainId: Number(net.chainId) };
}
// Деплоим модуль
let tx;
try {
tx = await wallet.sendTransaction({
data: moduleInit,
nonce: targetNonce,
gasLimit,
...feeOverrides
});
} catch (e) {
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} deploy error(first): ${e?.message || e}`);
// Повторная попытка с обновленным nonce
const updatedNonce = await provider.getTransactionCount(wallet.address, 'pending');
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} retry deploy with nonce=${updatedNonce}`);
tx = await wallet.sendTransaction({
data: moduleInit,
nonce: updatedNonce,
gasLimit,
...feeOverrides
});
}
const rc = await tx.wait();
const deployedAddress = rc.contractAddress || predictedAddress;
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleType} deployed at=${deployedAddress}`);
return { address: deployedAddress, chainId: Number(net.chainId) };
}
// Верификация модуля в одной сети
async function verifyModuleInNetwork(rpcUrl, pk, dleAddress, moduleType, moduleConfig, moduleAddress) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
const net = await provider.getNetwork();
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} verifying ${moduleConfig.contractName}...`);
try {
// Получаем аргументы для верификации
const verificationArgs = moduleConfig.verificationArgs(dleAddress, Number(net.chainId), wallet.address);
await hre.run("verify:verify", {
address: moduleAddress,
constructorArguments: verificationArgs,
});
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleConfig.contractName} verification successful`);
return 'success';
} catch (error) {
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleConfig.contractName} verification failed: ${error.message}`);
return 'failed';
}
}
// Деплой всех модулей в одной сети
async function deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
const net = await provider.getNetwork();
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} deploying modules: ${modulesToDeploy.join(', ')}`);
const results = {};
for (let i = 0; i < modulesToDeploy.length; i++) {
const moduleType = modulesToDeploy[i];
const moduleInit = moduleInits[moduleType];
const targetNonce = targetNonces[moduleType];
if (!MODULE_CONFIGS[moduleType]) {
console.error(`[MODULES_DBG] chainId=${Number(net.chainId)} Unknown module type: ${moduleType}`);
results[moduleType] = { success: false, error: `Unknown module type: ${moduleType}` };
continue;
}
if (!moduleInit) {
console.error(`[MODULES_DBG] chainId=${Number(net.chainId)} No init code for module: ${moduleType}`);
results[moduleType] = { success: false, error: `No init code for module: ${moduleType}` };
continue;
}
try {
const result = await deployModuleInNetwork(rpcUrl, pk, salt, null, targetNonce, moduleInit, moduleType);
results[moduleType] = { ...result, success: true };
} catch (error) {
console.error(`[MODULES_DBG] chainId=${Number(net.chainId)} ${moduleType} deployment failed:`, error.message);
results[moduleType] = {
chainId: Number(net.chainId),
success: false,
error: error.message
};
}
}
return {
chainId: Number(net.chainId),
modules: results
};
}
// Верификация всех модулей в одной сети
async function verifyAllModulesInNetwork(rpcUrl, pk, dleAddress, moduleResults, modulesToVerify) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
const net = await provider.getNetwork();
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} verifying modules: ${modulesToVerify.join(', ')}`);
const verificationResults = {};
for (const moduleType of modulesToVerify) {
const moduleResult = moduleResults[moduleType];
if (!moduleResult || !moduleResult.success || !moduleResult.address) {
console.log(`[MODULES_DBG] chainId=${Number(net.chainId)} skipping verification for ${moduleType} - deployment failed`);
verificationResults[moduleType] = 'skipped';
continue;
}
if (!MODULE_CONFIGS[moduleType]) {
console.error(`[MODULES_DBG] chainId=${Number(net.chainId)} Unknown module type for verification: ${moduleType}`);
verificationResults[moduleType] = 'unknown_module';
continue;
}
const moduleConfig = MODULE_CONFIGS[moduleType];
const verification = await verifyModuleInNetwork(rpcUrl, pk, dleAddress, moduleType, moduleConfig, moduleResult.address);
verificationResults[moduleType] = verification;
}
return {
chainId: Number(net.chainId),
modules: verificationResults
};
}
// Деплой всех модулей во всех сетях
async function deployAllModulesInAllNetworks(networks, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces) {
const results = [];
for (let i = 0; i < networks.length; i++) {
const rpcUrl = networks[i];
console.log(`[MODULES_DBG] deploying modules to network ${i + 1}/${networks.length}: ${rpcUrl}`);
const result = await deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces);
results.push(result);
}
return results;
}
// Верификация всех модулей во всех сетях
async function verifyAllModulesInAllNetworks(networks, pk, dleAddress, deployResults, modulesToVerify) {
const verificationResults = [];
for (let i = 0; i < networks.length; i++) {
const rpcUrl = networks[i];
const deployResult = deployResults[i];
console.log(`[MODULES_DBG] verifying modules in network ${i + 1}/${networks.length}: ${rpcUrl}`);
const verification = await verifyAllModulesInNetwork(rpcUrl, pk, dleAddress, deployResult.modules, modulesToVerify);
verificationResults.push(verification);
}
return verificationResults;
}
async function main() {
const { ethers } = hre;
// Загружаем параметры из базы данных или файла
let params;
try {
// Пытаемся загрузить из базы данных
const DeployParamsService = require('../../services/deployParamsService');
const deployParamsService = new DeployParamsService();
// Получаем последние параметры деплоя
const latestParams = await deployParamsService.getLatestDeployParams(1);
if (latestParams.length > 0) {
params = latestParams[0];
console.log('✅ Параметры загружены из базы данных');
} else {
throw new Error('Параметры деплоя не найдены в базе данных');
}
await deployParamsService.close();
} catch (dbError) {
console.log('⚠️ Не удалось загрузить параметры из БД, пытаемся загрузить из файла:', dbError.message);
// Fallback к файлу
const paramsPath = path.join(__dirname, './current-params.json');
if (!fs.existsSync(paramsPath)) {
throw new Error('Файл параметров не найден: ' + paramsPath);
}
params = JSON.parse(fs.readFileSync(paramsPath, 'utf8'));
console.log('✅ Параметры загружены из файла');
}
console.log('[MODULES_DBG] Загружены параметры:', {
name: params.name,
symbol: params.symbol,
supportedChainIds: params.supportedChainIds,
CREATE2_SALT: params.CREATE2_SALT
});
const pk = process.env.PRIVATE_KEY;
const networks = params.rpcUrls || params.rpc_urls || [];
const dleAddress = params.dleAddress;
const salt = params.CREATE2_SALT || params.create2_salt;
// Модули для деплоя (можно настроить через параметры)
const modulesToDeploy = params.modulesToDeploy || ['treasury', 'timelock', 'reader'];
if (!pk) throw new Error('Env: PRIVATE_KEY');
if (!dleAddress) throw new Error('DLE_ADDRESS not found in params');
if (!salt) throw new Error('CREATE2_SALT not found in params');
if (networks.length === 0) throw new Error('RPC URLs not found in params');
console.log(`[MODULES_DBG] Starting modules deployment to ${networks.length} networks`);
console.log(`[MODULES_DBG] DLE Address: ${dleAddress}`);
console.log(`[MODULES_DBG] Modules to deploy: ${modulesToDeploy.join(', ')}`);
console.log(`[MODULES_DBG] Networks:`, networks);
// Проверяем, что все модули поддерживаются
const unsupportedModules = modulesToDeploy.filter(module => !MODULE_CONFIGS[module]);
if (unsupportedModules.length > 0) {
throw new Error(`Unsupported modules: ${unsupportedModules.join(', ')}. Available modules: ${Object.keys(MODULE_CONFIGS).join(', ')}`);
}
// Подготовим init код для каждого модуля
const moduleInits = {};
const moduleInitCodeHashes = {};
for (const moduleType of modulesToDeploy) {
const moduleConfig = MODULE_CONFIGS[moduleType];
const ContractFactory = await hre.ethers.getContractFactory(moduleConfig.contractName);
// Получаем аргументы конструктора для первой сети (для расчета init кода)
const firstProvider = new hre.ethers.JsonRpcProvider(networks[0]);
const firstWallet = new hre.ethers.Wallet(pk, firstProvider);
const firstNetwork = await firstProvider.getNetwork();
const constructorArgs = moduleConfig.constructorArgs(dleAddress, Number(firstNetwork.chainId), firstWallet.address);
const deployTx = await ContractFactory.getDeployTransaction(...constructorArgs);
moduleInits[moduleType] = deployTx.data;
moduleInitCodeHashes[moduleType] = ethers.keccak256(deployTx.data);
console.log(`[MODULES_DBG] ${moduleType} init code prepared, hash: ${moduleInitCodeHashes[moduleType]}`);
}
// Подготовим провайдеры и вычислим общие nonce для каждого модуля
const providers = networks.map(u => new hre.ethers.JsonRpcProvider(u));
const wallets = providers.map(p => new hre.ethers.Wallet(pk, p));
const nonces = [];
for (let i = 0; i < providers.length; i++) {
const n = await providers[i].getTransactionCount(wallets[i].address, 'pending');
nonces.push(n);
}
// Вычисляем target nonce для каждого модуля
const targetNonces = {};
let currentMaxNonce = Math.max(...nonces);
for (const moduleType of modulesToDeploy) {
targetNonces[moduleType] = currentMaxNonce;
currentMaxNonce++; // каждый следующий модуль получает nonce +1
}
console.log(`[MODULES_DBG] nonces=${JSON.stringify(nonces)} targetNonces=${JSON.stringify(targetNonces)}`);
// ПАРАЛЛЕЛЬНЫЙ деплой всех модулей во всех сетях одновременно
console.log(`[MODULES_DBG] Starting PARALLEL deployment of all modules to ${networks.length} networks`);
const deploymentPromises = networks.map(async (rpcUrl, networkIndex) => {
console.log(`[MODULES_DBG] 🚀 Starting deployment to network ${networkIndex + 1}/${networks.length}: ${rpcUrl}`);
try {
// Получаем chainId динамически из сети
const provider = new hre.ethers.JsonRpcProvider(rpcUrl);
const network = await provider.getNetwork();
const chainId = Number(network.chainId);
console.log(`[MODULES_DBG] 📡 Network ${networkIndex + 1} chainId: ${chainId}`);
const result = await deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces);
console.log(`[MODULES_DBG] ✅ Network ${networkIndex + 1} (chainId: ${chainId}) deployment SUCCESS`);
return { rpcUrl, chainId, ...result };
} catch (error) {
console.error(`[MODULES_DBG] ❌ Network ${networkIndex + 1} deployment FAILED:`, error.message);
return { rpcUrl, error: error.message };
}
});
// Ждем завершения всех деплоев
const deployResults = await Promise.all(deploymentPromises);
console.log(`[MODULES_DBG] All ${networks.length} deployments completed`);
// Логируем результаты деплоя для каждой сети
deployResults.forEach((result, index) => {
if (result.modules) {
console.log(`[MODULES_DBG] ✅ Network ${index + 1} (chainId: ${result.chainId}) SUCCESS`);
Object.entries(result.modules).forEach(([moduleType, moduleResult]) => {
if (moduleResult.success) {
console.log(`[MODULES_DBG] ✅ ${moduleType}: ${moduleResult.address}`);
} else {
console.log(`[MODULES_DBG] ❌ ${moduleType}: ${moduleResult.error}`);
}
});
} else {
console.log(`[MODULES_DBG] ❌ Network ${index + 1} (chainId: ${result.chainId}) FAILED: ${result.error}`);
}
});
// Проверяем, что все адреса модулей одинаковые в каждой сети
for (const moduleType of modulesToDeploy) {
const addresses = deployResults
.filter(r => r.modules && r.modules[moduleType] && r.modules[moduleType].success)
.map(r => r.modules[moduleType].address);
const uniqueAddresses = [...new Set(addresses)];
console.log(`[MODULES_DBG] ${moduleType} addresses:`, addresses);
console.log(`[MODULES_DBG] ${moduleType} unique addresses:`, uniqueAddresses);
if (uniqueAddresses.length > 1) {
console.error(`[MODULES_DBG] ERROR: ${moduleType} addresses are different across networks!`);
console.error(`[MODULES_DBG] addresses:`, uniqueAddresses);
throw new Error(`Nonce alignment failed for ${moduleType} - addresses are different`);
}
if (uniqueAddresses.length === 0) {
console.error(`[MODULES_DBG] ERROR: No successful ${moduleType} deployments!`);
throw new Error(`No successful ${moduleType} deployments`);
}
console.log(`[MODULES_DBG] SUCCESS: All ${moduleType} addresses are identical:`, uniqueAddresses[0]);
}
// Верификация во всех сетях
console.log(`[MODULES_DBG] Starting verification in all networks...`);
const verificationResults = await verifyAllModulesInAllNetworks(networks, pk, dleAddress, deployResults, modulesToDeploy);
// Объединяем результаты
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'
};
return acc;
}, {}) : {}
}));
console.log('MODULES_DEPLOY_RESULT', JSON.stringify(finalResults));
// Сохраняем результаты в отдельные файлы для каждого модуля
const dleDir = path.join(__dirname, '../contracts-data/modules');
if (!fs.existsSync(dleDir)) {
fs.mkdirSync(dleDir, { recursive: true });
}
// Создаем файл для каждого модуля
for (const moduleType of modulesToDeploy) {
const moduleInfo = {
moduleType: moduleType,
dleAddress: dleAddress,
networks: [],
deployTimestamp: new Date().toISOString(),
// Добавляем данные из основного DLE контракта
dleName: params.name,
dleSymbol: params.symbol,
dleLocation: params.location,
dleJurisdiction: params.jurisdiction
};
// Собираем информацию о всех сетях для этого модуля
for (let i = 0; i < networks.length; i++) {
const rpcUrl = networks[i];
const deployResult = deployResults[i];
const verificationResult = verificationResults[i];
const moduleResult = deployResult.modules?.[moduleType];
const verification = verificationResult?.modules?.[moduleType] || 'unknown';
try {
const provider = new hre.ethers.JsonRpcProvider(rpcUrl);
const network = await provider.getNetwork();
moduleInfo.networks.push({
chainId: Number(network.chainId),
rpcUrl: rpcUrl,
address: moduleResult?.success ? moduleResult.address : null,
verification: verification,
success: moduleResult?.success || false,
error: moduleResult?.error || null
});
} catch (error) {
console.error(`[MODULES_DBG] Ошибка получения chainId для модуля ${moduleType} в сети ${i + 1}:`, error.message);
moduleInfo.networks.push({
chainId: null,
rpcUrl: rpcUrl,
address: null,
verification: 'error',
success: false,
error: error.message
});
}
}
// Сохраняем файл модуля
const fileName = `${moduleType}-${dleAddress.toLowerCase()}.json`;
const filePath = path.join(dleDir, fileName);
fs.writeFileSync(filePath, JSON.stringify(moduleInfo, null, 2));
console.log(`[MODULES_DBG] ${moduleType} info saved to: ${filePath}`);
}
console.log('[MODULES_DBG] All modules deployment completed!');
console.log(`[MODULES_DBG] Available modules: ${Object.keys(MODULE_CONFIGS).join(', ')}`);
console.log(`[MODULES_DBG] DLE Address: ${dleAddress}`);
console.log(`[MODULES_DBG] DLE Name: ${params.name}`);
console.log(`[MODULES_DBG] DLE Symbol: ${params.symbol}`);
console.log(`[MODULES_DBG] DLE Location: ${params.location}`);
}
main().catch((e) => { console.error(e); process.exit(1); });

View File

@@ -14,6 +14,7 @@
const hre = require('hardhat');
const path = require('path');
const fs = require('fs');
const { getRpcUrlByChainId } = require('../../services/rpcProviderService');
// Подбираем безопасные gas/fee для разных сетей (включая L2)
async function getFeeOverrides(provider, { minPriorityGwei = 1n, minFeeGwei = 20n } = {}) {
@@ -32,7 +33,7 @@ async function getFeeOverrides(provider, { minPriorityGwei = 1n, minFeeGwei = 20
return overrides;
}
async function deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, dleInit) {
async function deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, dleInit, params) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
@@ -162,6 +163,28 @@ async function deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, d
const existingCode = await provider.getCode(predictedAddress);
if (existingCode && existingCode !== '0x') {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE already exists at predictedAddress, skip deploy`);
// Проверяем и инициализируем логотип для существующего контракта
if (params.logoURI && params.logoURI !== '') {
try {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} checking logoURI for existing contract`);
const DLE = await hre.ethers.getContractFactory('DLE');
const dleContract = DLE.attach(predictedAddress);
const currentLogo = await dleContract.logoURI();
if (currentLogo === '' || currentLogo === '0x') {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing logoURI for existing contract: ${params.logoURI}`);
const logoTx = await dleContract.connect(wallet).initializeLogoURI(params.logoURI, feeOverrides);
await logoTx.wait();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialized for existing contract`);
} else {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI already set: ${currentLogo}`);
}
} catch (error) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialization failed for existing contract: ${error.message}`);
}
}
return { address: predictedAddress, chainId: Number(net.chainId) };
}
@@ -191,303 +214,74 @@ async function deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, d
const deployedAddress = rc.contractAddress || predictedAddress;
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE deployed at=${deployedAddress}`);
// Инициализация логотипа если он указан
if (params.logoURI && params.logoURI !== '') {
try {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing logoURI: ${params.logoURI}`);
const DLE = await hre.ethers.getContractFactory('DLE');
const dleContract = DLE.attach(deployedAddress);
const logoTx = await dleContract.connect(wallet).initializeLogoURI(params.logoURI, feeOverrides);
await logoTx.wait();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialized successfully`);
} catch (error) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialization failed: ${error.message}`);
// Не прерываем деплой из-за ошибки логотипа
}
} else {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} no logoURI specified, skipping initialization`);
}
return { address: deployedAddress, chainId: Number(net.chainId) };
}
// Деплой модулей в одной сети
async function deployModulesInNetwork(rpcUrl, pk, dleAddress, params) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
const net = await provider.getNetwork();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} deploying modules...`);
const modules = {};
// Получаем начальный nonce для всех модулей
let currentNonce = await wallet.getNonce();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} starting nonce for modules: ${currentNonce}`);
// Функция для безопасного деплоя с правильным nonce
async function deployWithNonce(contractFactory, args, moduleName) {
try {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} deploying ${moduleName} with nonce: ${currentNonce}`);
// Проверяем, что nonce актуален
const actualNonce = await wallet.getNonce();
if (actualNonce > currentNonce) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce mismatch, updating from ${currentNonce} to ${actualNonce}`);
currentNonce = actualNonce;
}
const contract = await contractFactory.connect(wallet).deploy(...args);
await contract.waitForDeployment();
const address = await contract.getAddress();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} ${moduleName} deployed at: ${address}`);
currentNonce++;
return address;
} catch (error) {
console.error(`[MULTI_DBG] chainId=${Number(net.chainId)} ${moduleName} deployment failed:`, error.message);
// Даже при ошибке увеличиваем nonce, чтобы не было конфликтов
currentNonce++;
return null;
}
}
// Деплой TreasuryModule
const TreasuryModule = await hre.ethers.getContractFactory('TreasuryModule');
modules.treasuryModule = await deployWithNonce(
TreasuryModule,
[dleAddress, Number(net.chainId), wallet.address], // _dleContract, _chainId, _emergencyAdmin
'TreasuryModule'
);
// Деплой TimelockModule
const TimelockModule = await hre.ethers.getContractFactory('TimelockModule');
modules.timelockModule = await deployWithNonce(
TimelockModule,
[dleAddress], // _dleContract
'TimelockModule'
);
// Деплой DLEReader
const DLEReader = await hre.ethers.getContractFactory('DLEReader');
modules.dleReader = await deployWithNonce(
DLEReader,
[dleAddress], // _dleContract
'DLEReader'
);
// Инициализация модулей в DLE
try {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing modules in DLE with nonce: ${currentNonce}`);
// Проверяем, что nonce актуален
const actualNonce = await wallet.getNonce();
if (actualNonce > currentNonce) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce mismatch before module init, updating from ${currentNonce} to ${actualNonce}`);
currentNonce = actualNonce;
}
const dleContract = await hre.ethers.getContractAt('DLE', dleAddress, wallet);
// Проверяем, что все модули задеплоены
const treasuryAddress = modules.treasuryModule;
const timelockAddress = modules.timelockModule;
const readerAddress = modules.dleReader;
if (treasuryAddress && timelockAddress && readerAddress) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} All modules deployed, initializing...`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Treasury: ${treasuryAddress}`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Timelock: ${timelockAddress}`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Reader: ${readerAddress}`);
// Модули деплоятся отдельно, инициализация через governance
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Modules deployed successfully, initialization will be done through governance proposals`);
} else {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} skipping module initialization - not all modules deployed`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Treasury: ${treasuryAddress || 'MISSING'}`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Timelock: ${timelockAddress || 'MISSING'}`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Reader: ${readerAddress || 'MISSING'}`);
}
} catch (error) {
console.error(`[MULTI_DBG] chainId=${Number(net.chainId)} module initialization failed:`, error.message);
// Даже при ошибке увеличиваем nonce
currentNonce++;
}
// Инициализация logoURI
try {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing logoURI with nonce: ${currentNonce}`);
// Проверяем, что nonce актуален
const actualNonce = await wallet.getNonce();
if (actualNonce > currentNonce) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce mismatch before logoURI init, updating from ${currentNonce} to ${actualNonce}`);
currentNonce = actualNonce;
}
// Используем логотип из параметров деплоя или fallback
const logoURL = params.logoURI || "https://via.placeholder.com/200x200/0066cc/ffffff?text=DLE";
const dleContract = await hre.ethers.getContractAt('DLE', dleAddress, wallet);
await dleContract.initializeLogoURI(logoURL);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialized: ${logoURL}`);
currentNonce++;
} catch (e) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialization failed: ${e.message}`);
// Fallback на базовый логотип
try {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} trying fallback logoURI with nonce: ${currentNonce}`);
const dleContract = await hre.ethers.getContractAt('DLE', dleAddress, wallet);
await dleContract.initializeLogoURI("https://via.placeholder.com/200x200/0066cc/ffffff?text=DLE");
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} fallback logoURI initialized`);
currentNonce++;
} catch (fallbackError) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} fallback logoURI also failed: ${fallbackError.message}`);
// Даже при ошибке увеличиваем nonce
currentNonce++;
}
}
return modules;
}
// Деплой модулей во всех сетях
async function deployModulesInAllNetworks(networks, pk, dleAddress, params) {
const moduleResults = [];
for (let i = 0; i < networks.length; i++) {
const rpcUrl = networks[i];
console.log(`[MULTI_DBG] deploying modules to network ${i + 1}/${networks.length}: ${rpcUrl}`);
try {
const modules = await deployModulesInNetwork(rpcUrl, pk, dleAddress, params);
moduleResults.push(modules);
} catch (error) {
console.error(`[MULTI_DBG] Failed to deploy modules in network ${i + 1}:`, error.message);
moduleResults.push({ error: error.message });
}
}
return moduleResults;
}
// Верификация контрактов в одной сети
async function verifyContractsInNetwork(rpcUrl, pk, dleAddress, modules, params) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
const net = await provider.getNetwork();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} starting verification...`);
const verification = {};
try {
// Верификация DLE
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} verifying DLE...`);
await hre.run("verify:verify", {
address: dleAddress,
constructorArguments: [
{
name: params.name || '',
symbol: params.symbol || '',
location: params.location || '',
coordinates: params.coordinates || '',
jurisdiction: params.jurisdiction || 0,
oktmo: params.oktmo || '',
okvedCodes: params.okvedCodes || [],
kpp: params.kpp ? BigInt(params.kpp) : 0n,
quorumPercentage: params.quorumPercentage || 51,
initialPartners: params.initialPartners || [],
initialAmounts: (params.initialAmounts || []).map(amount => BigInt(amount)),
supportedChainIds: (params.supportedChainIds || []).map(id => BigInt(id))
},
BigInt(params.currentChainId || params.supportedChainIds?.[0] || 1),
params.initializer || params.initialPartners?.[0] || "0x0000000000000000000000000000000000000000"
],
});
verification.dle = 'success';
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE verification successful`);
} catch (error) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE verification failed: ${error.message}`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE verification error details:`, error);
verification.dle = 'failed';
}
// Верификация модулей
if (modules && !modules.error) {
try {
// Верификация TreasuryModule
if (modules.treasuryModule) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} verifying TreasuryModule...`);
await hre.run("verify:verify", {
address: modules.treasuryModule,
constructorArguments: [
dleAddress, // _dleContract
Number(net.chainId), // _chainId
wallet.address // _emergencyAdmin
],
});
verification.treasuryModule = 'success';
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} TreasuryModule verification successful`);
}
} catch (error) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} TreasuryModule verification failed: ${error.message}`);
verification.treasuryModule = 'failed';
}
try {
// Верификация TimelockModule
if (modules.timelockModule) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} verifying TimelockModule...`);
await hre.run("verify:verify", {
address: modules.timelockModule,
constructorArguments: [
dleAddress // _dleContract
],
});
verification.timelockModule = 'success';
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} TimelockModule verification successful`);
}
} catch (error) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} TimelockModule verification failed: ${error.message}`);
verification.timelockModule = 'failed';
}
try {
// Верификация DLEReader
if (modules.dleReader) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} verifying DLEReader...`);
await hre.run("verify:verify", {
address: modules.dleReader,
constructorArguments: [
dleAddress // _dleContract
],
});
verification.dleReader = 'success';
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLEReader verification successful`);
}
} catch (error) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLEReader verification failed: ${error.message}`);
verification.dleReader = 'failed';
}
}
return verification;
}
// Верификация контрактов во всех сетях
async function verifyContractsInAllNetworks(networks, pk, dleAddress, moduleResults, params) {
const verificationResults = [];
for (let i = 0; i < networks.length; i++) {
const rpcUrl = networks[i];
console.log(`[MULTI_DBG] verifying contracts in network ${i + 1}/${networks.length}: ${rpcUrl}`);
try {
const verification = await verifyContractsInNetwork(rpcUrl, pk, dleAddress, moduleResults[i], params);
verificationResults.push(verification);
} catch (error) {
console.error(`[MULTI_DBG] Failed to verify contracts in network ${i + 1}:`, error.message);
verificationResults.push({ error: error.message });
}
}
return verificationResults;
}
async function main() {
const { ethers } = hre;
// Загружаем параметры из файла
// Загружаем параметры из базы данных или файла
let params;
try {
// Пытаемся загрузить из базы данных
const DeployParamsService = require('../../services/deployParamsService');
const deployParamsService = new DeployParamsService();
// Проверяем, передан ли конкретный deploymentId
const deploymentId = process.env.DEPLOYMENT_ID;
if (deploymentId) {
console.log(`🔍 Ищем параметры для deploymentId: ${deploymentId}`);
params = await deployParamsService.getDeployParams(deploymentId);
if (params) {
console.log('✅ Параметры загружены из базы данных по deploymentId');
} else {
throw new Error(`Параметры деплоя не найдены для deploymentId: ${deploymentId}`);
}
} else {
// Получаем последние параметры деплоя
const latestParams = await deployParamsService.getLatestDeployParams(1);
if (latestParams.length > 0) {
params = latestParams[0];
console.log('✅ Параметры загружены из базы данных (последние)');
} else {
throw new Error('Параметры деплоя не найдены в базе данных');
}
}
await deployParamsService.close();
} catch (dbError) {
console.log('⚠️ Не удалось загрузить параметры из БД, пытаемся загрузить из файла:', dbError.message);
// Fallback к файлу
const paramsPath = path.join(__dirname, './current-params.json');
if (!fs.existsSync(paramsPath)) {
throw new Error('Файл параметров не найден: ' + paramsPath);
}
const params = JSON.parse(fs.readFileSync(paramsPath, 'utf8'));
params = JSON.parse(fs.readFileSync(paramsPath, 'utf8'));
console.log('✅ Параметры загружены из файла');
}
console.log('[MULTI_DBG] Загружены параметры:', {
name: params.name,
symbol: params.symbol,
@@ -495,16 +289,16 @@ async function main() {
CREATE2_SALT: params.CREATE2_SALT
});
const pk = process.env.PRIVATE_KEY;
const salt = params.CREATE2_SALT;
const networks = params.rpcUrls || [];
const pk = params.private_key || process.env.PRIVATE_KEY;
const salt = params.CREATE2_SALT || params.create2_salt;
const networks = params.rpcUrls || params.rpc_urls || [];
if (!pk) throw new Error('Env: PRIVATE_KEY');
if (!salt) throw new Error('CREATE2_SALT not found in params');
if (networks.length === 0) throw new Error('RPC URLs not found in params');
// Prepare init code once
const DLE = await hre.ethers.getContractFactory('DLE');
const DLE = await hre.ethers.getContractFactory('contracts/DLE.sol:DLE');
const dleConfig = {
name: params.name || '',
symbol: params.symbol || '',
@@ -516,7 +310,7 @@ async function main() {
kpp: params.kpp ? BigInt(params.kpp) : 0n,
quorumPercentage: params.quorumPercentage || 51,
initialPartners: params.initialPartners || [],
initialAmounts: (params.initialAmounts || []).map(amount => BigInt(amount)),
initialAmounts: (params.initialAmounts || []).map(amount => BigInt(amount) * BigInt(10**18)),
supportedChainIds: (params.supportedChainIds || []).map(id => BigInt(id))
};
const deployTx = await DLE.getDeployTransaction(dleConfig, BigInt(params.currentChainId || params.supportedChainIds?.[0] || 1), params.initializer || params.initialPartners?.[0] || "0x0000000000000000000000000000000000000000");
@@ -559,7 +353,7 @@ async function main() {
console.log(`[MULTI_DBG] 📡 Network ${i + 1} chainId: ${chainId}`);
const r = await deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, dleInit);
const r = await deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, dleInit, params);
console.log(`[MULTI_DBG] ✅ Network ${i + 1} (chainId: ${chainId}) deployment SUCCESS: ${r.address}`);
return { rpcUrl, chainId, ...r };
} catch (error) {
@@ -603,76 +397,51 @@ async function main() {
console.log('[MULTI_DBG] SUCCESS: All DLE addresses are identical:', uniqueAddresses[0]);
// Деплой модулей ОТКЛЮЧЕН - модули будут деплоиться отдельно
console.log('[MULTI_DBG] Module deployment DISABLED - modules will be deployed separately');
const moduleResults = [];
const verificationResults = [];
// Автоматическая верификация контрактов
let verificationResults = [];
console.log(`[MULTI_DBG] autoVerifyAfterDeploy: ${params.autoVerifyAfterDeploy}`);
if (params.autoVerifyAfterDeploy) {
console.log('[MULTI_DBG] Starting automatic contract verification...');
try {
// Импортируем функцию верификации
const { verifyWithHardhatV2 } = require('../verify-with-hardhat-v2');
// Подготавливаем данные о развернутых сетях
const deployedNetworks = results
.filter(result => result.address && !result.error)
.map(result => ({
chainId: result.chainId,
address: result.address
}));
// Запускаем верификацию с данными о сетях
await verifyWithHardhatV2(params, deployedNetworks);
// Если верификация прошла успешно, отмечаем все как верифицированные
verificationResults = networks.map(() => 'verified');
console.log('[MULTI_DBG] ✅ Automatic verification completed successfully');
} catch (verificationError) {
console.error('[MULTI_DBG] ❌ Automatic verification failed:', verificationError.message);
verificationResults = networks.map(() => 'verification_failed');
}
} else {
console.log('[MULTI_DBG] Contract verification disabled (autoVerifyAfterDeploy: false)');
verificationResults = networks.map(() => 'disabled');
}
// Объединяем результаты
const finalResults = results.map((result, index) => ({
...result,
modules: moduleResults[index] || {},
verification: verificationResults[index] || {}
verification: verificationResults[index] || 'failed'
}));
console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(finalResults));
// Сохраняем каждый модуль в отдельный файл
const dleAddress = uniqueAddresses[0];
const modulesDir = path.join(__dirname, '../contracts-data/modules');
if (!fs.existsSync(modulesDir)) {
fs.mkdirSync(modulesDir, { recursive: true });
}
// Создаем файлы для каждого типа модуля
const moduleTypes = ['treasury', 'timelock', 'reader'];
const moduleKeys = ['treasuryModule', 'timelockModule', 'dleReader'];
for (let moduleIndex = 0; moduleIndex < moduleTypes.length; moduleIndex++) {
const moduleType = moduleTypes[moduleIndex];
const moduleKey = moduleKeys[moduleIndex];
const moduleInfo = {
moduleType: moduleType,
dleAddress: dleAddress,
networks: [],
deployTimestamp: new Date().toISOString()
};
// Собираем адреса модуля во всех сетях
for (let i = 0; i < networks.length; i++) {
const rpcUrl = networks[i];
const moduleResult = moduleResults[i];
try {
const provider = new hre.ethers.JsonRpcProvider(rpcUrl);
const network = await provider.getNetwork();
moduleInfo.networks.push({
chainId: Number(network.chainId),
rpcUrl: rpcUrl,
address: moduleResult && moduleResult[moduleKey] ? moduleResult[moduleKey] : null,
verification: verificationResults[i] && verificationResults[i][moduleKey] ? verificationResults[i][moduleKey] : 'unknown'
});
} catch (error) {
console.error(`[MULTI_DBG] Ошибка получения chainId для модуля ${moduleType} в сети ${i + 1}:`, error.message);
moduleInfo.networks.push({
chainId: null,
rpcUrl: rpcUrl,
address: null,
verification: 'error'
});
}
}
// Сохраняем файл модуля
const moduleFileName = `${moduleType}-${dleAddress.toLowerCase()}.json`;
const moduleFilePath = path.join(modulesDir, moduleFileName);
fs.writeFileSync(moduleFilePath, JSON.stringify(moduleInfo, null, 2));
console.log(`[MULTI_DBG] Module ${moduleType} saved to: ${moduleFilePath}`);
}
console.log(`[MULTI_DBG] All modules saved to separate files in: ${modulesDir}`);
console.log('[MULTI_DBG] DLE deployment completed successfully!');
}
main().catch((e) => { console.error(e); process.exit(1); });

View File

@@ -0,0 +1,130 @@
/**
* Главный скрипт для запуска всех тестов
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
*/
const { spawn } = require('child_process');
const path = require('path');
console.log('🧪 ЗАПУСК ВСЕХ ТЕСТОВ ДЛЯ ВЫЯВЛЕНИЯ ПРОБЛЕМЫ');
console.log('=' .repeat(70));
const tests = [
{
name: 'Тест создания файла',
script: './test-file-creation.js',
description: 'Проверяет базовое создание и обновление файла current-params.json'
},
{
name: 'Тест полного потока деплоя',
script: './test-deploy-flow.js',
description: 'Имитирует полный процесс деплоя DLE с созданием файла'
},
{
name: 'Тест сохранения файла',
script: './test-file-persistence.js',
description: 'Проверяет сохранение файла после различных операций'
},
{
name: 'Тест обработки ошибок',
script: './test-error-handling.js',
description: 'Проверяет поведение при ошибках деплоя'
}
];
async function runTest(testInfo, index) {
return new Promise((resolve, reject) => {
console.log(`\n${index + 1}️⃣ ${testInfo.name}`);
console.log(`📝 ${testInfo.description}`);
console.log('─'.repeat(50));
const testPath = path.join(__dirname, testInfo.script);
const testProcess = spawn('node', [testPath], {
stdio: 'inherit',
cwd: __dirname
});
testProcess.on('close', (code) => {
if (code === 0) {
console.log(`${testInfo.name} - УСПЕШНО`);
resolve(true);
} else {
console.log(`${testInfo.name} - ОШИБКА (код: ${code})`);
resolve(false);
}
});
testProcess.on('error', (error) => {
console.log(`${testInfo.name} - ОШИБКА ЗАПУСКА: ${error.message}`);
resolve(false);
});
});
}
async function runAllTests() {
console.log('🚀 Запуск всех тестов...\n');
const results = [];
for (let i = 0; i < tests.length; i++) {
const result = await runTest(tests[i], i);
results.push({
name: tests[i].name,
success: result
});
// Небольшая пауза между тестами
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Итоговый отчет
console.log('\n' + '='.repeat(70));
console.log('📊 ИТОГОВЫЙ ОТЧЕТ ТЕСТОВ');
console.log('='.repeat(70));
const successfulTests = results.filter(r => r.success).length;
const totalTests = results.length;
results.forEach((result, index) => {
const status = result.success ? '✅' : '❌';
console.log(`${index + 1}. ${status} ${result.name}`);
});
console.log(`\n📈 Результаты: ${successfulTests}/${totalTests} тестов прошли успешно`);
if (successfulTests === totalTests) {
console.log('🎉 ВСЕ ТЕСТЫ ПРОШЛИ УСПЕШНО!');
console.log('💡 Проблема НЕ в базовых операциях с файлами');
console.log('🔍 Возможные причины проблемы:');
console.log(' - Процесс деплоя прерывается до создания файла');
console.log(' - Ошибка в логике dleV2Service.js');
console.log(' - Проблема с правами доступа к файлам');
console.log(' - Конфликт с другими процессами');
} else {
console.log('⚠️ НЕКОТОРЫЕ ТЕСТЫ НЕ ПРОШЛИ');
console.log('🔍 Это поможет локализовать проблему');
}
console.log('\n🛠 СЛЕДУЮЩИЕ ШАГИ:');
console.log('1. Запустите: node debug-file-monitor.js (в отдельном терминале)');
console.log('2. Запустите деплой DLE в другом терминале');
console.log('3. Наблюдайте за созданием/удалением файлов в реальном времени');
console.log('4. Проверьте логи Docker: docker logs dapp-backend -f');
console.log('\n📋 ДОПОЛНИТЕЛЬНЫЕ КОМАНДЫ ДЛЯ ОТЛАДКИ:');
console.log('# Проверить права доступа к директориям:');
console.log('ls -la backend/scripts/deploy/');
console.log('ls -la backend/temp/');
console.log('');
console.log('# Проверить процессы Node.js:');
console.log('ps aux | grep node');
console.log('');
console.log('# Проверить использование диска:');
console.log('df -h backend/scripts/deploy/');
}
// Запускаем все тесты
runAllTests().catch(error => {
console.error('❌ КРИТИЧЕСКАЯ ОШИБКА:', error.message);
process.exit(1);
});

View File

@@ -0,0 +1,240 @@
/**
* Верификация контрактов с Hardhat V2 API
*/
const { execSync } = require('child_process');
const DeployParamsService = require('../services/deployParamsService');
async function verifyWithHardhatV2(params = null, deployedNetworks = null) {
console.log('🚀 Запуск верификации с Hardhat V2...');
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];
}
if (!params.etherscan_api_key) {
throw new Error('Etherscan API ключ не найден в параметрах');
}
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 = {
1: 'mainnet',
11155111: 'sepolia',
17000: 'holesky',
137: 'polygon',
42161: 'arbitrumOne',
421614: 'arbitrumSepolia',
56: 'bsc',
8453: 'base',
84532: 'baseSepolia'
};
// Подготавливаем аргументы конструктора
const constructorArgs = [
{
name: params.name || '',
symbol: params.symbol || '',
location: params.location || '',
coordinates: params.coordinates || '',
jurisdiction: params.jurisdiction || 0,
oktmo: params.oktmo || '',
okvedCodes: params.okvedCodes || [],
kpp: params.kpp ? params.kpp : 0,
quorumPercentage: params.quorumPercentage || 51,
initialPartners: params.initialPartners || [],
initialAmounts: (params.initialAmounts || []).map(amount => (parseFloat(amount) * 10**18).toString()),
supportedChainIds: (params.supportedChainIds || []).map(id => id.toString())
},
(params.currentChainId || params.supportedChainIds?.[0] || 1).toString(),
params.initializer || params.initialPartners?.[0] || "0x0000000000000000000000000000000000000000"
];
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));
}
// Создаем временный файл с аргументами конструктора
const fs = require('fs');
const path = require('path');
const argsFile = path.join(__dirname, `constructor-args-${Date.now()}.json`);
try {
fs.writeFileSync(argsFile, JSON.stringify(constructorArgs, null, 2));
// Формируем команду верификации с файлом аргументов
const command = `ETHERSCAN_API_KEY="${params.etherscan_api_key}" npx hardhat verify --network ${networkName} ${address} --constructor-args ${argsFile}`;
console.log(`💻 Выполняем команду: npx hardhat verify --network ${networkName} ${address} --constructor-args ${argsFile}`);
const output = execSync(command, {
cwd: '/app',
encoding: 'utf8',
stdio: 'pipe'
});
console.log('✅ Верификация успешна:');
console.log(output);
verificationResults.push({
success: true,
network: networkName,
chainId: chainId
});
// Удаляем временный файл
try {
fs.unlinkSync(argsFile);
} catch (e) {
console.log(`⚠️ Не удалось удалить временный файл: ${argsFile}`);
}
} catch (error) {
// Удаляем временный файл в случае ошибки
try {
fs.unlinkSync(argsFile);
} catch (e) {
console.log(`⚠️ Не удалось удалить временный файл: ${argsFile}`);
}
const errorOutput = error.stdout || error.stderr || error.message;
console.log('📥 Вывод команды:');
console.log(errorOutput);
if (errorOutput.includes('Already Verified')) {
console.log(' Контракт уже верифицирован');
verificationResults.push({
success: true,
network: networkName,
chainId: chainId,
alreadyVerified: true
});
} else if (errorOutput.includes('Successfully verified')) {
console.log('✅ Контракт успешно верифицирован!');
verificationResults.push({
success: true,
network: networkName,
chainId: chainId
});
} else {
console.log('❌ Ошибка верификации');
verificationResults.push({
success: false,
network: networkName,
chainId: chainId,
error: errorOutput
});
}
}
}
// Выводим итоговые результаты
console.log('\n📊 Итоговые результаты верификации:');
const successful = verificationResults.filter(r => r.success).length;
const failed = verificationResults.filter(r => !r.success).length;
const alreadyVerified = verificationResults.filter(r => r.alreadyVerified).length;
console.log(`✅ Успешно верифицировано: ${successful}`);
console.log(` Уже было верифицировано: ${alreadyVerified}`);
console.log(`❌ Ошибки: ${failed}`);
verificationResults.forEach(result => {
const status = result.success
? (result.alreadyVerified ? '' : '✅')
: '❌';
console.log(`${status} ${result.network} (${result.chainId}): ${result.success ? 'OK' : result.error?.substring(0, 100) + '...'}`);
});
console.log('\n🎉 Верификация завершена!');
} catch (error) {
console.error('💥 Ошибка верификации:', error.message);
console.error(error.stack);
process.exit(1);
}
}
// Запускаем верификацию если скрипт вызван напрямую
if (require.main === module) {
verifyWithHardhatV2()
.then(() => {
console.log('\n🏁 Скрипт завершен');
process.exit(0);
})
.catch((error) => {
console.error('💥 Скрипт завершился с ошибкой:', error);
process.exit(1);
});
}
module.exports = { verifyWithHardhatV2, verifyContracts: verifyWithHardhatV2 };