ваше сообщение коммита
This commit is contained in:
@@ -64,7 +64,8 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
"function totalSupply() external view returns (uint256)",
|
||||
"function balanceOf(address account) external view returns (uint256)",
|
||||
"function quorumPercentage() external view returns (uint256)",
|
||||
"function getCurrentChainId() external view returns (uint256)"
|
||||
"function getCurrentChainId() external view returns (uint256)",
|
||||
"function logoURI() external view returns (string memory)"
|
||||
];
|
||||
|
||||
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
||||
@@ -75,21 +76,102 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
const quorumPercentage = await dle.quorumPercentage();
|
||||
const currentChainId = await dle.getCurrentChainId();
|
||||
|
||||
// Проверяем баланс создателя (адрес, который деплоил контракт)
|
||||
const deployer = "0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B";
|
||||
const deployerBalance = await dle.balanceOf(deployer);
|
||||
|
||||
// Определяем количество участников (держателей токенов)
|
||||
let participantCount = 0;
|
||||
if (deployerBalance > 0) {
|
||||
participantCount++;
|
||||
// Читаем логотип
|
||||
let logoURI = '';
|
||||
try {
|
||||
logoURI = await dle.logoURI();
|
||||
} catch (error) {
|
||||
console.log(`[Blockchain] Ошибка при чтении logoURI:`, error.message);
|
||||
}
|
||||
|
||||
// Проверяем, есть ли другие держатели токенов
|
||||
// Для простоты считаем, что если создатель имеет меньше 100% токенов, то есть другие участники
|
||||
const deployerPercentage = (Number(deployerBalance) / Number(totalSupply)) * 100;
|
||||
if (deployerPercentage < 100) {
|
||||
participantCount = Math.max(participantCount, 2); // Минимум 2 участника
|
||||
// Получаем информацию о партнерах из блокчейна
|
||||
const partnerBalances = [];
|
||||
let participantCount = 0;
|
||||
|
||||
// Получаем события InitialTokensDistributed для определения партнеров
|
||||
try {
|
||||
// Получаем последние блоки для поиска событий
|
||||
const currentBlock = await provider.getBlockNumber();
|
||||
const fromBlock = Math.max(0, currentBlock - 10000); // Ищем в последних 10000 блоках
|
||||
|
||||
// ABI для события InitialTokensDistributed
|
||||
const eventAbi = [
|
||||
"event InitialTokensDistributed(address[] partners, uint256[] amounts)"
|
||||
];
|
||||
|
||||
const dleWithEvents = new ethers.Contract(dleAddress, [...dleAbi, ...eventAbi], provider);
|
||||
|
||||
// Ищем события InitialTokensDistributed
|
||||
const events = await dleWithEvents.queryFilter(
|
||||
dleWithEvents.filters.InitialTokensDistributed(),
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
|
||||
if (events.length > 0) {
|
||||
// Берем последнее событие
|
||||
const lastEvent = events[events.length - 1];
|
||||
const partners = lastEvent.args.partners;
|
||||
const amounts = lastEvent.args.amounts;
|
||||
|
||||
for (let i = 0; i < partners.length; i++) {
|
||||
const partnerAddress = partners[i];
|
||||
const amount = amounts[i];
|
||||
|
||||
// Проверяем текущий баланс
|
||||
const currentBalance = await dle.balanceOf(partnerAddress);
|
||||
if (Number(currentBalance) > 0) {
|
||||
participantCount++;
|
||||
partnerBalances.push({
|
||||
address: partnerAddress,
|
||||
balance: ethers.formatUnits(currentBalance, 18),
|
||||
percentage: (Number(currentBalance) / Number(totalSupply)) * 100,
|
||||
initialAmount: ethers.formatUnits(amount, 18)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[Blockchain] Ошибка при получении событий партнеров:`, error.message);
|
||||
|
||||
// Fallback: ищем держателей токенов через события Transfer
|
||||
try {
|
||||
const currentBlock = await provider.getBlockNumber();
|
||||
const fromBlock = Math.max(0, currentBlock - 10000);
|
||||
|
||||
const transferEventAbi = [
|
||||
"event Transfer(address indexed from, address indexed to, uint256 value)"
|
||||
];
|
||||
|
||||
const dleWithTransferEvents = new ethers.Contract(dleAddress, [...dleAbi, ...transferEventAbi], provider);
|
||||
|
||||
// Ищем события Transfer с from = address(0) (mint события)
|
||||
const mintEvents = await dleWithTransferEvents.queryFilter(
|
||||
dleWithTransferEvents.filters.Transfer(ethers.ZeroAddress),
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
|
||||
const uniqueRecipients = new Set();
|
||||
for (const event of mintEvents) {
|
||||
uniqueRecipients.add(event.args.to);
|
||||
}
|
||||
|
||||
// Проверяем текущие балансы всех получателей
|
||||
for (const recipient of uniqueRecipients) {
|
||||
const balance = await dle.balanceOf(recipient);
|
||||
if (Number(balance) > 0) {
|
||||
participantCount++;
|
||||
partnerBalances.push({
|
||||
address: recipient,
|
||||
balance: ethers.formatUnits(balance, 18),
|
||||
percentage: (Number(balance) / Number(totalSupply)) * 100
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (fallbackError) {
|
||||
console.log(`[Blockchain] Ошибка при fallback поиске партнеров:`, fallbackError.message);
|
||||
}
|
||||
}
|
||||
|
||||
const blockchainData = {
|
||||
@@ -106,7 +188,8 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
creationTimestamp: Number(dleInfo.creationTimestamp),
|
||||
isActive: dleInfo.isActive,
|
||||
totalSupply: ethers.formatUnits(totalSupply, 18),
|
||||
deployerBalance: ethers.formatUnits(deployerBalance, 18),
|
||||
partnerBalances: partnerBalances, // Информация о партнерах и их балансах
|
||||
logoURI: logoURI, // URL логотипа токена
|
||||
quorumPercentage: Number(quorumPercentage),
|
||||
currentChainId: Number(currentChainId),
|
||||
rpcUsed: rpcUrl,
|
||||
|
||||
@@ -46,7 +46,8 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
"function totalSupply() external view returns (uint256)",
|
||||
"function balanceOf(address account) external view returns (uint256)",
|
||||
"function quorumPercentage() external view returns (uint256)",
|
||||
"function getCurrentChainId() external view returns (uint256)"
|
||||
"function getCurrentChainId() external view returns (uint256)",
|
||||
"function logoURI() external view returns (string memory)"
|
||||
];
|
||||
|
||||
const dle = new ethers.Contract(dleAddress, dleAbi, provider);
|
||||
@@ -57,21 +58,102 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
const quorumPercentage = await dle.quorumPercentage();
|
||||
const currentChainId = await dle.getCurrentChainId();
|
||||
|
||||
// Проверяем баланс создателя (адрес, который деплоил контракт)
|
||||
const deployer = "0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B";
|
||||
const deployerBalance = await dle.balanceOf(deployer);
|
||||
|
||||
// Определяем количество участников (держателей токенов)
|
||||
let participantCount = 0;
|
||||
if (deployerBalance > 0) {
|
||||
participantCount++;
|
||||
// Читаем логотип
|
||||
let logoURI = '';
|
||||
try {
|
||||
logoURI = await dle.logoURI();
|
||||
} catch (error) {
|
||||
console.log(`[DLE Core] Ошибка при чтении logoURI:`, error.message);
|
||||
}
|
||||
|
||||
// Проверяем, есть ли другие держатели токенов
|
||||
// Для простоты считаем, что если создатель имеет меньше 100% токенов, то есть другие участники
|
||||
const deployerPercentage = (Number(deployerBalance) / Number(totalSupply)) * 100;
|
||||
if (deployerPercentage < 100) {
|
||||
participantCount = Math.max(participantCount, 2); // Минимум 2 участника
|
||||
// Получаем информацию о партнерах из блокчейна
|
||||
const partnerBalances = [];
|
||||
let participantCount = 0;
|
||||
|
||||
// Получаем события InitialTokensDistributed для определения партнеров
|
||||
try {
|
||||
// Получаем последние блоки для поиска событий
|
||||
const currentBlock = await provider.getBlockNumber();
|
||||
const fromBlock = Math.max(0, currentBlock - 10000); // Ищем в последних 10000 блоках
|
||||
|
||||
// ABI для события InitialTokensDistributed
|
||||
const eventAbi = [
|
||||
"event InitialTokensDistributed(address[] partners, uint256[] amounts)"
|
||||
];
|
||||
|
||||
const dleWithEvents = new ethers.Contract(dleAddress, [...dleAbi, ...eventAbi], provider);
|
||||
|
||||
// Ищем события InitialTokensDistributed
|
||||
const events = await dleWithEvents.queryFilter(
|
||||
dleWithEvents.filters.InitialTokensDistributed(),
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
|
||||
if (events.length > 0) {
|
||||
// Берем последнее событие
|
||||
const lastEvent = events[events.length - 1];
|
||||
const partners = lastEvent.args.partners;
|
||||
const amounts = lastEvent.args.amounts;
|
||||
|
||||
for (let i = 0; i < partners.length; i++) {
|
||||
const partnerAddress = partners[i];
|
||||
const amount = amounts[i];
|
||||
|
||||
// Проверяем текущий баланс
|
||||
const currentBalance = await dle.balanceOf(partnerAddress);
|
||||
if (Number(currentBalance) > 0) {
|
||||
participantCount++;
|
||||
partnerBalances.push({
|
||||
address: partnerAddress,
|
||||
balance: ethers.formatUnits(currentBalance, 18),
|
||||
percentage: (Number(currentBalance) / Number(totalSupply)) * 100,
|
||||
initialAmount: ethers.formatUnits(amount, 18)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`[DLE Core] Ошибка при получении событий партнеров:`, error.message);
|
||||
|
||||
// Fallback: ищем держателей токенов через события Transfer
|
||||
try {
|
||||
const currentBlock = await provider.getBlockNumber();
|
||||
const fromBlock = Math.max(0, currentBlock - 10000);
|
||||
|
||||
const transferEventAbi = [
|
||||
"event Transfer(address indexed from, address indexed to, uint256 value)"
|
||||
];
|
||||
|
||||
const dleWithTransferEvents = new ethers.Contract(dleAddress, [...dleAbi, ...transferEventAbi], provider);
|
||||
|
||||
// Ищем события Transfer с from = address(0) (mint события)
|
||||
const mintEvents = await dleWithTransferEvents.queryFilter(
|
||||
dleWithTransferEvents.filters.Transfer(ethers.ZeroAddress),
|
||||
fromBlock,
|
||||
currentBlock
|
||||
);
|
||||
|
||||
const uniqueRecipients = new Set();
|
||||
for (const event of mintEvents) {
|
||||
uniqueRecipients.add(event.args.to);
|
||||
}
|
||||
|
||||
// Проверяем текущие балансы всех получателей
|
||||
for (const recipient of uniqueRecipients) {
|
||||
const balance = await dle.balanceOf(recipient);
|
||||
if (Number(balance) > 0) {
|
||||
participantCount++;
|
||||
partnerBalances.push({
|
||||
address: recipient,
|
||||
balance: ethers.formatUnits(balance, 18),
|
||||
percentage: (Number(balance) / Number(totalSupply)) * 100
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (fallbackError) {
|
||||
console.log(`[DLE Core] Ошибка при fallback поиске партнеров:`, fallbackError.message);
|
||||
}
|
||||
}
|
||||
|
||||
const blockchainData = {
|
||||
@@ -87,7 +169,8 @@ router.post('/read-dle-info', async (req, res) => {
|
||||
creationTimestamp: Number(dleInfo.creationTimestamp),
|
||||
isActive: dleInfo.isActive,
|
||||
totalSupply: ethers.formatUnits(totalSupply, 18),
|
||||
deployerBalance: ethers.formatUnits(deployerBalance, 18),
|
||||
partnerBalances: partnerBalances, // Информация о партнерах и их балансах
|
||||
logoURI: logoURI, // URL логотипа токена
|
||||
quorumPercentage: Number(quorumPercentage),
|
||||
currentChainId: Number(currentChainId),
|
||||
participantCount: participantCount
|
||||
|
||||
@@ -66,6 +66,37 @@ router.post('/is-module-active', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Получить информацию о задеплоенных модулях для DLE
|
||||
router.get('/deployed/:dleAddress', async (req, res) => {
|
||||
try {
|
||||
const { dleAddress } = req.params;
|
||||
|
||||
if (!dleAddress) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Адрес DLE обязателен'
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`[DLE Modules] Получение информации о модулях для DLE: ${dleAddress}`);
|
||||
|
||||
// Получаем информацию о модулях из файлов деплоя
|
||||
const modulesInfo = await getDeployedModulesInfo(dleAddress);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: modulesInfo
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[DLE Modules] Ошибка при получении информации о модулях:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Ошибка при получении информации о модулях: ' + error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Получить адрес модуля
|
||||
router.post('/get-module-address', async (req, res) => {
|
||||
try {
|
||||
@@ -300,4 +331,45 @@ router.post('/create-remove-module-proposal', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Функция для получения информации о задеплоенных модулях
|
||||
async function getDeployedModulesInfo(dleAddress) {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
try {
|
||||
// Ищем файл модулей для конкретного DLE
|
||||
const deployDir = path.join(__dirname, '../temp');
|
||||
if (!fs.existsSync(deployDir)) {
|
||||
return { modules: [], verification: {} };
|
||||
}
|
||||
|
||||
// Ищем файл по адресу DLE
|
||||
const modulesFileName = `modules-${dleAddress.toLowerCase()}.json`;
|
||||
const modulesFilePath = path.join(deployDir, modulesFileName);
|
||||
|
||||
if (!fs.existsSync(modulesFilePath)) {
|
||||
console.log(`[DLE Modules] Файл модулей не найден: ${modulesFileName}`);
|
||||
return { modules: [], verification: {} };
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(modulesFilePath, 'utf8'));
|
||||
console.log(`[DLE Modules] Загружена информация о модулях для DLE: ${dleAddress}`);
|
||||
|
||||
return {
|
||||
modules: data.modules || [],
|
||||
verification: data.verification || {},
|
||||
deployTimestamp: data.deployTimestamp
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Ошибка при чтении файла ${modulesFileName}:`, error);
|
||||
return { modules: [], verification: {} };
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении информации о модулях:', error);
|
||||
return { modules: [], verification: {} };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -173,6 +173,230 @@ async function deployInNetwork(rpcUrl, pk, salt, initCodeHash, targetDLENonce, d
|
||||
return { address: deployedAddress, chainId: Number(net.chainId) };
|
||||
}
|
||||
|
||||
// Деплой модулей в одной сети
|
||||
async function deployModulesInNetwork(rpcUrl, pk, dleAddress) {
|
||||
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 = {};
|
||||
|
||||
try {
|
||||
// Деплой TreasuryModule
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} deploying TreasuryModule...`);
|
||||
const TreasuryModule = await hre.ethers.getContractFactory('TreasuryModule');
|
||||
const treasuryModule = await TreasuryModule.connect(wallet).deploy(
|
||||
dleAddress, // _dleContract
|
||||
Number(net.chainId), // _chainId
|
||||
wallet.address // _emergencyAdmin
|
||||
);
|
||||
await treasuryModule.waitForDeployment();
|
||||
const treasuryAddress = await treasuryModule.getAddress();
|
||||
modules.treasuryModule = treasuryAddress;
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} TreasuryModule deployed at: ${treasuryAddress}`);
|
||||
|
||||
// Деплой TimelockModule
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} deploying TimelockModule...`);
|
||||
const TimelockModule = await hre.ethers.getContractFactory('TimelockModule');
|
||||
const timelockModule = await TimelockModule.connect(wallet).deploy(
|
||||
dleAddress // _dleContract
|
||||
);
|
||||
await timelockModule.waitForDeployment();
|
||||
const timelockAddress = await timelockModule.getAddress();
|
||||
modules.timelockModule = timelockAddress;
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} TimelockModule deployed at: ${timelockAddress}`);
|
||||
|
||||
// Деплой DLEReader
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} deploying DLEReader...`);
|
||||
const DLEReader = await hre.ethers.getContractFactory('DLEReader');
|
||||
const dleReader = await DLEReader.connect(wallet).deploy(
|
||||
dleAddress // _dleContract
|
||||
);
|
||||
await dleReader.waitForDeployment();
|
||||
const readerAddress = await dleReader.getAddress();
|
||||
modules.dleReader = readerAddress;
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} DLEReader deployed at: ${readerAddress}`);
|
||||
|
||||
// Инициализация модулей в DLE
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} initializing modules in DLE...`);
|
||||
const dleContract = await hre.ethers.getContractAt('DLE', dleAddress, wallet);
|
||||
|
||||
// Инициализация базовых модулей
|
||||
await dleContract.initializeBaseModules(treasuryAddress, timelockAddress, readerAddress);
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} base modules initialized`);
|
||||
|
||||
// Инициализация logoURI
|
||||
try {
|
||||
// Используем логотип из параметров деплоя или fallback
|
||||
const logoURL = params.logoURI || "https://via.placeholder.com/200x200/0066cc/ffffff?text=DLE";
|
||||
await dleContract.initializeLogoURI(logoURL);
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialized: ${logoURL}`);
|
||||
} catch (e) {
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} logoURI initialization failed: ${e.message}`);
|
||||
// Fallback на базовый логотип
|
||||
try {
|
||||
await dleContract.initializeLogoURI("https://via.placeholder.com/200x200/0066cc/ffffff?text=DLE");
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} fallback logoURI initialized`);
|
||||
} catch (fallbackError) {
|
||||
console.log(`[MULTI_DBG] chainId=${Number(net.chainId)} fallback logoURI also failed: ${fallbackError.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[MULTI_DBG] chainId=${Number(net.chainId)} module deployment failed:`, error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
// Деплой модулей во всех сетях
|
||||
async function deployModulesInAllNetworks(networks, pk, dleAddress) {
|
||||
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);
|
||||
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}`);
|
||||
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;
|
||||
|
||||
@@ -258,7 +482,41 @@ async function main() {
|
||||
}
|
||||
|
||||
console.log('[MULTI_DBG] SUCCESS: All DLE addresses are identical:', uniqueAddresses[0]);
|
||||
console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(results));
|
||||
|
||||
// Деплой модулей во всех сетях
|
||||
console.log('[MULTI_DBG] Starting module deployment...');
|
||||
const moduleResults = await deployModulesInAllNetworks(networks, pk, uniqueAddresses[0]);
|
||||
|
||||
// Верификация контрактов
|
||||
console.log('[MULTI_DBG] Starting contract verification...');
|
||||
const verificationResults = await verifyContractsInAllNetworks(networks, pk, uniqueAddresses[0], moduleResults, params);
|
||||
|
||||
// Объединяем результаты
|
||||
const finalResults = results.map((result, index) => ({
|
||||
...result,
|
||||
modules: moduleResults[index] || {},
|
||||
verification: verificationResults[index] || {}
|
||||
}));
|
||||
|
||||
console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(finalResults));
|
||||
|
||||
// Сохраняем информацию о модулях в отдельный файл для каждого DLE
|
||||
const modulesInfo = {
|
||||
dleAddress: uniqueAddresses[0],
|
||||
modules: moduleResults,
|
||||
verification: verificationResults,
|
||||
deployTimestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Создаем директорию temp если её нет
|
||||
const tempDir = path.join(__dirname, '../temp');
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
}
|
||||
|
||||
const deployResultPath = path.join(tempDir, `modules-${uniqueAddresses[0].toLowerCase()}.json`);
|
||||
fs.writeFileSync(deployResultPath, JSON.stringify(modulesInfo, null, 2));
|
||||
console.log(`[MULTI_DBG] Modules info saved to: ${deployResultPath}`);
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error(e); process.exit(1); });
|
||||
|
||||
@@ -458,6 +458,18 @@ class DLEV2Service {
|
||||
deployParams.currentChainId = 1; // По умолчанию Ethereum
|
||||
}
|
||||
|
||||
// Обрабатываем logoURI
|
||||
if (deployParams.logoURI) {
|
||||
// Если logoURI относительный путь, делаем его абсолютным
|
||||
if (deployParams.logoURI.startsWith('/uploads/')) {
|
||||
deployParams.logoURI = `http://localhost:3000${deployParams.logoURI}`;
|
||||
}
|
||||
// Если это placeholder, оставляем как есть
|
||||
if (deployParams.logoURI.includes('placeholder.com')) {
|
||||
// Оставляем как есть
|
||||
}
|
||||
}
|
||||
|
||||
return deployParams;
|
||||
}
|
||||
|
||||
@@ -573,8 +585,24 @@ class DLEV2Service {
|
||||
const resultMatch = stdout.match(/MULTICHAIN_DEPLOY_RESULT\s+(\[.*\])/);
|
||||
|
||||
if (resultMatch) {
|
||||
const result = JSON.parse(resultMatch[1]);
|
||||
resolve(result);
|
||||
const deployResults = JSON.parse(resultMatch[1]);
|
||||
|
||||
// Преобразуем результат в нужный формат
|
||||
const addresses = deployResults.map(r => r.address);
|
||||
const allSame = addresses.every(addr => addr.toLowerCase() === addresses[0].toLowerCase());
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
data: {
|
||||
dleAddress: addresses[0],
|
||||
networks: deployResults.map((r, index) => ({
|
||||
chainId: r.chainId,
|
||||
address: r.address,
|
||||
success: true
|
||||
})),
|
||||
allSame
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Fallback: ищем адреса DLE в выводе по новому формату
|
||||
const dleAddressMatches = stdout.match(/\[MULTI_DBG\] chainId=\d+ DLE deployed at=(0x[a-fA-F0-9]{40})/g);
|
||||
|
||||
3
frontend/public/dle-logo.png
Normal file
3
frontend/public/dle-logo.png
Normal file
@@ -0,0 +1,3 @@
|
||||
# Это placeholder для логотипа DLE
|
||||
# В реальном проекте здесь должен быть PNG файл с логотипом
|
||||
# Размер рекомендуется: 200x200 пикселей
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
20
frontend/public/uploads/logos/default-token.svg
Normal file
20
frontend/public/uploads/logos/default-token.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#0066cc;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#0056b3;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Фон -->
|
||||
<rect width="200" height="200" rx="20" fill="url(#grad1)"/>
|
||||
|
||||
<!-- Текст DLE -->
|
||||
<text x="100" y="120" font-family="Arial, sans-serif" font-size="48" font-weight="bold" text-anchor="middle" fill="white">DLE</text>
|
||||
|
||||
<!-- Дополнительные элементы -->
|
||||
<circle cx="50" cy="50" r="8" fill="rgba(255,255,255,0.3)"/>
|
||||
<circle cx="150" cy="50" r="8" fill="rgba(255,255,255,0.3)"/>
|
||||
<circle cx="50" cy="150" r="8" fill="rgba(255,255,255,0.3)"/>
|
||||
<circle cx="150" cy="150" r="8" fill="rgba(255,255,255,0.3)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 907 B |
@@ -58,9 +58,21 @@
|
||||
@click="selectDle(dle)"
|
||||
>
|
||||
<div class="dle-header">
|
||||
<div class="dle-title-section">
|
||||
<img
|
||||
v-if="dle.logoURI"
|
||||
:src="dle.logoURI"
|
||||
:alt="dle.name"
|
||||
class="dle-logo"
|
||||
@error="handleLogoError"
|
||||
/>
|
||||
<div class="default-logo" v-else>DLE</div>
|
||||
<div class="dle-title">
|
||||
<h3>{{ dle.name }} ({{ dle.symbol }})</h3>
|
||||
<span class="dle-version">{{ dle.version || 'v2' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dle-details">
|
||||
<div class="detail-item" v-if="dle.deployedMultichain">
|
||||
@@ -106,7 +118,22 @@
|
||||
<strong>Коды ОКВЭД:</strong> {{ dle.okvedCodes?.join(', ') || 'Не указаны' }}
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<strong>Партнеры:</strong> {{ dle.participantCount || 0 }} участников
|
||||
<strong>Партнеры:</strong>
|
||||
<span v-if="dle.partnerBalances && dle.partnerBalances.length > 0">
|
||||
{{ dle.participantCount || dle.partnerBalances.length }} участников
|
||||
<div class="partners-details">
|
||||
<div v-for="(partner, index) in dle.partnerBalances.slice(0, 3)" :key="index" class="partner-info">
|
||||
<span class="partner-address">{{ shortenAddress(partner.address) }}</span>
|
||||
<span class="partner-balance">{{ partner.balance }} токенов ({{ partner.percentage.toFixed(1) }}%)</span>
|
||||
</div>
|
||||
<div v-if="dle.partnerBalances.length > 3" class="more-partners">
|
||||
+{{ dle.partnerBalances.length - 3 }} еще
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ dle.participantCount || 0 }} участников
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<strong>Статус:</strong>
|
||||
@@ -229,6 +256,16 @@ const openProposals = () => {
|
||||
router.push('/management/proposals');
|
||||
};
|
||||
|
||||
// Обработка ошибки загрузки логотипа
|
||||
const handleLogoError = (event) => {
|
||||
console.log('Ошибка загрузки логотипа:', event.target.src);
|
||||
event.target.style.display = 'none';
|
||||
const defaultLogo = event.target.parentElement.querySelector('.default-logo');
|
||||
if (defaultLogo) {
|
||||
defaultLogo.style.display = 'flex';
|
||||
}
|
||||
};
|
||||
|
||||
const openTokens = () => {
|
||||
router.push('/management/tokens');
|
||||
};
|
||||
@@ -309,7 +346,8 @@ async function loadDeployedDles() {
|
||||
kpp: blockchainData.kpp || dle.kpp,
|
||||
// Информация о токенах из блокчейна
|
||||
totalSupply: blockchainData.totalSupply,
|
||||
deployerBalance: blockchainData.deployerBalance,
|
||||
partnerBalances: blockchainData.partnerBalances || [], // Информация о партнерах
|
||||
logoURI: blockchainData.logoURI || '', // URL логотипа
|
||||
// Количество участников (держателей токенов)
|
||||
participantCount: blockchainData.participantCount || 0
|
||||
};
|
||||
@@ -867,6 +905,97 @@ onBeforeUnmount(() => {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Стили для отображения логотипа */
|
||||
.dle-title-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dle-logo {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
object-fit: contain;
|
||||
border: 2px solid #e9ecef;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.default-logo {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, var(--color-primary), #0056b3);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
border: 2px solid #e9ecef;
|
||||
}
|
||||
|
||||
.dle-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.dle-title h3 {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.dle-version {
|
||||
font-size: 0.8rem;
|
||||
color: #6c757d;
|
||||
background: #f8f9fa;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
/* Стили для отображения партнеров */
|
||||
.partners-details {
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.partner-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.partner-info:not(:last-child) {
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.partner-address {
|
||||
font-family: 'Courier New', monospace;
|
||||
color: #495057;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.partner-balance {
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.more-partners {
|
||||
text-align: center;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.blocks-row {
|
||||
@@ -880,6 +1009,25 @@ onBeforeUnmount(() => {
|
||||
.management-block h3 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.partner-info {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.dle-title-section {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dle-logo,
|
||||
.default-logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2475,6 +2475,28 @@ const deploySmartContracts = async () => {
|
||||
autoVerifyAfterDeploy: autoVerifyAfterDeploy.value
|
||||
};
|
||||
|
||||
// Обработка логотипа
|
||||
try {
|
||||
if (logoFile.value) {
|
||||
const form = new FormData();
|
||||
form.append('logo', logoFile.value);
|
||||
const uploadResp = await axios.post('/uploads/logo', form, { headers: { 'Content-Type': 'multipart/form-data' } });
|
||||
const uploaded = uploadResp.data?.data?.url || uploadResp.data?.data?.path;
|
||||
if (uploaded) {
|
||||
deployData.logoURI = uploaded;
|
||||
}
|
||||
} else if (ensResolvedUrl.value) {
|
||||
deployData.logoURI = ensResolvedUrl.value;
|
||||
} else {
|
||||
// фолбэк на дефолт
|
||||
deployData.logoURI = '/uploads/logos/default-token.svg';
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Ошибка при обработке логотипа:', error.message);
|
||||
// Используем fallback логотип
|
||||
deployData.logoURI = '/uploads/logos/default-token.svg';
|
||||
}
|
||||
|
||||
console.log('Данные для деплоя DLE:', deployData);
|
||||
|
||||
// Предварительная проверка балансов во всех сетях
|
||||
|
||||
Reference in New Issue
Block a user