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

This commit is contained in:
2025-08-07 20:27:24 +03:00
parent cde35ac576
commit 0a72902c37
44 changed files with 3594 additions and 1447 deletions

View File

@@ -12,6 +12,7 @@ pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
/**
* @title DLE (Digital Legal Entity)
@@ -59,36 +60,29 @@ contract DLE is ERC20, ReentrancyGuard {
mapping(uint256 => bool) chainVoteSynced; // Синхронизация голосов между цепочками
}
struct MultiSigOperation {
bytes32 operationHash;
uint256 forSignatures;
uint256 againstSignatures;
bool executed;
uint256 deadline;
address initiator;
mapping(address => bool) hasSigned;
mapping(uint256 => bool) chainSignSynced; // Синхронизация подписей между цепочками
}
// Основные настройки
DLEInfo public dleInfo;
uint256 public quorumPercentage;
uint256 public proposalCounter;
uint256 public multiSigCounter;
uint256 public currentChainId;
// Модули
mapping(bytes32 => address) public modules;
mapping(bytes32 => bool) public activeModules;
// Предложения и мультиподписи
// Предложения
mapping(uint256 => Proposal) public proposals;
mapping(uint256 => MultiSigOperation) public multiSigOperations;
// Мульти-чейн
mapping(uint256 => bool) public supportedChains;
uint256[] public supportedChainIds;
mapping(uint256 => bool) public executedProposals; // Синхронизация исполненных предложений
mapping(uint256 => bool) public executedMultiSig; // Синхронизация исполненных мультиподписей
// Merkle proofs для cross-chain синхронизации
mapping(uint256 => bytes32) public chainMerkleRoots; // chainId => merkleRoot
mapping(uint256 => mapping(uint256 => bool)) public processedProofs; // proposalId => proofHash => processed
// События
event DLEInitialized(
@@ -107,14 +101,16 @@ contract DLE is ERC20, ReentrancyGuard {
event ProposalCreated(uint256 proposalId, address initiator, string description);
event ProposalVoted(uint256 proposalId, address voter, bool support, uint256 votingPower);
event ProposalExecuted(uint256 proposalId, bytes operation);
event MultiSigOperationCreated(uint256 operationId, address initiator, bytes32 operationHash);
event MultiSigSigned(uint256 operationId, address signer, bool support, uint256 signaturePower);
event MultiSigExecuted(uint256 operationId, bytes32 operationHash);
event ModuleAdded(bytes32 moduleId, address moduleAddress);
event ModuleRemoved(bytes32 moduleId);
event CrossChainExecutionSync(uint256 proposalId, uint256 fromChainId, uint256 toChainId);
event CrossChainVoteSync(uint256 proposalId, uint256 fromChainId, uint256 toChainId);
event CrossChainMultiSigSync(uint256 operationId, uint256 fromChainId, uint256 toChainId);
event ChainAdded(uint256 chainId);
event ChainRemoved(uint256 chainId);
event ChainMerkleRootSet(uint256 chainId, bytes32 merkleRoot);
event DLEInfoUpdated(string name, string symbol, string location, string coordinates, uint256 jurisdiction, uint256 oktmo, string[] okvedCodes, uint256 kpp);
event QuorumPercentageUpdated(uint256 oldQuorumPercentage, uint256 newQuorumPercentage);
event CurrentChainIdUpdated(uint256 oldChainId, uint256 newChainId);
constructor(
DLEConfig memory config,
@@ -139,6 +135,7 @@ contract DLE is ERC20, ReentrancyGuard {
// Настраиваем поддерживаемые цепочки
for (uint256 i = 0; i < config.supportedChainIds.length; i++) {
supportedChains[config.supportedChainIds[i]] = true;
supportedChainIds.push(config.supportedChainIds[i]);
}
// Распределяем начальные токены партнерам
@@ -237,15 +234,34 @@ contract DLE is ERC20, ReentrancyGuard {
uint256 _fromChainId,
uint256 _forVotes,
uint256 _againstVotes,
bytes memory /* _proof */
bytes memory _proof
) external {
Proposal storage proposal = proposals[_proposalId];
require(proposal.id == _proposalId, "Proposal does not exist");
require(supportedChains[_fromChainId], "Chain not supported");
require(!proposal.chainVoteSynced[_fromChainId], "Already synced");
// Здесь должна быть проверка proof (для простоты пропускаем)
// В реальной реализации нужно проверять доказательство
// Проверяем доказательство cross-chain синхронизации
require(_proof.length > 0, "Proof required for cross-chain sync");
// Проверяем Merkle proof для cross-chain синхронизации
bytes32 proofHash = keccak256(abi.encodePacked(_proposalId, _fromChainId, _forVotes, _againstVotes));
require(!processedProofs[_proposalId][uint256(proofHash)], "Proof already processed");
// Проверяем, что Merkle root для цепочки установлен
bytes32 merkleRoot = chainMerkleRoots[_fromChainId];
require(merkleRoot != bytes32(0), "Merkle root not set for chain");
// Проверяем Merkle proof
bytes32[] memory proof = abi.decode(_proof, (bytes32[]));
require(MerkleProof.verify(proof, merkleRoot, proofHash), "Invalid Merkle proof");
// Отмечаем proof как обработанный
processedProofs[_proposalId][uint256(proofHash)] = true;
// Проверяем, что голоса не превышают общее количество токенов
uint256 totalVotes = _forVotes + _againstVotes;
require(totalVotes <= totalSupply(), "Votes exceed total supply");
proposal.forVotes += _forVotes;
proposal.againstVotes += _againstVotes;
@@ -281,9 +297,15 @@ contract DLE is ERC20, ReentrancyGuard {
Proposal storage proposal = proposals[_proposalId];
require(proposal.id == _proposalId, "Proposal does not exist");
require(!proposal.executed, "Proposal already executed");
require(block.timestamp >= proposal.deadline, "Voting not ended");
(bool passed, bool quorumReached) = checkProposalResult(_proposalId);
// Предложение можно выполнить если:
// 1. Дедлайн истек ИЛИ кворум достигнут
require(
block.timestamp >= proposal.deadline || quorumReached,
"Voting not ended and quorum not reached"
);
require(passed && quorumReached, "Proposal not passed");
proposal.executed = true;
@@ -294,123 +316,6 @@ contract DLE is ERC20, ReentrancyGuard {
emit ProposalExecuted(_proposalId, proposal.operation);
}
/**
* @dev Создать мультиподпись операцию
* @param _operationHash Хеш операции
* @param _duration Длительность сбора подписей
*/
function createMultiSigOperation(
bytes32 _operationHash,
uint256 _duration
) external returns (uint256) {
require(balanceOf(msg.sender) > 0, "Must hold tokens to create operation");
require(_duration > 0, "Duration must be positive");
uint256 operationId = multiSigCounter++;
MultiSigOperation storage operation = multiSigOperations[operationId];
operation.operationHash = _operationHash;
operation.forSignatures = 0;
operation.againstSignatures = 0;
operation.executed = false;
operation.deadline = block.timestamp + _duration;
operation.initiator = msg.sender;
emit MultiSigOperationCreated(operationId, msg.sender, _operationHash);
return operationId;
}
/**
* @dev Подписать мультиподпись операцию
* @param _operationId ID операции
* @param _support Поддержка операции
*/
function signMultiSigOperation(uint256 _operationId, bool _support) external nonReentrant {
MultiSigOperation storage operation = multiSigOperations[_operationId];
require(operation.operationHash != bytes32(0), "Operation does not exist");
require(block.timestamp < operation.deadline, "Signing ended");
require(!operation.executed, "Operation already executed");
require(!operation.hasSigned[msg.sender], "Already signed");
require(balanceOf(msg.sender) > 0, "No tokens to sign");
uint256 signaturePower = balanceOf(msg.sender);
operation.hasSigned[msg.sender] = true;
if (_support) {
operation.forSignatures += signaturePower;
} else {
operation.againstSignatures += signaturePower;
}
emit MultiSigSigned(_operationId, msg.sender, _support, signaturePower);
}
/**
* @dev Синхронизировать мультиподпись из другой цепочки
* @param _operationId ID операции
* @param _fromChainId ID цепочки откуда синхронизируем
* @param _forSignatures Подписи за
* @param _againstSignatures Подписи против
*/
function syncMultiSigFromChain(
uint256 _operationId,
uint256 _fromChainId,
uint256 _forSignatures,
uint256 _againstSignatures,
bytes memory /* _proof */
) external {
MultiSigOperation storage operation = multiSigOperations[_operationId];
require(operation.operationHash != bytes32(0), "Operation does not exist");
require(supportedChains[_fromChainId], "Chain not supported");
require(!operation.chainSignSynced[_fromChainId], "Already synced");
// Здесь должна быть проверка proof
// В реальной реализации нужно проверять доказательство
operation.forSignatures += _forSignatures;
operation.againstSignatures += _againstSignatures;
operation.chainSignSynced[_fromChainId] = true;
emit CrossChainMultiSigSync(_operationId, _fromChainId, currentChainId);
}
/**
* @dev Проверить результат мультиподписи
* @param _operationId ID операции
* @return passed Прошла ли операция
* @return quorumReached Достигнут ли кворум
*/
function checkMultiSigResult(uint256 _operationId) public view returns (bool passed, bool quorumReached) {
MultiSigOperation storage operation = multiSigOperations[_operationId];
require(operation.operationHash != bytes32(0), "Operation does not exist");
uint256 totalSignatures = operation.forSignatures + operation.againstSignatures;
uint256 quorumRequired = (totalSupply() * quorumPercentage) / 100;
quorumReached = totalSignatures >= quorumRequired;
passed = quorumReached && operation.forSignatures > operation.againstSignatures;
return (passed, quorumReached);
}
/**
* @dev Исполнить мультиподпись операцию
* @param _operationId ID операции
*/
function executeMultiSigOperation(uint256 _operationId) external {
MultiSigOperation storage operation = multiSigOperations[_operationId];
require(operation.operationHash != bytes32(0), "Operation does not exist");
require(!operation.executed, "Operation already executed");
require(block.timestamp >= operation.deadline, "Signing not ended");
(bool passed, bool quorumReached) = checkMultiSigResult(_operationId);
require(passed && quorumReached, "Operation not passed");
operation.executed = true;
emit MultiSigExecuted(_operationId, operation.operationHash);
}
/**
* @dev Синхронизировать исполнение из другой цепочки
* @param _proposalId ID предложения
@@ -419,18 +324,37 @@ contract DLE is ERC20, ReentrancyGuard {
function syncExecutionFromChain(
uint256 _proposalId,
uint256 _fromChainId,
bytes memory /* _proof */
bytes memory _proof
) external {
require(supportedChains[_fromChainId], "Chain not supported");
require(!executedProposals[_proposalId], "Already executed");
// Здесь должна быть проверка proof
// В реальной реализации нужно проверять доказательство
// Проверяем доказательство исполнения из другой цепочки
require(_proof.length > 0, "Proof required for cross-chain execution");
// Проверяем Merkle proof для cross-chain исполнения
bytes32 proofHash = keccak256(abi.encodePacked(_proposalId, _fromChainId, "EXECUTION"));
require(!processedProofs[_proposalId][uint256(proofHash)], "Proof already processed");
// Проверяем, что Merkle root для цепочки установлен
bytes32 merkleRoot = chainMerkleRoots[_fromChainId];
require(merkleRoot != bytes32(0), "Merkle root not set for chain");
// Проверяем Merkle proof
bytes32[] memory proof = abi.decode(_proof, (bytes32[]));
require(MerkleProof.verify(proof, merkleRoot, proofHash), "Invalid Merkle proof");
// Отмечаем proof как обработанный
processedProofs[_proposalId][uint256(proofHash)] = true;
// Проверяем, что предложение существует и не было исполнено
Proposal storage proposal = proposals[_proposalId];
require(proposal.id == _proposalId, "Proposal does not exist");
require(!proposal.executed, "Proposal already executed");
executedProposals[_proposalId] = true;
// Получаем операцию из предложения
Proposal storage proposal = proposals[_proposalId];
// Исполняем операцию из предложения
if (proposal.id == _proposalId) {
_executeOperation(proposal.operation);
}
@@ -444,9 +368,19 @@ contract DLE is ERC20, ReentrancyGuard {
* @return isAvailable Доступна ли цепочка
*/
function checkChainConnection(uint256 _chainId) public view returns (bool isAvailable) {
// В реальной реализации здесь должна быть проверка подключения
// Для примера возвращаем true для поддерживаемых цепочек
return supportedChains[_chainId];
// Проверяем, поддерживается ли цепочка
if (!supportedChains[_chainId]) {
return false;
}
// Проверяем, что Merkle root установлен для цепочки
// Это означает, что цепочка активна и готова к синхронизации
bytes32 merkleRoot = chainMerkleRoots[_chainId];
if (merkleRoot == bytes32(0)) {
return false;
}
return true;
}
/**
@@ -491,30 +425,103 @@ contract DLE is ERC20, ReentrancyGuard {
* @param _chainId ID цепочки
*/
function syncToChain(uint256 _proposalId, uint256 _chainId) internal {
// В реальной реализации здесь будет вызов cross-chain bridge
// Для примера просто эмитим событие
// Проверяем, что цепочка поддерживается
require(supportedChains[_chainId], "Chain not supported");
// Получаем информацию о предложении
Proposal storage proposal = proposals[_proposalId];
require(proposal.id == _proposalId, "Proposal does not exist");
// Проверяем, что цепочка готова к синхронизации
require(checkChainConnection(_chainId), "Chain not ready for sync");
// Создаем Merkle root для синхронизации
bytes32 syncData = keccak256(abi.encodePacked(_proposalId, currentChainId, proposal.operation));
// Обновляем Merkle root для целевой цепочки
chainMerkleRoots[_chainId] = syncData;
// Эмитим событие для cross-chain bridge
emit CrossChainExecutionSync(_proposalId, currentChainId, _chainId);
}
/**
* @dev Получить количество поддерживаемых цепочек
*/
function getSupportedChainCount() public pure returns (uint256) {
// В реальной реализации нужно хранить массив поддерживаемых цепочек
// Для примера возвращаем 4 (Ethereum, Polygon, BSC, Arbitrum)
return 4;
function getSupportedChainCount() public view returns (uint256) {
return supportedChainIds.length;
}
/**
* @dev Получить ID поддерживаемой цепочки по индексу
* @param _index Индекс цепочки
*/
function getSupportedChainId(uint256 _index) public pure returns (uint256) {
if (_index == 0) return 1; // Ethereum
if (_index == 1) return 137; // Polygon
if (_index == 2) return 56; // BSC
if (_index == 3) return 42161; // Arbitrum
revert("Invalid chain index");
function getSupportedChainId(uint256 _index) public view returns (uint256) {
require(_index < supportedChainIds.length, "Invalid chain index");
return supportedChainIds[_index];
}
/**
* @dev Добавить поддерживаемую цепочку (только для владельцев токенов)
* @param _chainId ID цепочки
*/
function addSupportedChain(uint256 _chainId) external {
require(balanceOf(msg.sender) > 0, "Must hold tokens to add chain");
require(!supportedChains[_chainId], "Chain already supported");
require(_chainId != currentChainId, "Cannot add current chain");
supportedChains[_chainId] = true;
supportedChainIds.push(_chainId);
emit ChainAdded(_chainId);
}
/**
* @dev Удалить поддерживаемую цепочку (только для владельцев токенов)
* @param _chainId ID цепочки
*/
function removeSupportedChain(uint256 _chainId) external {
require(balanceOf(msg.sender) > 0, "Must hold tokens to remove chain");
require(supportedChains[_chainId], "Chain not supported");
require(_chainId != currentChainId, "Cannot remove current chain");
supportedChains[_chainId] = false;
// Удаляем из массива
for (uint256 i = 0; i < supportedChainIds.length; i++) {
if (supportedChainIds[i] == _chainId) {
supportedChainIds[i] = supportedChainIds[supportedChainIds.length - 1];
supportedChainIds.pop();
break;
}
}
// Очищаем Merkle root для цепочки
delete chainMerkleRoots[_chainId];
emit ChainRemoved(_chainId);
}
/**
* @dev Установить Merkle root для цепочки (только для владельцев токенов)
* @param _chainId ID цепочки
* @param _merkleRoot Merkle root для цепочки
*/
function setChainMerkleRoot(uint256 _chainId, bytes32 _merkleRoot) external {
require(balanceOf(msg.sender) > 0, "Must hold tokens to set merkle root");
require(supportedChains[_chainId], "Chain not supported");
chainMerkleRoots[_chainId] = _merkleRoot;
emit ChainMerkleRootSet(_chainId, _merkleRoot);
}
/**
* @dev Получить Merkle root для цепочки
* @param _chainId ID цепочки
*/
function getChainMerkleRoot(uint256 _chainId) external view returns (bytes32) {
return chainMerkleRoots[_chainId];
}
/**
@@ -537,6 +544,27 @@ contract DLE is ERC20, ReentrancyGuard {
// Операция сжигания токенов
(address from, uint256 amount) = abi.decode(data, (address, uint256));
_burn(from, amount);
} else if (selector == bytes4(keccak256("updateDLEInfo(string,string,string,string,uint256,uint256,string[],uint256)"))) {
// Операция обновления информации DLE
(string memory name, string memory symbol, string memory location, string memory coordinates,
uint256 jurisdiction, uint256 oktmo, string[] memory okvedCodes, uint256 kpp) = abi.decode(data, (string, string, string, string, uint256, uint256, string[], uint256));
_updateDLEInfo(name, symbol, location, coordinates, jurisdiction, oktmo, okvedCodes, kpp);
} else if (selector == bytes4(keccak256("updateQuorumPercentage(uint256)"))) {
// Операция обновления процента кворума
(uint256 newQuorumPercentage) = abi.decode(data, (uint256));
_updateQuorumPercentage(newQuorumPercentage);
} else if (selector == bytes4(keccak256("updateCurrentChainId(uint256)"))) {
// Операция обновления текущей цепочки
(uint256 newChainId) = abi.decode(data, (uint256));
_updateCurrentChainId(newChainId);
} else if (selector == bytes4(keccak256("_addModule(bytes32,address)"))) {
// Операция добавления модуля
(bytes32 moduleId, address moduleAddress) = abi.decode(data, (bytes32, address));
_addModule(moduleId, moduleAddress);
} else if (selector == bytes4(keccak256("_removeModule(bytes32)"))) {
// Операция удаления модуля
(bytes32 moduleId) = abi.decode(data, (bytes32));
_removeModule(moduleId);
} else {
// Неизвестная операция
revert("Unknown operation");
@@ -544,12 +572,156 @@ contract DLE is ERC20, ReentrancyGuard {
}
/**
* @dev Добавить модуль
* @dev Обновить информацию DLE
* @param _name Новое название
* @param _symbol Новый символ
* @param _location Новое местонахождение
* @param _coordinates Новые координаты
* @param _jurisdiction Новая юрисдикция
* @param _oktmo Новый ОКТМО
* @param _okvedCodes Новые коды ОКВЭД
* @param _kpp Новый КПП
*/
function _updateDLEInfo(
string memory _name,
string memory _symbol,
string memory _location,
string memory _coordinates,
uint256 _jurisdiction,
uint256 _oktmo,
string[] memory _okvedCodes,
uint256 _kpp
) internal {
require(bytes(_name).length > 0, "Name cannot be empty");
require(bytes(_symbol).length > 0, "Symbol cannot be empty");
require(bytes(_location).length > 0, "Location cannot be empty");
require(_jurisdiction > 0, "Invalid jurisdiction");
require(_oktmo > 0, "Invalid OKTMO");
require(_kpp > 0, "Invalid KPP");
dleInfo.name = _name;
dleInfo.symbol = _symbol;
dleInfo.location = _location;
dleInfo.coordinates = _coordinates;
dleInfo.jurisdiction = _jurisdiction;
dleInfo.oktmo = _oktmo;
dleInfo.okvedCodes = _okvedCodes;
dleInfo.kpp = _kpp;
emit DLEInfoUpdated(_name, _symbol, _location, _coordinates, _jurisdiction, _oktmo, _okvedCodes, _kpp);
}
/**
* @dev Обновить процент кворума
* @param _newQuorumPercentage Новый процент кворума
*/
function _updateQuorumPercentage(uint256 _newQuorumPercentage) internal {
require(_newQuorumPercentage > 0 && _newQuorumPercentage <= 100, "Invalid quorum percentage");
uint256 oldQuorumPercentage = quorumPercentage;
quorumPercentage = _newQuorumPercentage;
emit QuorumPercentageUpdated(oldQuorumPercentage, _newQuorumPercentage);
}
/**
* @dev Обновить текущую цепочку
* @param _newChainId Новый ID цепочки
*/
function _updateCurrentChainId(uint256 _newChainId) internal {
require(supportedChains[_newChainId], "Chain not supported");
require(_newChainId != currentChainId, "Same chain ID");
uint256 oldChainId = currentChainId;
currentChainId = _newChainId;
emit CurrentChainIdUpdated(oldChainId, _newChainId);
}
/**
* @dev Создать предложение о добавлении модуля
* @param _description Описание предложения
* @param _duration Длительность голосования в секундах
* @param _moduleId ID модуля
* @param _moduleAddress Адрес модуля
* @param _chainId ID цепочки для голосования
*/
function createAddModuleProposal(
string memory _description,
uint256 _duration,
bytes32 _moduleId,
address _moduleAddress,
uint256 _chainId
) external returns (uint256) {
require(supportedChains[_chainId], "Chain not supported");
require(checkChainConnection(_chainId), "Chain not available");
require(_moduleAddress != address(0), "Zero address");
require(!activeModules[_moduleId], "Module already exists");
require(balanceOf(msg.sender) > 0, "Must hold tokens to create proposal");
uint256 proposalId = proposalCounter++;
Proposal storage proposal = proposals[proposalId];
proposal.id = proposalId;
proposal.description = _description;
proposal.deadline = block.timestamp + _duration;
proposal.initiator = msg.sender;
// Кодируем операцию добавления модуля
bytes memory operation = abi.encodeWithSelector(
bytes4(keccak256("_addModule(bytes32,address)")),
_moduleId,
_moduleAddress
);
proposal.operation = operation;
emit ProposalCreated(proposalId, msg.sender, _description);
return proposalId;
}
/**
* @dev Создать предложение об удалении модуля
* @param _description Описание предложения
* @param _duration Длительность голосования в секундах
* @param _moduleId ID модуля
* @param _chainId ID цепочки для голосования
*/
function createRemoveModuleProposal(
string memory _description,
uint256 _duration,
bytes32 _moduleId,
uint256 _chainId
) external returns (uint256) {
require(supportedChains[_chainId], "Chain not supported");
require(checkChainConnection(_chainId), "Chain not available");
require(activeModules[_moduleId], "Module does not exist");
require(balanceOf(msg.sender) > 0, "Must hold tokens to create proposal");
uint256 proposalId = proposalCounter++;
Proposal storage proposal = proposals[proposalId];
proposal.id = proposalId;
proposal.description = _description;
proposal.deadline = block.timestamp + _duration;
proposal.initiator = msg.sender;
// Кодируем операцию удаления модуля
bytes memory operation = abi.encodeWithSelector(
bytes4(keccak256("_removeModule(bytes32)")),
_moduleId
);
proposal.operation = operation;
emit ProposalCreated(proposalId, msg.sender, _description);
return proposalId;
}
/**
* @dev Добавить модуль (внутренняя функция, вызывается через кворум)
* @param _moduleId ID модуля
* @param _moduleAddress Адрес модуля
*/
function addModule(bytes32 _moduleId, address _moduleAddress) external {
require(balanceOf(msg.sender) > 0, "Must hold tokens to add module");
function _addModule(bytes32 _moduleId, address _moduleAddress) internal {
require(_moduleAddress != address(0), "Zero address");
require(!activeModules[_moduleId], "Module already exists");
@@ -560,11 +732,10 @@ contract DLE is ERC20, ReentrancyGuard {
}
/**
* @dev Удалить модуль
* @dev Удалить модуль (внутренняя функция, вызывается через кворум)
* @param _moduleId ID модуля
*/
function removeModule(bytes32 _moduleId) external {
require(balanceOf(msg.sender) > 0, "Must hold tokens to remove module");
function _removeModule(bytes32 _moduleId) internal {
require(activeModules[_moduleId], "Module does not exist");
delete modules[_moduleId];
@@ -613,4 +784,190 @@ contract DLE is ERC20, ReentrancyGuard {
// События для новых функций
event SyncCompleted(uint256 proposalId);
event DLEDeactivated(address indexed deactivatedBy, uint256 timestamp);
event DeactivationProposalCreated(uint256 proposalId, address indexed initiator, string description);
event DeactivationProposalVoted(uint256 proposalId, address indexed voter, bool support, uint256 votingPower);
event DeactivationProposalExecuted(uint256 proposalId, address indexed executedBy);
// Структура для предложения деактивации
struct DeactivationProposal {
uint256 id;
string description;
uint256 forVotes;
uint256 againstVotes;
bool executed;
uint256 deadline;
address initiator;
uint256 chainId;
mapping(address => bool) hasVoted;
}
// Предложения деактивации
mapping(uint256 => DeactivationProposal) public deactivationProposals;
uint256 public deactivationProposalCounter;
bool public isDeactivated;
/**
* @dev Создать предложение о деактивации DLE
* @param _description Описание предложения
* @param _duration Длительность голосования в секундах
* @param _chainId ID цепочки для деактивации
*/
function createDeactivationProposal(
string memory _description,
uint256 _duration,
uint256 _chainId
) external returns (uint256) {
require(!isDeactivated, "DLE already deactivated");
require(balanceOf(msg.sender) > 0, "Must hold tokens to create deactivation proposal");
require(_duration > 0, "Duration must be positive");
require(supportedChains[_chainId], "Chain not supported");
uint256 proposalId = deactivationProposalCounter++;
DeactivationProposal storage proposal = deactivationProposals[proposalId];
proposal.id = proposalId;
proposal.description = _description;
proposal.forVotes = 0;
proposal.againstVotes = 0;
proposal.executed = false;
proposal.deadline = block.timestamp + _duration;
proposal.initiator = msg.sender;
proposal.chainId = _chainId;
emit DeactivationProposalCreated(proposalId, msg.sender, _description);
return proposalId;
}
/**
* @dev Голосовать за предложение деактивации
* @param _proposalId ID предложения
* @param _support Поддержка предложения
*/
function voteDeactivation(uint256 _proposalId, bool _support) external nonReentrant {
DeactivationProposal storage proposal = deactivationProposals[_proposalId];
require(proposal.id == _proposalId, "Deactivation proposal does not exist");
require(block.timestamp < proposal.deadline, "Voting ended");
require(!proposal.executed, "Proposal already executed");
require(!proposal.hasVoted[msg.sender], "Already voted");
require(balanceOf(msg.sender) > 0, "No tokens to vote");
uint256 votingPower = balanceOf(msg.sender);
if (_support) {
proposal.forVotes += votingPower;
} else {
proposal.againstVotes += votingPower;
}
proposal.hasVoted[msg.sender] = true;
emit DeactivationProposalVoted(_proposalId, msg.sender, _support, votingPower);
}
/**
* @dev Проверить результат предложения деактивации
* @param _proposalId ID предложения
*/
function checkDeactivationProposalResult(uint256 _proposalId) public view returns (bool passed, bool quorumReached) {
DeactivationProposal storage proposal = deactivationProposals[_proposalId];
require(proposal.id == _proposalId, "Deactivation proposal does not exist");
uint256 totalVotes = proposal.forVotes + proposal.againstVotes;
uint256 totalSupply = totalSupply();
quorumReached = totalVotes >= (totalSupply * quorumPercentage) / 100;
passed = quorumReached && proposal.forVotes > proposal.againstVotes;
return (passed, quorumReached);
}
/**
* @dev Исполнить предложение деактивации
* @param _proposalId ID предложения
*/
function executeDeactivationProposal(uint256 _proposalId) external {
DeactivationProposal storage proposal = deactivationProposals[_proposalId];
require(proposal.id == _proposalId, "Deactivation proposal does not exist");
require(!proposal.executed, "Proposal already executed");
require(block.timestamp >= proposal.deadline, "Voting not ended");
(bool passed, bool quorumReached) = checkDeactivationProposalResult(_proposalId);
require(quorumReached, "Quorum not reached");
require(passed, "Proposal not passed");
proposal.executed = true;
isDeactivated = true;
dleInfo.isActive = false;
emit DeactivationProposalExecuted(_proposalId, msg.sender);
emit DLEDeactivated(msg.sender, block.timestamp);
}
/**
* @dev Деактивировать DLE напрямую (только при достижении кворума)
* Может быть вызвана только если есть активное предложение деактивации с достигнутым кворумом
*/
function deactivate() external {
require(!isDeactivated, "DLE already deactivated");
require(balanceOf(msg.sender) > 0, "Must hold tokens to deactivate DLE");
// Проверяем, есть ли активное предложение деактивации с достигнутым кворумом
bool hasValidDeactivationProposal = false;
for (uint256 i = 0; i < deactivationProposalCounter; i++) {
DeactivationProposal storage proposal = deactivationProposals[i];
if (!proposal.executed && block.timestamp >= proposal.deadline) {
(bool passed, bool quorumReached) = checkDeactivationProposalResult(i);
if (quorumReached && passed) {
hasValidDeactivationProposal = true;
proposal.executed = true;
break;
}
}
}
require(hasValidDeactivationProposal, "No valid deactivation proposal with quorum");
isDeactivated = true;
dleInfo.isActive = false;
emit DLEDeactivated(msg.sender, block.timestamp);
}
/**
* @dev Проверить, деактивирован ли DLE
*/
function isActive() external view returns (bool) {
return !isDeactivated && dleInfo.isActive;
}
/**
* @dev Получить информацию о предложении деактивации
* @param _proposalId ID предложения
*/
function getDeactivationProposal(uint256 _proposalId) external view returns (
uint256 id,
string memory description,
uint256 forVotes,
uint256 againstVotes,
bool executed,
uint256 deadline,
address initiator,
uint256 chainId
) {
DeactivationProposal storage proposal = deactivationProposals[_proposalId];
require(proposal.id == _proposalId, "Deactivation proposal does not exist");
return (
proposal.id,
proposal.description,
proposal.forVotes,
proposal.againstVotes,
proposal.executed,
proposal.deadline,
proposal.initiator,
proposal.chainId
);
}
}