// SPDX-License-Identifier: PROPRIETARY AND MIT // Copyright (c) 2024-2025 Тарабанов Александр Викторович // All rights reserved. // // This software is proprietary and confidential. // Unauthorized copying, modification, or distribution is prohibited. // // For licensing inquiries: info@hb3-accelerator.com // Website: https://hb3-accelerator.com // GitHub: https://github.com/HB3-ACCELERATOR pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; /** * @title DLE (Digital Legal Entity) * @dev Основной контракт DLE с модульной архитектурой и мульти-чейн поддержкой */ contract DLE is ERC20, ReentrancyGuard { struct DLEInfo { string name; string symbol; string location; string coordinates; uint256 jurisdiction; uint256 oktmo; string[] okvedCodes; uint256 kpp; uint256 creationTimestamp; bool isActive; } struct DLEConfig { string name; string symbol; string location; string coordinates; uint256 jurisdiction; uint256 oktmo; string[] okvedCodes; uint256 kpp; uint256 quorumPercentage; address[] initialPartners; uint256[] initialAmounts; uint256[] supportedChainIds; // Поддерживаемые цепочки } struct Proposal { uint256 id; string description; uint256 forVotes; uint256 againstVotes; bool executed; uint256 deadline; address initiator; bytes operation; // Операция для исполнения mapping(address => bool) hasVoted; 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; mapping(uint256 => bool) public executedProposals; // Синхронизация исполненных предложений mapping(uint256 => bool) public executedMultiSig; // Синхронизация исполненных мультиподписей // События event DLEInitialized( string name, string symbol, string location, string coordinates, uint256 jurisdiction, uint256 oktmo, string[] okvedCodes, uint256 kpp, address tokenAddress, uint256[] supportedChainIds ); event InitialTokensDistributed(address[] partners, uint256[] amounts); 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); constructor( DLEConfig memory config, uint256 _currentChainId ) ERC20(config.name, config.symbol) { dleInfo = DLEInfo({ name: config.name, symbol: config.symbol, location: config.location, coordinates: config.coordinates, jurisdiction: config.jurisdiction, oktmo: config.oktmo, okvedCodes: config.okvedCodes, kpp: config.kpp, creationTimestamp: block.timestamp, isActive: true }); quorumPercentage = config.quorumPercentage; currentChainId = _currentChainId; // Настраиваем поддерживаемые цепочки for (uint256 i = 0; i < config.supportedChainIds.length; i++) { supportedChains[config.supportedChainIds[i]] = true; } // Распределяем начальные токены партнерам require(config.initialPartners.length == config.initialAmounts.length, "Arrays length mismatch"); require(config.initialPartners.length > 0, "No initial partners"); for (uint256 i = 0; i < config.initialPartners.length; i++) { require(config.initialPartners[i] != address(0), "Zero address"); require(config.initialAmounts[i] > 0, "Zero amount"); _mint(config.initialPartners[i], config.initialAmounts[i]); } emit InitialTokensDistributed(config.initialPartners, config.initialAmounts); emit DLEInitialized( config.name, config.symbol, config.location, config.coordinates, config.jurisdiction, config.oktmo, config.okvedCodes, config.kpp, address(this), config.supportedChainIds ); } /** * @dev Создать предложение с выбором цепочки для кворума * @param _description Описание предложения * @param _duration Длительность голосования в секундах * @param _operation Операция для исполнения * @param _governanceChainId ID цепочки для сбора голосов */ function createProposal( string memory _description, uint256 _duration, bytes memory _operation, uint256 _governanceChainId ) external returns (uint256) { require(balanceOf(msg.sender) > 0, "Must hold tokens to create proposal"); require(_duration > 0, "Duration must be positive"); require(supportedChains[_governanceChainId], "Chain not supported"); require(checkChainConnection(_governanceChainId), "Chain not available"); uint256 proposalId = proposalCounter++; Proposal storage proposal = proposals[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.operation = _operation; emit ProposalCreated(proposalId, msg.sender, _description); return proposalId; } /** * @dev Голосовать за предложение * @param _proposalId ID предложения * @param _support Поддержка предложения */ function vote(uint256 _proposalId, bool _support) external nonReentrant { Proposal storage proposal = proposals[_proposalId]; require(proposal.id == _proposalId, "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); proposal.hasVoted[msg.sender] = true; if (_support) { proposal.forVotes += votingPower; } else { proposal.againstVotes += votingPower; } emit ProposalVoted(_proposalId, msg.sender, _support, votingPower); } /** * @dev Синхронизировать голос из другой цепочки * @param _proposalId ID предложения * @param _fromChainId ID цепочки откуда синхронизируем * @param _forVotes Голоса за * @param _againstVotes Голоса против */ function syncVoteFromChain( uint256 _proposalId, uint256 _fromChainId, uint256 _forVotes, uint256 _againstVotes, 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 (для простоты пропускаем) // В реальной реализации нужно проверять доказательство proposal.forVotes += _forVotes; proposal.againstVotes += _againstVotes; proposal.chainVoteSynced[_fromChainId] = true; emit CrossChainVoteSync(_proposalId, _fromChainId, currentChainId); } /** * @dev Проверить результат предложения * @param _proposalId ID предложения * @return passed Прошло ли предложение * @return quorumReached Достигнут ли кворум */ function checkProposalResult(uint256 _proposalId) public view returns (bool passed, bool quorumReached) { Proposal storage proposal = proposals[_proposalId]; require(proposal.id == _proposalId, "Proposal does not exist"); uint256 totalVotes = proposal.forVotes + proposal.againstVotes; uint256 quorumRequired = (totalSupply() * quorumPercentage) / 100; quorumReached = totalVotes >= quorumRequired; passed = quorumReached && proposal.forVotes > proposal.againstVotes; return (passed, quorumReached); } /** * @dev Исполнить предложение * @param _proposalId ID предложения */ function executeProposal(uint256 _proposalId) external { 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); require(passed && quorumReached, "Proposal not passed"); proposal.executed = true; // Исполняем операцию _executeOperation(proposal.operation); 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 предложения * @param _fromChainId ID цепочки откуда синхронизируем */ function syncExecutionFromChain( uint256 _proposalId, uint256 _fromChainId, bytes memory /* _proof */ ) external { require(supportedChains[_fromChainId], "Chain not supported"); require(!executedProposals[_proposalId], "Already executed"); // Здесь должна быть проверка proof // В реальной реализации нужно проверять доказательство executedProposals[_proposalId] = true; // Получаем операцию из предложения Proposal storage proposal = proposals[_proposalId]; if (proposal.id == _proposalId) { _executeOperation(proposal.operation); } emit CrossChainExecutionSync(_proposalId, _fromChainId, currentChainId); } /** * @dev Проверить подключение к цепочке * @param _chainId ID цепочки * @return isAvailable Доступна ли цепочка */ function checkChainConnection(uint256 _chainId) public view returns (bool isAvailable) { // В реальной реализации здесь должна быть проверка подключения // Для примера возвращаем true для поддерживаемых цепочек return supportedChains[_chainId]; } /** * @dev Проверить все подключения перед синхронизацией * @param _proposalId ID предложения * @return allChainsReady Готовы ли все цепочки */ function checkSyncReadiness(uint256 _proposalId) public view returns (bool allChainsReady) { Proposal storage proposal = proposals[_proposalId]; require(proposal.id == _proposalId, "Proposal does not exist"); // Проверяем все поддерживаемые цепочки for (uint256 i = 0; i < getSupportedChainCount(); i++) { uint256 chainId = getSupportedChainId(i); if (!checkChainConnection(chainId)) { return false; } } return true; } /** * @dev Синхронизация только при 100% готовности * @param _proposalId ID предложения */ function syncToAllChains(uint256 _proposalId) external { require(checkSyncReadiness(_proposalId), "Not all chains ready"); // Выполняем синхронизацию во все цепочки for (uint256 i = 0; i < getSupportedChainCount(); i++) { uint256 chainId = getSupportedChainId(i); syncToChain(_proposalId, chainId); } emit SyncCompleted(_proposalId); } /** * @dev Синхронизация в конкретную цепочку * @param _proposalId ID предложения * @param _chainId ID цепочки */ function syncToChain(uint256 _proposalId, uint256 _chainId) internal { // В реальной реализации здесь будет вызов cross-chain bridge // Для примера просто эмитим событие emit CrossChainExecutionSync(_proposalId, currentChainId, _chainId); } /** * @dev Получить количество поддерживаемых цепочек */ function getSupportedChainCount() public pure returns (uint256) { // В реальной реализации нужно хранить массив поддерживаемых цепочек // Для примера возвращаем 4 (Ethereum, Polygon, BSC, Arbitrum) return 4; } /** * @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"); } /** * @dev Исполнить операцию * @param _operation Операция для исполнения */ function _executeOperation(bytes memory _operation) internal { // Декодируем операцию (bytes4 selector, bytes memory data) = abi.decode(_operation, (bytes4, bytes)); if (selector == bytes4(keccak256("transfer(address,uint256)"))) { // Операция передачи токенов (address to, uint256 amount) = abi.decode(data, (address, uint256)); _transfer(msg.sender, to, amount); } else if (selector == bytes4(keccak256("mint(address,uint256)"))) { // Операция минтинга токенов (address to, uint256 amount) = abi.decode(data, (address, uint256)); _mint(to, amount); } else if (selector == bytes4(keccak256("burn(address,uint256)"))) { // Операция сжигания токенов (address from, uint256 amount) = abi.decode(data, (address, uint256)); _burn(from, amount); } else { // Неизвестная операция revert("Unknown operation"); } } /** * @dev Добавить модуль * @param _moduleId ID модуля * @param _moduleAddress Адрес модуля */ function addModule(bytes32 _moduleId, address _moduleAddress) external { require(balanceOf(msg.sender) > 0, "Must hold tokens to add module"); require(_moduleAddress != address(0), "Zero address"); require(!activeModules[_moduleId], "Module already exists"); modules[_moduleId] = _moduleAddress; activeModules[_moduleId] = true; emit ModuleAdded(_moduleId, _moduleAddress); } /** * @dev Удалить модуль * @param _moduleId ID модуля */ function removeModule(bytes32 _moduleId) external { require(balanceOf(msg.sender) > 0, "Must hold tokens to remove module"); require(activeModules[_moduleId], "Module does not exist"); delete modules[_moduleId]; activeModules[_moduleId] = false; emit ModuleRemoved(_moduleId); } /** * @dev Получить информацию о DLE */ function getDLEInfo() external view returns (DLEInfo memory) { return dleInfo; } /** * @dev Проверить, активен ли модуль * @param _moduleId ID модуля */ function isModuleActive(bytes32 _moduleId) external view returns (bool) { return activeModules[_moduleId]; } /** * @dev Получить адрес модуля * @param _moduleId ID модуля */ function getModuleAddress(bytes32 _moduleId) external view returns (address) { return modules[_moduleId]; } /** * @dev Проверить, поддерживается ли цепочка * @param _chainId ID цепочки */ function isChainSupported(uint256 _chainId) external view returns (bool) { return supportedChains[_chainId]; } /** * @dev Получить текущий ID цепочки */ function getCurrentChainId() external view returns (uint256) { return currentChainId; } // События для новых функций event SyncCompleted(uint256 proposalId); }