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

This commit is contained in:
2025-08-29 19:19:26 +03:00
parent 4e4cb611a1
commit c007c0b296
10 changed files with 756 additions and 39 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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;

View File

@@ -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); });

View File

@@ -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);

View File

@@ -0,0 +1,3 @@
# Это placeholder для логотипа DLE
# В реальном проекте здесь должен быть PNG файл с логотипом
# Размер рекомендуется: 200x200 пикселей

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View 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

View File

@@ -58,8 +58,20 @@
@click="selectDle(dle)"
>
<div class="dle-header">
<h3>{{ dle.name }} ({{ dle.symbol }})</h3>
<span class="dle-version">{{ dle.version || 'v2' }}</span>
<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">
@@ -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;
}
}

View File

@@ -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);
// Предварительная проверка балансов во всех сетях