Files
DLE/backend/scripts/deploy/deploy-multichain.js
Alex de0f8aecf2 feat: Добавлены формы деплоя модулей DLE с полными настройками
- Создана форма деплоя TreasuryModule с детальными настройками казны
- Создана форма деплоя TimelockModule с настройками временных задержек
- Создана форма деплоя DLEReader с простой конфигурацией
- Добавлены маршруты и индексы для всех модулей
- Исправлены пути импорта BaseLayout
- Добавлены авторские права во все файлы
- Улучшена архитектура деплоя модулей отдельно от основного DLE
2025-09-23 02:57:59 +03:00

685 lines
31 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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;
}
async function deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, dleInit) {
const { ethers } = hre;
const provider = new ethers.JsonRpcProvider(rpcUrl);
const wallet = new ethers.Wallet(pk, provider);
const net = await provider.getNetwork();
// DEBUG: базовая информация по сети
try {
const calcInitHash = ethers.keccak256(dleInit);
const saltLen = ethers.getBytes(salt).length;
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} rpc=${rpcUrl}`);
console.log(`[MULTI_DBG] wallet=${wallet.address} targetDLENonce=${targetDLENonce}`);
console.log(`[MULTI_DBG] saltLenBytes=${saltLen} salt=${salt}`);
console.log(`[MULTI_DBG] initCodeHash(provided)=${initCodeHash}`);
console.log(`[MULTI_DBG] initCodeHash(calculated)=${calcInitHash}`);
console.log(`[MULTI_DBG] dleInit.lenBytes=${ethers.getBytes(dleInit).length} head16=${dleInit.slice(0, 34)}...`);
} catch (e) {
console.log('[MULTI_DBG] precheck error', e?.message || e);
}
// 1) Выравнивание nonce до targetDLENonce нулевыми транзакциями (если нужно)
let current = await provider.getTransactionCount(wallet.address, 'pending');
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} current nonce=${current} target=${targetDLENonce}`);
if (current > targetDLENonce) {
throw new Error(`Current nonce ${current} > targetDLENonce ${targetDLENonce} on chainId=${Number(net.chainId)}`);
}
if (current < targetDLENonce) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} starting nonce alignment: ${current} -> ${targetDLENonce} (${targetDLENonce - current} transactions needed)`);
} else {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce already aligned: ${current} = ${targetDLENonce}`);
}
if (current < targetDLENonce) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} aligning nonce from ${current} to ${targetDLENonce} (${targetDLENonce - current} transactions needed)`);
// Используем burn address для более надежных транзакций
const burnAddress = "0x000000000000000000000000000000000000dEaD";
while (current < targetDLENonce) {
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, // отправляем на burn address вместо своего адреса
value: 0n,
nonce: current,
gasLimit,
...overrides
};
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} sending filler tx nonce=${current} attempt=${attempt + 1}`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} tx details: to=${burnAddress}, value=0, gasLimit=${gasLimit}`);
const txFill = await wallet.sendTransaction(txReq);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} filler tx sent, hash=${txFill.hash}, waiting for confirmation...`);
await txFill.wait();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} filler tx nonce=${current} confirmed, hash=${txFill.hash}`);
sent = true;
} catch (e) {
lastErr = e;
console.log(`[MULTI_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; // увеличиваем gas limit
continue;
}
if (String(e?.message || '').toLowerCase().includes('nonce too low') && attempt < 2) {
// Обновляем nonce и пробуем снова
current = await provider.getTransactionCount(wallet.address, 'pending');
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} updated nonce to ${current}`);
continue;
}
throw e;
}
}
if (!sent) {
console.error(`[MULTI_DBG] chainId=${Number(net.chainId)} failed to send filler tx for nonce=${current}`);
throw lastErr || new Error('filler tx failed');
}
current++;
}
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce alignment completed, current nonce=${current}`);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} ready for DLE deployment with nonce=${current}`);
} else {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} nonce already aligned at ${current}`);
}
// 2) Деплой DLE напрямую на согласованном nonce
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} deploying DLE directly with nonce=${targetDLENonce}`);
const feeOverrides = await getFeeOverrides(provider);
let gasLimit;
try {
// Оцениваем газ для деплоя DLE
const est = await wallet.estimateGas({ data: dleInit, ...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 : 3_000_000n;
const fallbackGas = maxByBalance > 5_000_000n ? 5_000_000n : (maxByBalance < 2_500_000n ? 2_500_000n : maxByBalance);
gasLimit = est ? (est + est / 5n) : fallbackGas;
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} estGas=${est?.toString?.()||'null'} effGasPrice=${effPrice?.toString?.()||'0'} maxByBalance=${maxByBalance.toString()} chosenGasLimit=${gasLimit.toString()}`);
} catch (_) {
gasLimit = 3_000_000n;
}
// Вычисляем предсказанный адрес DLE
const predictedAddress = ethers.getCreateAddress({
from: wallet.address,
nonce: targetDLENonce
});
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} predicted DLE address=${predictedAddress}`);
// Проверяем, не развернут ли уже контракт
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`);
return { address: predictedAddress, chainId: Number(net.chainId) };
}
// Деплоим DLE
let tx;
try {
tx = await wallet.sendTransaction({
data: dleInit,
nonce: targetDLENonce,
gasLimit,
...feeOverrides
});
} catch (e) {
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} deploy error(first): ${e?.message || e}`);
// Повторная попытка с обновленным nonce
const updatedNonce = await provider.getTransactionCount(wallet.address, 'pending');
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} retry deploy with nonce=${updatedNonce}`);
tx = await wallet.sendTransaction({
data: dleInit,
nonce: updatedNonce,
gasLimit,
...feeOverrides
});
}
const rc = await tx.wait();
const deployedAddress = rc.contractAddress || predictedAddress;
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLE deployed at=${deployedAddress}`);
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}`);
// Инициализация базовых модулей
const initTx = await dleContract.initializeBaseModules(treasuryAddress, timelockAddress, readerAddress);
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} Module initialization tx: ${initTx.hash}`);
await initTx.wait();
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} base modules initialized successfully`);
currentNonce++;
} 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;
// Загружаем параметры из файла
const paramsPath = path.join(__dirname, './current-params.json');
if (!fs.existsSync(paramsPath)) {
throw new Error('Файл параметров не найден: ' + paramsPath);
}
const params = JSON.parse(fs.readFileSync(paramsPath, 'utf8'));
console.log('[MULTI_DBG] Загружены параметры:', {
name: params.name,
symbol: params.symbol,
supportedChainIds: params.supportedChainIds,
CREATE2_SALT: params.CREATE2_SALT
});
const pk = process.env.PRIVATE_KEY;
const salt = params.CREATE2_SALT;
const networks = params.rpcUrls || [];
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 dleConfig = {
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))
};
const deployTx = await DLE.getDeployTransaction(dleConfig, BigInt(params.currentChainId || params.supportedChainIds?.[0] || 1), params.initializer || params.initialPartners?.[0] || "0x0000000000000000000000000000000000000000");
const dleInit = deployTx.data;
const initCodeHash = ethers.keccak256(dleInit);
// DEBUG: глобальные значения
try {
const saltLen = ethers.getBytes(salt).length;
console.log(`[MULTI_DBG] GLOBAL saltLenBytes=${saltLen} salt=${salt}`);
console.log(`[MULTI_DBG] GLOBAL initCodeHash(calculated)=${initCodeHash}`);
console.log(`[MULTI_DBG] GLOBAL dleInit.lenBytes=${ethers.getBytes(dleInit).length} head16=${dleInit.slice(0, 34)}...`);
} catch (e) {
console.log('[MULTI_DBG] GLOBAL precheck error', e?.message || e);
}
// Подготовим провайдеры и вычислим общий nonce для DLE
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);
}
const targetDLENonce = Math.max(...nonces);
console.log(`[MULTI_DBG] nonces=${JSON.stringify(nonces)} targetDLENonce=${targetDLENonce}`);
console.log(`[MULTI_DBG] Starting deployment to ${networks.length} networks:`, networks);
// ПАРАЛЛЕЛЬНЫЙ деплой во всех сетях одновременно
console.log(`[MULTI_DBG] Starting PARALLEL deployment to ${networks.length} networks`);
const deploymentPromises = networks.map(async (rpcUrl, i) => {
console.log(`[MULTI_DBG] 🚀 Starting deployment to network ${i + 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(`[MULTI_DBG] 📡 Network ${i + 1} chainId: ${chainId}`);
const r = await deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, dleInit);
console.log(`[MULTI_DBG] ✅ Network ${i + 1} (chainId: ${chainId}) deployment SUCCESS: ${r.address}`);
return { rpcUrl, chainId, ...r };
} catch (error) {
console.error(`[MULTI_DBG] ❌ Network ${i + 1} deployment FAILED:`, error.message);
return { rpcUrl, error: error.message };
}
});
// Ждем завершения всех деплоев
const results = await Promise.all(deploymentPromises);
console.log(`[MULTI_DBG] All ${networks.length} deployments completed`);
// Логируем результаты для каждой сети
results.forEach((result, index) => {
if (result.address) {
console.log(`[MULTI_DBG] ✅ Network ${index + 1} (chainId: ${result.chainId}) SUCCESS: ${result.address}`);
} else {
console.log(`[MULTI_DBG] ❌ Network ${index + 1} (chainId: ${result.chainId}) FAILED: ${result.error}`);
}
});
// Проверяем, что все адреса одинаковые
const addresses = results.map(r => r.address).filter(addr => addr);
const uniqueAddresses = [...new Set(addresses)];
console.log('[MULTI_DBG] All addresses:', addresses);
console.log('[MULTI_DBG] Unique addresses:', uniqueAddresses);
console.log('[MULTI_DBG] Results count:', results.length);
console.log('[MULTI_DBG] Networks count:', networks.length);
if (uniqueAddresses.length > 1) {
console.error('[MULTI_DBG] ERROR: DLE addresses are different across networks!');
console.error('[MULTI_DBG] addresses:', uniqueAddresses);
throw new Error('Nonce alignment failed - addresses are different');
}
if (uniqueAddresses.length === 0) {
console.error('[MULTI_DBG] ERROR: No successful deployments!');
throw new Error('No successful deployments');
}
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 = [];
// Объединяем результаты
const finalResults = results.map((result, index) => ({
...result,
modules: moduleResults[index] || {},
verification: verificationResults[index] || {}
}));
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}`);
}
main().catch((e) => { console.error(e); process.exit(1); });