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

This commit is contained in:
2025-09-30 00:23:37 +03:00
parent ca718e3178
commit 4b03951b31
77 changed files with 17161 additions and 7255 deletions

View File

@@ -0,0 +1,106 @@
/**
* ABI для DLE смарт-контракта
* АВТОМАТИЧЕСКИ СГЕНЕРИРОВАНО - НЕ РЕДАКТИРОВАТЬ ВРУЧНУЮ
* Для обновления запустите: node backend/scripts/generate-abi.js
*
* Последнее обновление: 2025-09-29T18:16:32.027Z
*/
export const DLE_ABI = [
"function CLOCK_MODE() returns (string)",
"function DOMAIN_SEPARATOR() returns (bytes32)",
"function activeModules(bytes32 ) returns (bool)",
"function allProposalIds(uint256 ) returns (uint256)",
"function allowance(address owner, address spender) returns (uint256)",
"function approve(address , uint256 ) returns (bool)",
"function balanceOf(address account) returns (uint256)",
"function cancelProposal(uint256 _proposalId, string reason)",
"function checkProposalResult(uint256 _proposalId) returns (bool, bool)",
"function checkpoints(address account, uint32 pos) returns (tuple)",
"function clock() returns (uint48)",
"function createAddModuleProposal(string _description, uint256 _duration, bytes32 _moduleId, address _moduleAddress, uint256 _chainId) returns (uint256)",
"function createProposal(string _description, uint256 _duration, bytes _operation, uint256 _governanceChainId, uint256[] _targetChains, uint256 ) returns (uint256)",
"function createRemoveModuleProposal(string _description, uint256 _duration, bytes32 _moduleId, uint256 _chainId) returns (uint256)",
"function decimals() returns (uint8)",
"function delegate(address delegatee)",
"function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s)",
"function delegates(address account) returns (address)",
"function dleInfo() returns (string, string, string, string, uint256, uint256, uint256, bool)",
"function eip712Domain() returns (bytes1, string, string, uint256, address, bytes32, uint256[])",
"function executeProposal(uint256 _proposalId)",
"function executeProposalBySignatures(uint256 _proposalId, address[] signers, bytes[] signatures)",
"function getCurrentChainId() returns (uint256)",
"function getDLEInfo() returns (tuple)",
"function getModuleAddress(bytes32 _moduleId) returns (address)",
"function getMultichainAddresses() returns (uint256[], address[])",
"function getMultichainInfo() returns (uint256[], uint256)",
"function getMultichainMetadata() returns (string)",
"function getPastTotalSupply(uint256 timepoint) returns (uint256)",
"function getPastVotes(address account, uint256 timepoint) returns (uint256)",
"function getProposalState(uint256 _proposalId) returns (uint8)",
"function getProposalSummary(uint256 _proposalId) returns (uint256, string, uint256, uint256, bool, bool, uint256, address, uint256, uint256, uint256[])",
"function getSupportedChainCount() returns (uint256)",
"function getSupportedChainId(uint256 _index) returns (uint256)",
"function getVotes(address account) returns (uint256)",
"function initializeLogoURI(string _logoURI)",
"function initializer() returns (address)",
"function isActive() returns (bool)",
"function isChainSupported(uint256 _chainId) returns (bool)",
"function isModuleActive(bytes32 _moduleId) returns (bool)",
"function logo() returns (string)",
"function logoURI() returns (string)",
"function maxVotingDuration() returns (uint256)",
"function minVotingDuration() returns (uint256)",
"function modules(bytes32 ) returns (address)",
"function name() returns (string)",
"function nonces(address owner) returns (uint256)",
"function numCheckpoints(address account) returns (uint32)",
"function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
"function proposalCounter() returns (uint256)",
"function proposals(uint256 ) returns (uint256, string, uint256, uint256, bool, bool, uint256, address, bytes, uint256, uint256)",
"function quorumPercentage() returns (uint256)",
"function supportedChainIds(uint256 ) returns (uint256)",
"function supportedChains(uint256 ) returns (bool)",
"function symbol() returns (string)",
"function tokenURI() returns (string)",
"function totalSupply() returns (uint256)",
"function transfer(address , uint256 ) returns (bool)",
"function transferFrom(address , address , uint256 ) returns (bool)",
"function vote(uint256 _proposalId, bool _support)",
"event Approval(address owner, address spender, uint256 value)",
"event ChainAdded(uint256 chainId)",
"event ChainRemoved(uint256 chainId)",
"event DLEInfoUpdated(string name, string symbol, string location, string coordinates, uint256 jurisdiction, string[] okvedCodes, uint256 kpp)",
"event DLEInitialized(string name, string symbol, string location, string coordinates, uint256 jurisdiction, string[] okvedCodes, uint256 kpp, address tokenAddress, uint256[] supportedChainIds)",
"event DelegateChanged(address delegator, address fromDelegate, address toDelegate)",
"event DelegateVotesChanged(address delegate, uint256 previousVotes, uint256 newVotes)",
"event EIP712DomainChanged()",
"event InitialTokensDistributed(address[] partners, uint256[] amounts)",
"event LogoURIUpdated(string oldURI, string newURI)",
"event ModuleAdded(bytes32 moduleId, address moduleAddress)",
"event ModuleRemoved(bytes32 moduleId)",
"event ProposalCancelled(uint256 proposalId, string reason)",
"event ProposalCreated(uint256 proposalId, address initiator, string description)",
"event ProposalExecuted(uint256 proposalId, bytes operation)",
"event ProposalExecutionApprovedInChain(uint256 proposalId, uint256 chainId)",
"event ProposalGovernanceChainSet(uint256 proposalId, uint256 governanceChainId)",
"event ProposalTargetsSet(uint256 proposalId, uint256[] targetChains)",
"event ProposalVoted(uint256 proposalId, address voter, bool support, uint256 votingPower)",
"event QuorumPercentageUpdated(uint256 oldQuorumPercentage, uint256 newQuorumPercentage)",
"event TokensTransferredByGovernance(address recipient, uint256 amount)",
"event Transfer(address from, address to, uint256 value)",
"event VotingDurationsUpdated(uint256 oldMinDuration, uint256 newMinDuration, uint256 oldMaxDuration, uint256 newMaxDuration)",
];
// ABI для деактивации (специальные функции) - НЕ СУЩЕСТВУЮТ В КОНТРАКТЕ
export const DLE_DEACTIVATION_ABI = [
// Эти функции не существуют в контракте DLE
];
// ABI для токенов (базовые функции)
export const TOKEN_ABI = [
"function balanceOf(address owner) view returns (uint256)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)"
];

View File

@@ -12,6 +12,91 @@
import api from '@/api/axios';
import { ethers } from 'ethers';
import { DLE_ABI, DLE_DEACTIVATION_ABI, TOKEN_ABI } from './dle-abi';
// Функция для переключения сети кошелька
export async function switchToVotingNetwork(chainId) {
try {
console.log(`🔄 [NETWORK] Пытаемся переключиться на сеть ${chainId}...`);
// Конфигурации сетей
const networks = {
'11155111': { // Sepolia
chainId: '0xaa36a7',
chainName: 'Sepolia',
nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: ['https://1rpc.io/sepolia'],
blockExplorerUrls: ['https://sepolia.etherscan.io']
},
'17000': { // Holesky
chainId: '0x4268',
chainName: 'Holesky',
nativeCurrency: { name: 'Holesky Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: ['https://ethereum-holesky.publicnode.com'],
blockExplorerUrls: ['https://holesky.etherscan.io']
},
'421614': { // Arbitrum Sepolia
chainId: '0x66eee',
chainName: 'Arbitrum Sepolia',
nativeCurrency: { name: 'Arbitrum Sepolia Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: ['https://sepolia-rollup.arbitrum.io/rpc'],
blockExplorerUrls: ['https://sepolia.arbiscan.io']
},
'84532': { // Base Sepolia
chainId: '0x14a34',
chainName: 'Base Sepolia',
nativeCurrency: { name: 'Base Sepolia Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: ['https://sepolia.base.org'],
blockExplorerUrls: ['https://sepolia.basescan.org']
}
};
const networkConfig = networks[chainId];
if (!networkConfig) {
console.error(`❌ [NETWORK] Неизвестная сеть: ${chainId}`);
return false;
}
// Проверяем, подключена ли уже нужная сеть
const currentChainId = await window.ethereum.request({ method: 'eth_chainId' });
if (currentChainId === networkConfig.chainId) {
console.log(`✅ [NETWORK] Сеть ${chainId} уже подключена`);
return true;
}
// Пытаемся переключиться на нужную сеть
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: networkConfig.chainId }]
});
console.log(`✅ [NETWORK] Успешно переключились на сеть ${chainId}`);
return true;
} catch (switchError) {
// Если сеть не добавлена, добавляем её
if (switchError.code === 4902) {
console.log(` [NETWORK] Добавляем сеть ${chainId}...`);
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [networkConfig]
});
console.log(`✅ [NETWORK] Сеть ${chainId} добавлена и подключена`);
return true;
} catch (addError) {
console.error(`❌ [NETWORK] Ошибка добавления сети ${chainId}:`, addError);
return false;
}
} else {
console.error(`❌ [NETWORK] Ошибка переключения на сеть ${chainId}:`, switchError);
return false;
}
}
} catch (error) {
console.error(`❌ [NETWORK] Общая ошибка переключения сети:`, error);
return false;
}
}
/**
* Проверить подключение к браузерному кошельку
@@ -60,6 +145,8 @@ export async function checkWalletConnection() {
* Используется только система голосования (proposals)
*/
/**
* Получить информацию о DLE из блокчейна
* @param {string} dleAddress - Адрес DLE контракта
@@ -109,12 +196,9 @@ export async function createProposal(dleAddress, proposalData) {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// ABI для создания предложения
const dleAbi = [
"function createProposal(string memory _description, uint256 _duration, bytes memory _operation, uint256 _governanceChainId, uint256[] memory _targetChains, uint256 _timelockDelay) external returns (uint256)"
];
// Используем общий ABI
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
const dle = new ethers.Contract(dleAddress, DLE_ABI, signer);
// Создаем предложение
const tx = await dle.createProposal(
@@ -162,14 +246,111 @@ export async function voteForProposal(dleAddress, proposalId, support) {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// ABI для голосования
const dleAbi = [
"function vote(uint256 _proposalId, bool _support) external"
];
// Используем общий ABI
let dle = new ethers.Contract(dleAddress, DLE_ABI, signer);
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
// Дополнительная диагностика перед голосованием
try {
console.log('🔍 [VOTE DEBUG] Проверяем состояние предложения...');
const proposalState = await dle.getProposalState(proposalId);
console.log('🔍 [VOTE DEBUG] Состояние предложения:', proposalState);
// Проверяем, можно ли голосовать (состояние должно быть 0 = Pending)
if (Number(proposalState) !== 0) {
throw new Error(`Предложение в состоянии ${proposalState}, голосование невозможно`);
}
console.log('🔍 [VOTE DEBUG] Предложение в правильном состоянии для голосования');
// Проверяем сеть голосования
try {
const proposal = await dle.proposals(proposalId);
const currentChainId = await dle.getCurrentChainId();
const governanceChainId = proposal.governanceChainId;
console.log('🔍 [VOTE DEBUG] Текущая сеть контракта:', currentChainId.toString());
console.log('🔍 [VOTE DEBUG] Сеть голосования предложения:', governanceChainId.toString());
if (currentChainId.toString() !== governanceChainId.toString()) {
console.log('🔄 [VOTE DEBUG] Неправильная сеть! Пытаемся переключиться...');
// Пытаемся переключить сеть
const switched = await switchToVotingNetwork(governanceChainId.toString());
if (switched) {
console.log('✅ [VOTE DEBUG] Сеть успешно переключена, переподключаемся к контракту...');
// Определяем правильный адрес контракта для сети голосования
let correctContractAddress = dleAddress;
// Если контракт развернут в другой сети, нужно найти контракт в нужной сети
if (currentChainId.toString() !== governanceChainId.toString()) {
console.log('🔍 [VOTE DEBUG] Ищем контракт в сети голосования...');
try {
// Получаем информацию о мультичейн развертывании из БД
const response = await fetch('/api/dle-core/get-multichain-contracts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
originalContract: dleAddress,
targetChainId: governanceChainId.toString()
})
});
if (response.ok) {
const data = await response.json();
if (data.success && data.contractAddress) {
correctContractAddress = data.contractAddress;
console.log('🔍 [VOTE DEBUG] Найден контракт в сети голосования:', correctContractAddress);
} else {
console.warn('⚠️ [VOTE DEBUG] Контракт в сети голосования не найден, используем исходный');
}
} else {
console.warn('⚠️ [VOTE DEBUG] Ошибка получения контракта из БД, используем исходный');
}
} catch (error) {
console.warn('⚠️ [VOTE DEBUG] Ошибка поиска контракта, используем исходный:', error.message);
}
}
// Переподключаемся к контракту в новой сети
const newProvider = new ethers.BrowserProvider(window.ethereum);
const newSigner = await newProvider.getSigner();
dle = new ethers.Contract(correctContractAddress, DLE_ABI, newSigner);
// Проверяем, что теперь все корректно
const newCurrentChainId = await dle.getCurrentChainId();
console.log('🔍 [VOTE DEBUG] Новая текущая сеть контракта:', newCurrentChainId.toString());
if (newCurrentChainId.toString() === governanceChainId.toString()) {
console.log('✅ [VOTE DEBUG] Сеть для голосования теперь корректна');
} else {
throw new Error(`Не удалось переключиться на правильную сеть. Текущая: ${newCurrentChainId}, требуется: ${governanceChainId}`);
}
} else {
throw new Error(`Неправильная сеть! Контракт в сети ${currentChainId}, а голосование должно быть в сети ${governanceChainId}. Переключите кошелек вручную.`);
}
} else {
console.log('🔍 [VOTE DEBUG] Сеть для голосования корректна');
}
// Проверяем право голоса
const votingPower = await dle.getPastVotes(signer.address, proposal.snapshotTimepoint);
console.log('🔍 [VOTE DEBUG] Право голоса:', votingPower.toString());
if (votingPower === 0n) {
throw new Error('У пользователя нет права голоса (votingPower = 0)');
}
console.log('🔍 [VOTE DEBUG] У пользователя есть право голоса');
} catch (votingPowerError) {
console.warn('⚠️ [VOTE DEBUG] Не удалось проверить право голоса (продолжаем):', votingPowerError.message);
}
} catch (debugError) {
console.warn('⚠️ [VOTE DEBUG] Ошибка диагностики (продолжаем):', debugError.message);
}
// Голосуем за предложение
console.log('🗳️ [VOTE] Отправляем транзакцию голосования...');
const tx = await dle.vote(proposalId, support);
// Ждем подтверждения транзакции
@@ -182,10 +363,40 @@ export async function voteForProposal(dleAddress, proposalId, support) {
blockNumber: receipt.blockNumber
};
} catch (error) {
console.error('Ошибка голосования:', error);
throw error;
}
} catch (error) {
console.error('Ошибка голосования:', error);
// Детальная диагностика ошибки
if (error.code === 'CALL_EXCEPTION' && error.data) {
console.error('🔍 [ERROR DEBUG] Детали ошибки:', {
code: error.code,
data: error.data,
reason: error.reason,
action: error.action
});
// Расшифровка кода ошибки
if (error.data === '0x2eaf0f6d') {
console.error('❌ [ERROR DEBUG] Ошибка: ErrWrongChain - неправильная сеть для голосования');
} else if (error.data === '0xe7005635') {
console.error('❌ [ERROR DEBUG] Ошибка: ErrAlreadyVoted - пользователь уже голосовал по этому предложению');
} else if (error.data === '0x21c19873') {
console.error('❌ [ERROR DEBUG] Ошибка: ErrNoPower - у пользователя нет права голоса');
} else if (error.data === '0x834d7b85') {
console.error('❌ [ERROR DEBUG] Ошибка: ErrProposalMissing - предложение не найдено');
} else if (error.data === '0xd6792fad') {
console.error('❌ [ERROR DEBUG] Ошибка: ErrProposalEnded - время голосования истекло');
} else if (error.data === '0x2d686f73') {
console.error('❌ [ERROR DEBUG] Ошибка: ErrProposalExecuted - предложение уже исполнено');
} else if (error.data === '0xc7567e07') {
console.error('❌ [ERROR DEBUG] Ошибка: ErrProposalCanceled - предложение отменено');
} else {
console.error('❌ [ERROR DEBUG] Неизвестная ошибка:', error.data);
}
}
throw error;
}
}
/**
@@ -206,12 +417,9 @@ export async function executeProposal(dleAddress, proposalId) {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// ABI для исполнения предложения
const dleAbi = [
"function executeProposal(uint256 _proposalId) external"
];
// Используем общий ABI
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
const dle = new ethers.Contract(dleAddress, DLE_ABI, signer);
// Исполняем предложение
const tx = await dle.executeProposal(proposalId);
@@ -233,30 +441,112 @@ export async function executeProposal(dleAddress, proposalId) {
}
/**
* Создать предложение о добавлении модуля
* Отменить предложение
* @param {string} dleAddress - Адрес DLE контракта
* @param {number} proposalId - ID предложения
* @param {string} reason - Причина отмены
* @returns {Promise<Object>} - Результат отмены
*/
export async function cancelProposal(dleAddress, proposalId, reason) {
try {
// Проверяем наличие браузерного кошелька
if (!window.ethereum) {
throw new Error('Браузерный кошелек не установлен');
}
// Запрашиваем подключение к кошельку
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// Используем общий ABI
const dle = new ethers.Contract(dleAddress, DLE_ABI, signer);
// Отменяем предложение
const tx = await dle.cancelProposal(proposalId, reason);
// Ждем подтверждения транзакции
const receipt = await tx.wait();
console.log('Предложение отменено, tx hash:', tx.hash);
return {
txHash: tx.hash,
blockNumber: receipt.blockNumber
};
} catch (error) {
console.error('Ошибка отмены предложения:', error);
throw error;
}
}
/**
* Проверить баланс токенов пользователя
* @param {string} dleAddress - Адрес DLE контракта
* @param {string} userAddress - Адрес пользователя
* @returns {Promise<Object>} - Баланс токенов
*/
export async function checkTokenBalance(dleAddress, userAddress) {
try {
// Проверяем наличие браузерного кошелька
if (!window.ethereum) {
throw new Error('Браузерный кошелек не установлен');
}
// Создаем провайдер (только для чтения)
const provider = new ethers.BrowserProvider(window.ethereum);
const dle = new ethers.Contract(dleAddress, DLE_ABI, provider);
// Получаем баланс токенов
const balance = await dle.balanceOf(userAddress);
const balanceFormatted = ethers.formatEther(balance);
console.log(`💰 Баланс токенов для ${userAddress}: ${balanceFormatted}`);
return {
balance: balanceFormatted,
hasTokens: balance > 0,
rawBalance: balance.toString()
};
} catch (error) {
console.error('Ошибка проверки баланса токенов:', error);
throw error;
}
}
/**
* Создать предложение о добавлении модуля (с автоматической оплатой газа)
* @param {string} dleAddress - Адрес DLE контракта
* @param {string} description - Описание предложения
* @param {number} duration - Длительность голосования в секундах
* @param {string} moduleId - ID модуля
* @param {string} moduleAddress - Адрес модуля
* @param {number} chainId - ID цепочки для голосования
* @param {string} deploymentId - ID деплоя для получения приватного ключа (опционально)
* @returns {Promise<Object>} - Результат создания предложения
*/
export async function createAddModuleProposal(dleAddress, description, duration, moduleId, moduleAddress, chainId) {
export async function createAddModuleProposal(dleAddress, description, duration, moduleId, moduleAddress, chainId, deploymentId = null) {
try {
const response = await api.post('/blockchain/create-add-module-proposal', {
const requestData = {
dleAddress: dleAddress,
description: description,
duration: duration,
moduleId: moduleId,
moduleAddress: moduleAddress,
chainId: chainId
});
};
// Добавляем deploymentId если он передан
if (deploymentId) {
requestData.deploymentId = deploymentId;
}
const response = await api.post('/dle-modules/create-add-module-proposal', requestData);
if (response.data.success) {
return response.data.data;
} else {
throw new Error(response.data.message || 'Не удалось создать предложение о добавлении модуля');
throw new Error(response.data.error || 'Не удалось создать предложение о добавлении модуля');
}
} catch (error) {
console.error('Ошибка создания предложения о добавлении модуля:', error);
@@ -537,6 +827,7 @@ export async function getSupportedChains(dleAddress) {
* @param {string} userAddress - Адрес пользователя
* @returns {Promise<Object>} - Результат деактивации
*/
// ФУНКЦИЯ НЕ СУЩЕСТВУЕТ В КОНТРАКТЕ
export async function deactivateDLE(dleAddress, userAddress) {
try {
// Проверяем наличие браузерного кошелька
@@ -568,15 +859,9 @@ export async function deactivateDLE(dleAddress, userAddress) {
console.log('Проверка деактивации прошла успешно, выполняем деактивацию...');
// ABI для деактивации DLE
const dleAbi = [
"function deactivate() external",
"function balanceOf(address) external view returns (uint256)",
"function totalSupply() external view returns (uint256)",
"function isActive() external view returns (bool)"
];
// Используем общий ABI для деактивации
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
const dle = new ethers.Contract(dleAddress, DLE_ABI, signer);
// Дополнительные проверки перед деактивацией
const balance = await dle.balanceOf(userAddress);
@@ -640,6 +925,7 @@ export async function deactivateDLE(dleAddress, userAddress) {
* @param {number} chainId - ID цепочки для деактивации
* @returns {Promise<Object>} - Результат создания предложения
*/
// ФУНКЦИЯ НЕ СУЩЕСТВУЕТ В КОНТРАКТЕ
export async function createDeactivationProposal(dleAddress, description, duration, chainId) {
try {
// Проверяем наличие браузерного кошелька
@@ -650,11 +936,9 @@ export async function createDeactivationProposal(dleAddress, description, durati
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const dleAbi = [
"function createDeactivationProposal(string memory _description, uint256 _duration, uint256 _chainId) external returns (uint256)"
];
// Используем общий ABI для деактивации
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
const dle = new ethers.Contract(dleAddress, DLE_DEACTIVATION_ABI, signer);
const tx = await dle.createDeactivationProposal(description, duration, chainId);
const receipt = await tx.wait();
@@ -681,6 +965,7 @@ export async function createDeactivationProposal(dleAddress, description, durati
* @param {boolean} support - Поддержка предложения
* @returns {Promise<Object>} - Результат голосования
*/
// ФУНКЦИЯ НЕ СУЩЕСТВУЕТ В КОНТРАКТЕ
export async function voteDeactivationProposal(dleAddress, proposalId, support) {
try {
if (!window.ethereum) {
@@ -690,11 +975,9 @@ export async function voteDeactivationProposal(dleAddress, proposalId, support)
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const dleAbi = [
"function voteDeactivation(uint256 _proposalId, bool _support) external"
];
// Используем общий ABI для деактивации
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
const dle = new ethers.Contract(dleAddress, DLE_DEACTIVATION_ABI, signer);
const tx = await dle.voteDeactivation(proposalId, support);
const receipt = await tx.wait();
@@ -744,6 +1027,7 @@ export async function checkDeactivationProposalResult(dleAddress, proposalId) {
* @param {number} proposalId - ID предложения
* @returns {Promise<Object>} - Результат исполнения
*/
// ФУНКЦИЯ НЕ СУЩЕСТВУЕТ В КОНТРАКТЕ
export async function executeDeactivationProposal(dleAddress, proposalId) {
try {
if (!window.ethereum) {
@@ -753,11 +1037,9 @@ export async function executeDeactivationProposal(dleAddress, proposalId) {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const dleAbi = [
"function executeDeactivationProposal(uint256 _proposalId) external"
];
// Используем общий ABI для деактивации
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
const dle = new ethers.Contract(dleAddress, DLE_DEACTIVATION_ABI, signer);
const tx = await dle.executeDeactivationProposal(proposalId);
const receipt = await tx.wait();
@@ -823,12 +1105,9 @@ export async function createTransferTokensProposal(dleAddress, transferData) {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// ABI для создания предложения
const dleAbi = [
"function createProposal(string memory _description, uint256 _duration, bytes memory _operation, uint256 _governanceChainId, uint256[] memory _targetChains, uint256 _timelockDelay) external returns (uint256)"
];
// Используем общий ABI
const dle = new ethers.Contract(dleAddress, dleAbi, signer);
const dle = new ethers.Contract(dleAddress, DLE_ABI, signer);
// Кодируем операцию перевода токенов
const transferFunctionSelector = ethers.id("_transferTokens(address,uint256)");
@@ -872,4 +1151,75 @@ export async function createTransferTokensProposal(dleAddress, transferData) {
console.error('Ошибка создания предложения о переводе токенов:', error);
throw error;
}
}
/**
* Исполнить мультиконтрактное предложение во всех целевых сетях
* @param {string} dleAddress - Адрес DLE контракта
* @param {number} proposalId - ID предложения
* @param {string} userAddress - Адрес пользователя
* @returns {Promise<Object>} - Результат исполнения
*/
export async function executeMultichainProposal(dleAddress, proposalId, userAddress) {
try {
// Импортируем сервис мультиконтрактного исполнения
const {
executeInAllTargetChains,
getDeploymentId,
formatExecutionResult,
getExecutionErrors
} = await import('@/services/multichainExecutionService');
// Получаем ID деплоя
const deploymentId = await getDeploymentId(dleAddress);
// Исполняем во всех целевых сетях
const result = await executeInAllTargetChains(dleAddress, proposalId, deploymentId, userAddress);
return {
success: true,
result,
summary: formatExecutionResult(result),
errors: getExecutionErrors(result)
};
} catch (error) {
console.error('Ошибка исполнения мультиконтрактного предложения:', error);
throw error;
}
}
/**
* Исполнить мультиконтрактное предложение в конкретной сети
* @param {string} dleAddress - Адрес DLE контракта
* @param {number} proposalId - ID предложения
* @param {number} targetChainId - ID целевой сети
* @param {string} userAddress - Адрес пользователя
* @returns {Promise<Object>} - Результат исполнения
*/
export async function executeMultichainProposalInChain(dleAddress, proposalId, targetChainId, userAddress) {
try {
// Импортируем сервис мультиконтрактного исполнения
const {
executeInTargetChain,
getDeploymentId,
getChainName
} = await import('@/services/multichainExecutionService');
// Получаем ID деплоя
const deploymentId = await getDeploymentId(dleAddress);
// Исполняем в конкретной сети
const result = await executeInTargetChain(dleAddress, proposalId, targetChainId, deploymentId, userAddress);
return {
success: true,
result,
chainName: getChainName(targetChainId)
};
} catch (error) {
console.error('Ошибка исполнения мультиконтрактного предложения в сети:', error);
throw error;
}
}

View File

@@ -0,0 +1,105 @@
/**
* Конфигурации сетей блокчейна для DLE
*
* Author: HB3 Accelerator
* For licensing inquiries: info@hb3-accelerator.com
* Website: https://hb3-accelerator.com
* GitHub: https://github.com/HB3-ACCELERATOR
*/
export const SUPPORTED_NETWORKS = {
1: {
chainId: '0x1',
chainName: 'Ethereum Mainnet',
nativeCurrency: {
name: 'Ether',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://mainnet.infura.io/v3/'],
blockExplorerUrls: ['https://etherscan.io'],
},
11155111: {
chainId: '0xaa36a7',
chainName: 'Sepolia',
nativeCurrency: {
name: 'Sepolia Ether',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://sepolia.infura.io/v3/', 'https://1rpc.io/sepolia'],
blockExplorerUrls: ['https://sepolia.etherscan.io'],
},
17000: {
chainId: '0x4268',
chainName: 'Holesky',
nativeCurrency: {
name: 'Holesky Ether',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://ethereum-holesky.publicnode.com'],
blockExplorerUrls: ['https://holesky.etherscan.io'],
},
421614: {
chainId: '0x66eee',
chainName: 'Arbitrum Sepolia',
nativeCurrency: {
name: 'Arbitrum Sepolia Ether',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://sepolia-rollup.arbitrum.io/rpc'],
blockExplorerUrls: ['https://sepolia.arbiscan.io'],
},
84532: {
chainId: '0x14a34',
chainName: 'Base Sepolia',
nativeCurrency: {
name: 'Base Sepolia Ether',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://sepolia.base.org'],
blockExplorerUrls: ['https://sepolia.basescan.org'],
},
8453: {
chainId: '0x2105',
chainName: 'Base',
nativeCurrency: {
name: 'Base Ether',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://mainnet.base.org'],
blockExplorerUrls: ['https://basescan.org'],
},
};
/**
* Получить конфигурацию сети по chainId
* @param {number|string} chainId - ID сети
* @returns {Object|null} - Конфигурация сети или null
*/
export function getNetworkConfig(chainId) {
const numericChainId = typeof chainId === 'string' ? parseInt(chainId, 16) : chainId;
return SUPPORTED_NETWORKS[numericChainId] || null;
}
/**
* Получить hex представление chainId
* @param {number} chainId - ID сети
* @returns {string} - Hex представление
*/
export function getHexChainId(chainId) {
return `0x${chainId.toString(16)}`;
}
/**
* Проверить, поддерживается ли сеть
* @param {number|string} chainId - ID сети
* @returns {boolean} - Поддерживается ли сеть
*/
export function isNetworkSupported(chainId) {
return getNetworkConfig(chainId) !== null;
}

View File

@@ -0,0 +1,157 @@
/**
* Утилиты для переключения сетей блокчейна
*
* Author: HB3 Accelerator
* For licensing inquiries: info@hb3-accelerator.com
* Website: https://hb3-accelerator.com
* GitHub: https://github.com/HB3-ACCELERATOR
*/
import { getNetworkConfig, getHexChainId, isNetworkSupported } from './networkConfig.js';
/**
* Переключить сеть в MetaMask
* @param {number} targetChainId - ID целевой сети
* @returns {Promise<Object>} - Результат переключения
*/
export async function switchNetwork(targetChainId) {
try {
console.log(`🔄 [Network Switch] Переключаемся на сеть ${targetChainId}...`);
// Проверяем, поддерживается ли сеть
if (!isNetworkSupported(targetChainId)) {
throw new Error(`Сеть ${targetChainId} не поддерживается`);
}
// Проверяем наличие MetaMask
if (!window.ethereum) {
throw new Error('MetaMask не найден. Пожалуйста, установите MetaMask.');
}
// Получаем конфигурацию сети
const networkConfig = getNetworkConfig(targetChainId);
if (!networkConfig) {
throw new Error(`Конфигурация для сети ${targetChainId} не найдена`);
}
// Проверяем текущую сеть
const currentChainId = await window.ethereum.request({ method: 'eth_chainId' });
console.log(`🔄 [Network Switch] Текущая сеть: ${currentChainId}, Целевая: ${getHexChainId(targetChainId)}`);
// Если уже в нужной сети, возвращаем успех
if (currentChainId === getHexChainId(targetChainId)) {
console.log(`✅ [Network Switch] Уже в сети ${targetChainId}`);
return {
success: true,
message: `Уже в сети ${networkConfig.chainName}`,
chainId: targetChainId,
chainName: networkConfig.chainName
};
}
// Пытаемся переключиться на существующую сеть
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: getHexChainId(targetChainId) }],
});
console.log(`✅ [Network Switch] Успешно переключились на ${networkConfig.chainName}`);
return {
success: true,
message: `Переключились на ${networkConfig.chainName}`,
chainId: targetChainId,
chainName: networkConfig.chainName
};
} catch (switchError) {
// Если сеть не добавлена в MetaMask, добавляем её
if (switchError.code === 4902) {
console.log(` [Network Switch] Добавляем сеть ${networkConfig.chainName} в MetaMask...`);
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: getHexChainId(targetChainId),
chainName: networkConfig.chainName,
nativeCurrency: networkConfig.nativeCurrency,
rpcUrls: networkConfig.rpcUrls,
blockExplorerUrls: networkConfig.blockExplorerUrls,
}],
});
console.log(`✅ [Network Switch] Сеть ${networkConfig.chainName} добавлена и активирована`);
return {
success: true,
message: `Сеть ${networkConfig.chainName} добавлена и активирована`,
chainId: targetChainId,
chainName: networkConfig.chainName
};
} catch (addError) {
console.error(`❌ [Network Switch] Ошибка добавления сети:`, addError);
throw new Error(`Не удалось добавить сеть ${networkConfig.chainName}: ${addError.message}`);
}
} else {
// Другие ошибки переключения
console.error(`❌ [Network Switch] Ошибка переключения сети:`, switchError);
throw new Error(`Не удалось переключиться на ${networkConfig.chainName}: ${switchError.message}`);
}
}
} catch (error) {
console.error(`❌ [Network Switch] Ошибка:`, error);
return {
success: false,
error: error.message,
chainId: targetChainId
};
}
}
/**
* Проверить текущую сеть
* @returns {Promise<Object>} - Информация о текущей сети
*/
export async function getCurrentNetwork() {
try {
if (!window.ethereum) {
throw new Error('MetaMask не найден');
}
const chainId = await window.ethereum.request({ method: 'eth_chainId' });
const numericChainId = parseInt(chainId, 16);
const networkConfig = getNetworkConfig(numericChainId);
return {
success: true,
chainId: numericChainId,
hexChainId: chainId,
chainName: networkConfig?.chainName || 'Неизвестная сеть',
isSupported: isNetworkSupported(numericChainId)
};
} catch (error) {
console.error('❌ [Network Check] Ошибка:', error);
return {
success: false,
error: error.message
};
}
}
/**
* Получить список поддерживаемых сетей
* @returns {Array} - Список поддерживаемых сетей
*/
export function getSupportedNetworks() {
return Object.entries(SUPPORTED_NETWORKS).map(([chainId, config]) => ({
chainId: parseInt(chainId),
hexChainId: getHexChainId(parseInt(chainId)),
chainName: config.chainName,
nativeCurrency: config.nativeCurrency,
rpcUrls: config.rpcUrls,
blockExplorerUrls: config.blockExplorerUrls
}));
}

View File

@@ -37,6 +37,9 @@ class WebSocketClient {
console.log('[WebSocket] Подключение установлено');
this.isConnected = true;
this.reconnectAttempts = 0;
// Уведомляем о подключении
this.emit('connected');
};
this.ws.onmessage = (event) => {
@@ -120,6 +123,15 @@ class WebSocketClient {
}
}
// Эмиссия события
emit(event, data) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(callback => {
callback(data);
});
}
}
// Алиас для on() - для совместимости с useDeploymentWebSocket
subscribe(event, callback) {
this.on(event, callback);