381 lines
13 KiB
Solidity
381 lines
13 KiB
Solidity
// SPDX-License-Identifier: MIT
|
||
pragma solidity ^0.8.20;
|
||
|
||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
|
||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
|
||
import "@openzeppelin/contracts/governance/Governor.sol";
|
||
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
|
||
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
|
||
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
|
||
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
|
||
import "@openzeppelin/contracts/governance/TimelockController.sol";
|
||
import "@openzeppelin/contracts/utils/Nonces.sol";
|
||
|
||
/**
|
||
* @title DLE (Digital Legal Entity)
|
||
* @dev Основной смарт-контракт DLE согласно требованиям SMART_CONTRACTS.md
|
||
*
|
||
* Функции:
|
||
* - ERC-20 токен управления с мультиподписью
|
||
* - Система голосования с кворумом
|
||
* - Казначейские функции
|
||
* - Коммуникационные функции
|
||
* - Настраиваемые таймлоки
|
||
* - Модульная система
|
||
*/
|
||
contract DLE is
|
||
ERC20Permit,
|
||
ERC20Votes,
|
||
Governor,
|
||
GovernorSettings,
|
||
GovernorCountingSimple,
|
||
GovernorVotesQuorumFraction,
|
||
GovernorTimelockControl
|
||
{
|
||
// Структура для хранения информации о DLE
|
||
struct DLEInfo {
|
||
string name;
|
||
string symbol;
|
||
string location;
|
||
string[] isicCodes;
|
||
uint256 creationTimestamp;
|
||
bool isActive;
|
||
}
|
||
|
||
// Структура для предложений
|
||
struct Proposal {
|
||
bytes operation; // Операция для выполнения
|
||
uint256[] targetChains; // Целевые сети для исполнения
|
||
uint256 timelock; // Время исполнения (timestamp)
|
||
uint256 governanceChain; // Сеть где проходит голосование
|
||
address initiator; // Инициатор предложения
|
||
bytes[] signatures; // Подписи токен-холдеров
|
||
bool executed; // Статус исполнения
|
||
uint256 quorumRequired; // Требуемый кворум
|
||
uint256 signaturesCount; // Количество собранных подписей
|
||
}
|
||
|
||
// Информация о DLE
|
||
DLEInfo public dleInfo;
|
||
|
||
// Таймлок контроллер
|
||
TimelockController public timelockController;
|
||
|
||
// Маппинг предложений
|
||
mapping(uint256 => Proposal) public proposals;
|
||
uint256 public proposalCounter;
|
||
|
||
// Настройки кворума
|
||
uint256 public quorumPercentage;
|
||
|
||
// События
|
||
event DLEInitialized(
|
||
string name,
|
||
string symbol,
|
||
string location,
|
||
address tokenAddress,
|
||
address timelockAddress,
|
||
address governorAddress
|
||
);
|
||
|
||
event TokensDistributed(address[] partners, uint256[] amounts);
|
||
event ProposalCreated(uint256 proposalId, address initiator, bytes operation);
|
||
event ProposalSigned(uint256 proposalId, address signer, uint256 signaturesCount);
|
||
event ProposalExecuted(uint256 proposalId);
|
||
event ModuleInstalled(string moduleName, address moduleAddress);
|
||
|
||
/**
|
||
* @dev Конструктор DLE
|
||
* @param _name Название DLE
|
||
* @param _symbol Символ токена управления
|
||
* @param _location Местонахождение DLE
|
||
* @param _isicCodes Коды деятельности ISIC
|
||
* @param _votingDelay Задержка голосования в блоках
|
||
* @param _votingPeriod Период голосования в блоках
|
||
* @param _proposalThreshold Порог для создания предложений
|
||
* @param _quorumPercentage Процент кворума
|
||
* @param _minTimelockDelay Минимальная задержка таймлока в секундах
|
||
*/
|
||
constructor(
|
||
string memory _name,
|
||
string memory _symbol,
|
||
string memory _location,
|
||
string[] memory _isicCodes,
|
||
uint48 _votingDelay,
|
||
uint32 _votingPeriod,
|
||
uint256 _proposalThreshold,
|
||
uint256 _quorumPercentage,
|
||
uint256 _minTimelockDelay
|
||
)
|
||
ERC20(_name, _symbol)
|
||
ERC20Permit(_name)
|
||
Governor(_name)
|
||
GovernorSettings(_votingDelay, _votingPeriod, _proposalThreshold)
|
||
GovernorVotesQuorumFraction(_quorumPercentage)
|
||
{
|
||
// Инициализируем информацию о DLE
|
||
dleInfo = DLEInfo({
|
||
name: _name,
|
||
symbol: _symbol,
|
||
location: _location,
|
||
isicCodes: _isicCodes,
|
||
creationTimestamp: block.timestamp,
|
||
isActive: true
|
||
});
|
||
|
||
// Устанавливаем кворум
|
||
quorumPercentage = _quorumPercentage;
|
||
|
||
// Создаем таймлок контроллер
|
||
address[] memory proposers = new address[](1);
|
||
address[] memory executors = new address[](1);
|
||
proposers[0] = address(this); // DLE контракт может предлагать
|
||
executors[0] = address(0); // Любой может выполнять
|
||
|
||
timelockController = new TimelockController(
|
||
_minTimelockDelay,
|
||
proposers,
|
||
executors,
|
||
address(0) // Нет админа для децентрализации
|
||
);
|
||
|
||
// Отказываемся от роли админа в таймлоке
|
||
timelockController.renounceRole(timelockController.DEFAULT_ADMIN_ROLE(), address(this));
|
||
}
|
||
|
||
/**
|
||
* @dev Распределяет начальные токены между партнерами
|
||
* @param _partners Массив адресов партнеров
|
||
* @param _amounts Массив сумм токенов для каждого партнера
|
||
*/
|
||
function distributeInitialTokens(
|
||
address[] memory _partners,
|
||
uint256[] memory _amounts
|
||
) external {
|
||
require(_partners.length == _amounts.length, "Arrays length mismatch");
|
||
require(_partners.length > 0, "Empty arrays");
|
||
|
||
uint256 totalSupply = 0;
|
||
for (uint256 i = 0; i < _partners.length; i++) {
|
||
require(_partners[i] != address(0), "Zero address");
|
||
require(_amounts[i] > 0, "Zero amount");
|
||
|
||
totalSupply += _amounts[i];
|
||
_mint(_partners[i], _amounts[i]);
|
||
}
|
||
|
||
emit TokensDistributed(_partners, _amounts);
|
||
}
|
||
|
||
/**
|
||
* @dev Создает новое предложение
|
||
* @param _operation Операция для выполнения
|
||
* @param _targetChains Целевые сети для исполнения
|
||
* @param _timelockDelay Задержка таймлока
|
||
* @return proposalId ID созданного предложения
|
||
*/
|
||
function createProposal(
|
||
bytes calldata _operation,
|
||
uint256[] calldata _targetChains,
|
||
uint256 _timelockDelay
|
||
) external onlyTokenHolder returns (uint256 proposalId) {
|
||
require(_operation.length > 0, "Empty operation");
|
||
require(_targetChains.length > 0, "No target chains");
|
||
require(_timelockDelay > 0, "Invalid timelock");
|
||
|
||
proposalId = proposalCounter++;
|
||
|
||
proposals[proposalId] = Proposal({
|
||
operation: _operation,
|
||
targetChains: _targetChains,
|
||
timelock: block.timestamp + _timelockDelay,
|
||
governanceChain: block.chainid,
|
||
initiator: msg.sender,
|
||
signatures: new bytes[](0),
|
||
executed: false,
|
||
quorumRequired: (totalSupply() * quorumPercentage) / 100,
|
||
signaturesCount: 0
|
||
});
|
||
|
||
emit ProposalCreated(proposalId, msg.sender, _operation);
|
||
return proposalId;
|
||
}
|
||
|
||
/**
|
||
* @dev Подписывает предложение
|
||
* @param _proposalId ID предложения
|
||
*/
|
||
function signProposal(uint256 _proposalId) external onlyTokenHolder {
|
||
Proposal storage proposal = proposals[_proposalId];
|
||
require(!proposal.executed, "Proposal already executed");
|
||
require(block.timestamp < proposal.timelock, "Timelock expired");
|
||
|
||
// Проверяем, что пользователь еще не подписал
|
||
for (uint256 i = 0; i < proposal.signatures.length; i++) {
|
||
require(
|
||
proposal.signatures[i].length == 0 ||
|
||
abi.decode(proposal.signatures[i], (address)) != msg.sender,
|
||
"Already signed"
|
||
);
|
||
}
|
||
|
||
// Добавляем подпись
|
||
proposal.signatures.push(abi.encode(msg.sender));
|
||
proposal.signaturesCount += balanceOf(msg.sender);
|
||
|
||
emit ProposalSigned(_proposalId, msg.sender, proposal.signaturesCount);
|
||
}
|
||
|
||
/**
|
||
* @dev Выполняет предложение
|
||
* @param _proposalId ID предложения
|
||
*/
|
||
function executeProposal(uint256 _proposalId) external {
|
||
Proposal storage proposal = proposals[_proposalId];
|
||
require(!proposal.executed, "Proposal already executed");
|
||
require(block.timestamp >= proposal.timelock, "Timelock not expired");
|
||
require(proposal.signaturesCount >= proposal.quorumRequired, "Insufficient quorum");
|
||
|
||
proposal.executed = true;
|
||
|
||
// Здесь будет логика выполнения операции
|
||
// В зависимости от типа операции
|
||
|
||
emit ProposalExecuted(_proposalId);
|
||
}
|
||
|
||
/**
|
||
* @dev Получает информацию о DLE
|
||
* @return Информация о DLE
|
||
*/
|
||
function getDLEInfo() external view returns (DLEInfo memory) {
|
||
return dleInfo;
|
||
}
|
||
|
||
/**
|
||
* @dev Получает адрес таймлока
|
||
* @return Адрес таймлок контроллера
|
||
*/
|
||
function getTimelockAddress() external view returns (address) {
|
||
return address(timelockController);
|
||
}
|
||
|
||
/**
|
||
* @dev Модификатор для проверки владения токенами
|
||
*/
|
||
modifier onlyTokenHolder() {
|
||
require(balanceOf(msg.sender) > 0, "Not a token holder");
|
||
_;
|
||
}
|
||
|
||
// Переопределения, необходимые для корректной работы токена голосования
|
||
function _update(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) {
|
||
super._update(from, to, amount);
|
||
}
|
||
|
||
function nonces(address owner) public view override(ERC20Permit, Nonces) returns (uint256) {
|
||
return super.nonces(owner);
|
||
}
|
||
|
||
// Переопределения для Governor
|
||
function votingDelay()
|
||
public
|
||
view
|
||
override(Governor, GovernorSettings)
|
||
returns (uint256)
|
||
{
|
||
return super.votingDelay();
|
||
}
|
||
|
||
function votingPeriod()
|
||
public
|
||
view
|
||
override(Governor, GovernorSettings)
|
||
returns (uint256)
|
||
{
|
||
return super.votingPeriod();
|
||
}
|
||
|
||
function quorum(uint256 blockNumber)
|
||
public
|
||
view
|
||
override(Governor, GovernorVotesQuorumFraction)
|
||
returns (uint256)
|
||
{
|
||
return super.quorum(blockNumber);
|
||
}
|
||
|
||
function state(uint256 proposalId)
|
||
public
|
||
view
|
||
override(Governor, GovernorTimelockControl)
|
||
returns (ProposalState)
|
||
{
|
||
return super.state(proposalId);
|
||
}
|
||
|
||
function proposalThreshold()
|
||
public
|
||
view
|
||
override(Governor, GovernorSettings)
|
||
returns (uint256)
|
||
{
|
||
return super.proposalThreshold();
|
||
}
|
||
|
||
function _cancel(
|
||
address[] memory targets,
|
||
uint256[] memory values,
|
||
bytes[] memory calldatas,
|
||
bytes32 descriptionHash
|
||
) internal override(Governor, GovernorTimelockControl) returns (uint256) {
|
||
return super._cancel(targets, values, calldatas, descriptionHash);
|
||
}
|
||
|
||
function _executor()
|
||
internal
|
||
view
|
||
override(Governor, GovernorTimelockControl)
|
||
returns (address)
|
||
{
|
||
return super._executor();
|
||
}
|
||
|
||
function supportsInterface(bytes4 interfaceId)
|
||
public
|
||
view
|
||
override(Governor)
|
||
returns (bool)
|
||
{
|
||
return super.supportsInterface(interfaceId);
|
||
}
|
||
|
||
function proposalNeedsQueuing(uint256 proposalId)
|
||
public
|
||
view
|
||
override(Governor, GovernorTimelockControl)
|
||
returns (bool)
|
||
{
|
||
return super.proposalNeedsQueuing(proposalId);
|
||
}
|
||
|
||
function _queueOperations(
|
||
uint256 proposalId,
|
||
address[] memory targets,
|
||
uint256[] memory values,
|
||
bytes[] memory calldatas,
|
||
bytes32 descriptionHash
|
||
) internal override(Governor, GovernorTimelockControl) returns (uint48) {
|
||
return super._queueOperations(proposalId, targets, values, calldatas, descriptionHash);
|
||
}
|
||
|
||
function _executeOperations(
|
||
uint256 proposalId,
|
||
address[] memory targets,
|
||
uint256[] memory values,
|
||
bytes[] memory calldatas,
|
||
bytes32 descriptionHash
|
||
) internal override(Governor, GovernorTimelockControl) {
|
||
super._executeOperations(proposalId, targets, values, calldatas, descriptionHash);
|
||
}
|
||
} |