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

This commit is contained in:
2025-12-29 20:03:09 +03:00
parent 32001ceacd
commit 546e92ffb2
22 changed files with 1458 additions and 305 deletions

View File

@@ -59,7 +59,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
uint256 deadline; // конец периода голосования (sec)
address initiator;
bytes operation; // операция для исполнения
uint256 governanceChainId; // сеть голосования (Single-Chain Governance)
uint256[] targetChains; // целевые сети для исполнения
uint256 snapshotTimepoint; // блок/временная точка для getPastVotes
mapping(address => bool) hasVoted;
@@ -106,7 +105,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
event ProposalExecuted(uint256 proposalId, bytes operation);
event ProposalCancelled(uint256 proposalId, string reason);
event ProposalTargetsSet(uint256 proposalId, uint256[] targetChains);
event ProposalGovernanceChainSet(uint256 proposalId, uint256 governanceChainId);
event ModuleAdded(bytes32 moduleId, address moduleAddress);
event ModuleRemoved(bytes32 moduleId);
event ProposalExecutionApprovedInChain(uint256 proposalId, uint256 chainId);
@@ -114,7 +112,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
event ChainRemoved(uint256 chainId);
event DLEInfoUpdated(string name, string symbol, string location, string coordinates, uint256 jurisdiction, string[] okvedCodes, uint256 kpp);
event QuorumPercentageUpdated(uint256 oldQuorumPercentage, uint256 newQuorumPercentage);
event TokensTransferredByGovernance(address indexed recipient, uint256 amount);
event TokensTransferredByGovernance(address indexed sender, address indexed recipient, uint256 amount);
event VotingDurationsUpdated(uint256 oldMinDuration, uint256 newMinDuration, uint256 oldMaxDuration, uint256 newMaxDuration);
event LogoURIUpdated(string oldURI, string newURI);
@@ -143,6 +141,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
error ErrNoPower();
error ErrNotReady();
error ErrNotInitiator();
error ErrUnauthorized();
error ErrLowPower();
error ErrBadTarget();
error ErrBadSig1271();
@@ -232,25 +231,22 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
emit LogoURIUpdated(old, _logoURI);
}
// Создать предложение с выбором цепочки для кворума
// Создать предложение для multi-chain голосования
function createProposal(
string memory _description,
string memory _description,
uint256 _duration,
bytes memory _operation,
uint256 _governanceChainId,
uint256[] memory _targetChains,
uint256 /* _timelockDelay */
) external returns (uint256) {
if (balanceOf(msg.sender) == 0) revert ErrNotHolder();
if (_duration < minVotingDuration) revert ErrTooShort();
if (_duration > maxVotingDuration) revert ErrTooLong();
if (!supportedChains[_governanceChainId]) revert ErrBadChain();
// _timelockDelay параметр игнорируется; timelock вынесем в отдельный модуль
return _createProposalInternal(
_description,
_duration,
_operation,
_governanceChainId,
_targetChains,
msg.sender
);
@@ -260,7 +256,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
string memory _description,
uint256 _duration,
bytes memory _operation,
uint256 _governanceChainId,
uint256[] memory _targetChains,
address _initiator
) internal returns (uint256) {
@@ -275,7 +270,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
proposal.deadline = block.timestamp + _duration;
proposal.initiator = _initiator;
proposal.operation = _operation;
proposal.governanceChainId = _governanceChainId;
// Снимок голосов: используем прошлую точку времени, чтобы getPastVotes был валиден в текущем блоке
uint256 nowClock = clock();
@@ -289,7 +283,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
allProposalIds.push(proposalId);
emit ProposalCreated(proposalId, _initiator, _description);
emit ProposalGovernanceChainSet(proposalId, _governanceChainId);
emit ProposalTargetsSet(proposalId, _targetChains);
return proposalId;
}
@@ -352,7 +345,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
proposal.executed = true;
// Исполняем операцию
_executeOperation(proposal.operation);
_executeOperation(_proposalId, proposal.operation);
emit ProposalExecuted(_proposalId, proposal.operation);
}
@@ -432,7 +425,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
if (votesFor < quorumRequired) revert ErrNoPower();
proposal.executed = true;
_executeOperation(proposal.operation);
_executeOperation(_proposalId, proposal.operation);
emit ProposalExecuted(_proposalId, proposal.operation);
emit ProposalExecutionApprovedInChain(_proposalId, block.chainid);
@@ -489,11 +482,15 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
/**
* @dev Исполнить операцию
* @param _proposalId ID предложения
* @param _operation Операция для исполнения
*/
function _executeOperation(bytes memory _operation) internal {
function _executeOperation(uint256 _proposalId, bytes memory _operation) internal {
if (_operation.length < 4) revert ErrInvalidOperation();
// Получаем информацию о предложении для доступа к initiator
Proposal storage proposal = proposals[_proposalId];
// Декодируем операцию из formата abi.encodeWithSelector
bytes4 selector;
bytes memory data;
@@ -527,10 +524,12 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
} else if (selector == bytes4(keccak256("_removeSupportedChain(uint256)"))) {
(uint256 chainIdToRemove) = abi.decode(data, (uint256));
_removeSupportedChain(chainIdToRemove);
} else if (selector == bytes4(keccak256("_transferTokens(address,uint256)"))) {
// Операция перевода токенов через governance
(address recipient, uint256 amount) = abi.decode(data, (address, uint256));
_transferTokens(recipient, amount);
} else if (selector == bytes4(keccak256("_transferTokens(address,address,uint256)"))) {
// Операция перевода токенов через governance от инициатора
(address sender, address recipient, uint256 amount) = abi.decode(data, (address, address, uint256));
// Проверяем, что sender совпадает с инициатором предложения
if (sender != proposal.initiator) revert ErrUnauthorized();
_transferTokens(sender, recipient, amount);
} else if (selector == bytes4(keccak256("_updateVotingDurations(uint256,uint256)"))) {
// Операция обновления времени голосования
(uint256 newMinDuration, uint256 newMaxDuration) = abi.decode(data, (uint256, uint256));
@@ -611,15 +610,15 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
* @param _recipient Адрес получателя
* @param _amount Количество токенов для перевода
*/
function _transferTokens(address _recipient, uint256 _amount) internal {
function _transferTokens(address _sender, address _recipient, uint256 _amount) internal {
if (_recipient == address(0)) revert ErrZeroAddress();
if (_amount == 0) revert ErrZeroAmount();
require(balanceOf(address(this)) >= _amount, "Insufficient DLE balance");
// Переводим токены от имени DLE (address(this))
_transfer(address(this), _recipient, _amount);
emit TokensTransferredByGovernance(_recipient, _amount);
require(balanceOf(_sender) >= _amount, "Insufficient token balance");
// Переводим токены от отправителя к получателю
_transfer(_sender, _recipient, _amount);
emit TokensTransferredByGovernance(_sender, _recipient, _amount);
}
/**
@@ -692,7 +691,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
_description,
_duration,
operation,
_chainId,
targets,
msg.sender
);
@@ -732,7 +730,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
_description,
_duration,
operation,
_chainId,
targets,
msg.sender
);
@@ -959,13 +956,12 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
bool canceled,
uint256 deadline,
address initiator,
uint256 governanceChainId,
uint256 snapshotTimepoint,
uint256[] memory targetChains
) {
Proposal storage p = proposals[_proposalId];
require(p.id == _proposalId, "Proposal does not exist");
return (
p.id,
p.description,
@@ -975,7 +971,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
p.canceled,
p.deadline,
p.initiator,
p.governanceChainId,
p.snapshotTimepoint,
p.targetChains
);

View File

@@ -1,4 +1,4 @@
// Sources flattened with hardhat v2.26.3 https://hardhat.org
// Sources flattened with hardhat v2.28.0 https://hardhat.org
// SPDX-License-Identifier: MIT AND PROPRIETARY
@@ -5482,7 +5482,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
uint256 deadline; // конец периода голосования (sec)
address initiator;
bytes operation; // операция для исполнения
uint256 governanceChainId; // сеть голосования (Single-Chain Governance)
uint256[] targetChains; // целевые сети для исполнения
uint256 snapshotTimepoint; // блок/временная точка для getPastVotes
mapping(address => bool) hasVoted;
@@ -5529,7 +5528,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
event ProposalExecuted(uint256 proposalId, bytes operation);
event ProposalCancelled(uint256 proposalId, string reason);
event ProposalTargetsSet(uint256 proposalId, uint256[] targetChains);
event ProposalGovernanceChainSet(uint256 proposalId, uint256 governanceChainId);
event ModuleAdded(bytes32 moduleId, address moduleAddress);
event ModuleRemoved(bytes32 moduleId);
event ProposalExecutionApprovedInChain(uint256 proposalId, uint256 chainId);
@@ -5537,7 +5535,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
event ChainRemoved(uint256 chainId);
event DLEInfoUpdated(string name, string symbol, string location, string coordinates, uint256 jurisdiction, string[] okvedCodes, uint256 kpp);
event QuorumPercentageUpdated(uint256 oldQuorumPercentage, uint256 newQuorumPercentage);
event TokensTransferredByGovernance(address indexed recipient, uint256 amount);
event TokensTransferredByGovernance(address indexed sender, address indexed recipient, uint256 amount);
event VotingDurationsUpdated(uint256 oldMinDuration, uint256 newMinDuration, uint256 oldMaxDuration, uint256 newMaxDuration);
event LogoURIUpdated(string oldURI, string newURI);
@@ -5566,6 +5564,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
error ErrNoPower();
error ErrNotReady();
error ErrNotInitiator();
error ErrUnauthorized();
error ErrLowPower();
error ErrBadTarget();
error ErrBadSig1271();
@@ -5655,25 +5654,22 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
emit LogoURIUpdated(old, _logoURI);
}
// Создать предложение с выбором цепочки для кворума
// Создать предложение для multi-chain голосования
function createProposal(
string memory _description,
string memory _description,
uint256 _duration,
bytes memory _operation,
uint256 _governanceChainId,
uint256[] memory _targetChains,
uint256 /* _timelockDelay */
) external returns (uint256) {
if (balanceOf(msg.sender) == 0) revert ErrNotHolder();
if (_duration < minVotingDuration) revert ErrTooShort();
if (_duration > maxVotingDuration) revert ErrTooLong();
if (!supportedChains[_governanceChainId]) revert ErrBadChain();
// _timelockDelay параметр игнорируется; timelock вынесем в отдельный модуль
return _createProposalInternal(
_description,
_duration,
_operation,
_governanceChainId,
_targetChains,
msg.sender
);
@@ -5683,7 +5679,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
string memory _description,
uint256 _duration,
bytes memory _operation,
uint256 _governanceChainId,
uint256[] memory _targetChains,
address _initiator
) internal returns (uint256) {
@@ -5698,7 +5693,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
proposal.deadline = block.timestamp + _duration;
proposal.initiator = _initiator;
proposal.operation = _operation;
proposal.governanceChainId = _governanceChainId;
// Снимок голосов: используем прошлую точку времени, чтобы getPastVotes был валиден в текущем блоке
uint256 nowClock = clock();
@@ -5712,7 +5706,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
allProposalIds.push(proposalId);
emit ProposalCreated(proposalId, _initiator, _description);
emit ProposalGovernanceChainSet(proposalId, _governanceChainId);
emit ProposalTargetsSet(proposalId, _targetChains);
return proposalId;
}
@@ -5775,7 +5768,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
proposal.executed = true;
// Исполняем операцию
_executeOperation(proposal.operation);
_executeOperation(_proposalId, proposal.operation);
emit ProposalExecuted(_proposalId, proposal.operation);
}
@@ -5855,7 +5848,7 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
if (votesFor < quorumRequired) revert ErrNoPower();
proposal.executed = true;
_executeOperation(proposal.operation);
_executeOperation(_proposalId, proposal.operation);
emit ProposalExecuted(_proposalId, proposal.operation);
emit ProposalExecutionApprovedInChain(_proposalId, block.chainid);
@@ -5912,11 +5905,15 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
/**
* @dev Исполнить операцию
* @param _proposalId ID предложения
* @param _operation Операция для исполнения
*/
function _executeOperation(bytes memory _operation) internal {
function _executeOperation(uint256 _proposalId, bytes memory _operation) internal {
if (_operation.length < 4) revert ErrInvalidOperation();
// Получаем информацию о предложении для доступа к initiator
Proposal storage proposal = proposals[_proposalId];
// Декодируем операцию из formата abi.encodeWithSelector
bytes4 selector;
bytes memory data;
@@ -5950,10 +5947,12 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
} else if (selector == bytes4(keccak256("_removeSupportedChain(uint256)"))) {
(uint256 chainIdToRemove) = abi.decode(data, (uint256));
_removeSupportedChain(chainIdToRemove);
} else if (selector == bytes4(keccak256("_transferTokens(address,uint256)"))) {
// Операция перевода токенов через governance
(address recipient, uint256 amount) = abi.decode(data, (address, uint256));
_transferTokens(recipient, amount);
} else if (selector == bytes4(keccak256("_transferTokens(address,address,uint256)"))) {
// Операция перевода токенов через governance от инициатора
(address sender, address recipient, uint256 amount) = abi.decode(data, (address, address, uint256));
// Проверяем, что sender совпадает с инициатором предложения
if (sender != proposal.initiator) revert ErrUnauthorized();
_transferTokens(sender, recipient, amount);
} else if (selector == bytes4(keccak256("_updateVotingDurations(uint256,uint256)"))) {
// Операция обновления времени голосования
(uint256 newMinDuration, uint256 newMaxDuration) = abi.decode(data, (uint256, uint256));
@@ -6034,15 +6033,15 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
* @param _recipient Адрес получателя
* @param _amount Количество токенов для перевода
*/
function _transferTokens(address _recipient, uint256 _amount) internal {
function _transferTokens(address _sender, address _recipient, uint256 _amount) internal {
if (_recipient == address(0)) revert ErrZeroAddress();
if (_amount == 0) revert ErrZeroAmount();
require(balanceOf(address(this)) >= _amount, "Insufficient DLE balance");
// Переводим токены от имени DLE (address(this))
_transfer(address(this), _recipient, _amount);
emit TokensTransferredByGovernance(_recipient, _amount);
require(balanceOf(_sender) >= _amount, "Insufficient token balance");
// Переводим токены от отправителя к получателю
_transfer(_sender, _recipient, _amount);
emit TokensTransferredByGovernance(_sender, _recipient, _amount);
}
/**
@@ -6115,7 +6114,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
_description,
_duration,
operation,
_chainId,
targets,
msg.sender
);
@@ -6155,7 +6153,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
_description,
_duration,
operation,
_chainId,
targets,
msg.sender
);
@@ -6382,13 +6379,12 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
bool canceled,
uint256 deadline,
address initiator,
uint256 governanceChainId,
uint256 snapshotTimepoint,
uint256[] memory targetChains
) {
Proposal storage p = proposals[_proposalId];
require(p.id == _proposalId, "Proposal does not exist");
return (
p.id,
p.description,
@@ -6398,7 +6394,6 @@ contract DLE is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard, IMultichainMeta
p.canceled,
p.deadline,
p.initiator,
p.governanceChainId,
p.snapshotTimepoint,
p.targetChains
);