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

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

@@ -44,10 +44,10 @@ COPY package.json yarn.lock ./
RUN yarn config set npmRegistryServer https://registry.npmjs.org \
&& yarn config set registry https://registry.npmjs.org \
&& yarn config set network-timeout 600000 \
&& yarn install --frozen-lockfile
&& yarn install
COPY . .
EXPOSE 8000
CMD ["yarn", "run", "dev"]
CMD ["yarn", "run", "start"]

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
);

View File

@@ -77,9 +77,8 @@
"utf7": "^1.0.2",
"viem": "^2.23.15",
"winston": "^3.17.0",
"ws": "^8.18.1"
},
"devDependencies": {
"ws": "^8.18.1",
"hardhat": "^2.24.1",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.0",
"@nomicfoundation/hardhat-ethers": "^3.0.0",
"@nomicfoundation/hardhat-ignition": "^0.15.10",
@@ -87,9 +86,13 @@
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.0",
"@typechain/hardhat": "^9.0.0",
"hardhat-contract-sizer": "^2.10.1",
"hardhat-gas-reporter": "^2.2.2"
},
"devDependencies": {
"@nomicfoundation/ignition-core": "^0.15.10",
"@typechain/ethers-v6": "^0.5.0",
"@typechain/hardhat": "^9.0.0",
"@types/chai": "^4.2.0",
"@types/minimatch": "^6.0.0",
"@types/mocha": ">=9.1.0",
@@ -98,9 +101,6 @@
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.0.2",
"globals": "^16.0.0",
"hardhat": "^2.24.1",
"hardhat-contract-sizer": "^2.10.1",
"hardhat-gas-reporter": "^2.2.2",
"minimatch": "^10.0.0",
"nodemon": "^3.1.9",
"prettier": "^3.5.3",

View File

@@ -60,7 +60,7 @@ router.post('/get-proposals', async (req, res) => {
return;
}
if (rpcUrl) {
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function getSupportedChainCount() external view returns (uint256)",
"function getSupportedChainId(uint256 _index) external view returns (uint256)"
@@ -97,7 +97,7 @@ router.post('/get-proposals', async (req, res) => {
continue;
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
// ABI для чтения предложений (используем getProposalSummary для мультиконтрактов)
const dleAbi = [
@@ -369,8 +369,8 @@ router.post('/get-proposal-info', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
// ABI для чтения информации о предложении
const dleAbi = [
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
@@ -429,7 +429,7 @@ router.post('/get-proposal-info', async (req, res) => {
router.post('/get-proposal-state', async (req, res) => {
try {
const { dleAddress, proposalId } = req.body;
if (!dleAddress || proposalId === undefined) {
return res.status(400).json({
success: false,
@@ -447,7 +447,7 @@ router.post('/get-proposal-state', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function getProposalState(uint256 _proposalId) public view returns (uint8 state)"
@@ -481,7 +481,7 @@ router.post('/get-proposal-state', async (req, res) => {
router.post('/get-proposal-votes', async (req, res) => {
try {
const { dleAddress, proposalId } = req.body;
if (!dleAddress || proposalId === undefined) {
return res.status(400).json({
success: false,
@@ -499,7 +499,7 @@ router.post('/get-proposal-votes', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function checkProposalResult(uint256 _proposalId) external view returns (bool passed, bool quorumReached)",
@@ -560,7 +560,7 @@ router.post('/get-proposals-count', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function getProposalsCount() external view returns (uint256)"
@@ -611,7 +611,7 @@ router.post('/list-proposals', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)"
@@ -664,7 +664,7 @@ router.post('/get-voting-power-at', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function getVotingPowerAt(address voter, uint256 timepoint) external view returns (uint256)"
@@ -717,7 +717,7 @@ router.post('/get-quorum-at', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function getQuorumAt(uint256 timepoint) external view returns (uint256)"
@@ -772,7 +772,7 @@ router.post('/execute-proposal', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function executeProposal(uint256 _proposalId) external"
@@ -827,7 +827,7 @@ router.post('/cancel-proposal', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function cancelProposal(uint256 _proposalId, string calldata reason) external"
@@ -879,7 +879,7 @@ router.post('/get-proposals-count', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function getProposalsCount() external view returns (uint256)"
@@ -929,7 +929,7 @@ router.post('/list-proposals', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function listProposals(uint256 offset, uint256 limit) external view returns (uint256[] memory)",
@@ -1030,7 +1030,7 @@ router.post('/vote-proposal', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
const dleAbi = [
"function vote(uint256 _proposalId, bool _support) external"
@@ -1088,7 +1088,7 @@ router.post('/check-vote-status', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
// Функция hasVoted не существует в контракте DLE
console.log(`[DLE Proposals] Функция hasVoted не поддерживается в контракте DLE`);
@@ -1135,7 +1135,7 @@ router.post('/track-vote-transaction', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
// Ждем подтверждения транзакции
const receipt = await provider.waitForTransaction(txHash, 1, 60000); // 60 секунд таймаут
@@ -1193,7 +1193,7 @@ router.post('/track-execution-transaction', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
// Ждем подтверждения транзакции
const receipt = await provider.waitForTransaction(txHash, 1, 60000); // 60 секунд таймаут
@@ -1252,7 +1252,7 @@ router.post('/decode-proposal-data', async (req, res) => {
});
}
const provider = new ethers.JsonRpcProvider(await rpcProviderService.getRpcUrlByChainId(chainId));
const provider = new ethers.JsonRpcProvider(rpcUrl);
// Получаем данные транзакции
const tx = await provider.getTransaction(transactionHash);

View File

@@ -191,6 +191,42 @@ router.get('/default-params', auth.requireAuth, async (req, res, next) => {
}
});
/**
* @route DELETE /api/dle-v2/deployment/:deploymentId
* @desc Удалить DLE v2 по deployment ID
* @access Private (только для авторизованных пользователей с ролью admin)
*/
router.delete('/deployment/:deploymentId', auth.requireAuth, auth.requireAdmin, async (req, res, next) => {
try {
const { deploymentId } = req.params;
logger.info(`Получен запрос на удаление DLE v2 с deployment ID: ${deploymentId}`);
// Удаляем запись из базы данных
const deleted = await unifiedDeploymentService.deleteDeployParams(deploymentId);
if (!deleted) {
return res.status(404).json({
success: false,
message: `DLE v2 с deployment ID ${deploymentId} не найдено`
});
}
logger.info(`DLE v2 с deployment ID ${deploymentId} успешно удалено`);
res.json({
success: true,
message: `DLE v2 с deployment ID ${deploymentId} успешно удалено`
});
} catch (error) {
logger.error('Ошибка при удалении DLE v2 по deployment ID:', error);
res.status(500).json({
success: false,
message: error.message || 'Произошла ошибка при удалении DLE v2'
});
}
});
/**
* @route DELETE /api/dle-v2/:dleAddress
* @desc Удалить DLE v2 по адресу

View File

@@ -56,10 +56,14 @@ function formatABI(abi) {
// Функции
functions.forEach(func => {
const inputs = func.inputs.map(input => `${input.type} ${input.name}`).join(', ');
const inputs = func.inputs.map(input => {
// Если имя параметра пустое, используем только тип
const paramName = input.name ? ` ${input.name}` : '';
return `${input.type}${paramName}`;
}).join(', ');
const outputs = func.outputs.map(output => output.type).join(', ');
const returns = outputs ? ` returns (${outputs})` : '';
result += ` "${func.type} ${func.name}(${inputs})${returns}",\n`;
});

View File

@@ -175,7 +175,8 @@ class UnifiedDeploymentService {
logger.info(`🚀 Запуск деплоя: ${scriptPath}`);
const child = spawn('npx', ['hardhat', 'run', scriptPath], {
const hardhatPath = path.join(__dirname, '..', 'node_modules', '.bin', 'hardhat');
const child = spawn(hardhatPath, ['run', scriptPath], {
cwd: path.join(__dirname, '..'),
env: {
...process.env,
@@ -378,6 +379,15 @@ class UnifiedDeploymentService {
return await this.deployParamsService.getAllDeployments();
}
/**
* Удаляет параметры деплоя по deploymentId
* @param {string} deploymentId - ID деплоя
* @returns {boolean} - Успешность удаления
*/
async deleteDeployParams(deploymentId) {
return await this.deployParamsService.deleteDeployParams(deploymentId);
}
/**
* Получает все DLE из файлов (для совместимости)
* @returns {Array} - Список DLE

View File

@@ -31,11 +31,6 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
"@cfworker/json-schema@^4.0.2":
version "4.1.1"
resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6"
integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@@ -494,7 +489,7 @@
resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c"
integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==
"@langchain/community@^0.3.56":
"@langchain/community@^0.3.34":
version "0.3.59"
resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.3.59.tgz#9c64d0e08b69436845ba5ca4afb510c26dae1f32"
integrity sha512-lYoVFC9wArWMXaixDgIadTE22jk4ZYAvSHHmwaMRagkGr5f4kyqMeJ83UUeW76XPx2cBy2fRSO+acSgqSuWE6A==
@@ -510,25 +505,24 @@
uuid "^10.0.0"
zod "^3.25.32"
"@langchain/core@^0.3.80":
version "0.3.80"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.80.tgz#c494a6944e53ab28bf32dc531e257b17cfc8f797"
integrity sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA==
"@langchain/core@0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.0.tgz#52bcf9d0bc480d2b2a456ee4aa8aed1cce6f6aba"
integrity sha512-uYuozr9cHpm+Aat6RdheGWAiJ2GEmb/N33FCbHlN/+vKTwRmaju2F5pZi2CioK9kQwrQZVNydCbgaZm1c6ry6w==
dependencies:
"@cfworker/json-schema" "^4.0.2"
ansi-styles "^5.0.0"
camelcase "6"
decamelize "1.2.0"
js-tiktoken "^1.0.12"
langsmith "^0.3.67"
langsmith "^0.1.43"
mustache "^4.2.0"
p-queue "^6.6.2"
p-retry "4"
uuid "^10.0.0"
zod "^3.25.32"
zod "^3.22.4"
zod-to-json-schema "^3.22.3"
"@langchain/ollama@^0.2.4":
"@langchain/ollama@^0.2.0":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@langchain/ollama/-/ollama-0.2.4.tgz#91c2108015e018f1dcae1207c8bc44da0cf047fa"
integrity sha512-XThDrZurNPcUO6sasN13rkes1aGgu5gWAtDkkyIGT3ZeMOvrYgPKGft+bbhvsigTIH9C01TfPzrSp8LAmvHIjA==
@@ -1745,9 +1739,9 @@ base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1:
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
basic-ftp@^5.0.2:
version "5.0.5"
resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0"
integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==
version "5.1.0"
resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.1.0.tgz#00eb8128ce536aa697c45716c739bf38e8d890f5"
integrity sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
@@ -2233,6 +2227,11 @@ command-line-usage@^6.1.0:
table-layout "^1.0.2"
typical "^5.2.0"
commander@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
commander@^8.1.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
@@ -4588,7 +4587,7 @@ kuler@^2.0.0:
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
"langchain@>=0.2.3 <0.3.0 || >=0.3.4 <0.4.0", langchain@^0.3.37:
"langchain@>=0.2.3 <0.3.0 || >=0.3.4 <0.4.0", langchain@^0.3.19:
version "0.3.37"
resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.3.37.tgz#6931ee5af763a6df35c0ac467eab028ba0ad17de"
integrity sha512-1jPsZ6xsxkcQPUvqRjvfuOLwZLLyt49hzcOK7OYAJovIkkOxd5gzK4Yw6giPUQ8g4XHyvULNlWBz+subdkcokw==
@@ -4605,6 +4604,18 @@ kuler@^2.0.0:
yaml "^2.2.1"
zod "^3.25.32"
langsmith@^0.1.43:
version "0.1.68"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.1.68.tgz#848332e822fe5e6734a07f1c36b6530cc1798afb"
integrity sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ==
dependencies:
"@types/uuid" "^10.0.0"
commander "^10.0.1"
p-queue "^6.6.2"
p-retry "4"
semver "^7.6.3"
uuid "^10.0.0"
langsmith@^0.3.67:
version "0.3.87"
resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.87.tgz#f1c991c93a5d4d226a31671be7e4443b4b8673b1"
@@ -5150,14 +5161,14 @@ nodemailer@7.0.11:
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-7.0.11.tgz#5f7b06afaec20073cff36bea92d1c7395cc3e512"
integrity sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==
nodemailer@^7.0.11:
version "7.0.12"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-7.0.12.tgz#b6b7bb05566c6c8458ee360aa30a407a478d35b7"
integrity sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==
nodemailer@^6.10.0:
version "6.10.1"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.10.1.tgz#cbc434c54238f83a51c07eabd04e2b3e832da623"
integrity sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==
nodemon@^3.1.11:
nodemon@^3.1.9:
version "3.1.11"
resolved "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz#04a54d1e794fbec9d8f6ffd8bf1ba9ea93a756ed"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.11.tgz#04a54d1e794fbec9d8f6ffd8bf1ba9ea93a756ed"
integrity sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==
dependencies:
chokidar "^3.5.2"
@@ -7532,11 +7543,11 @@ zip-stream@^6.0.1:
readable-stream "^4.0.0"
zod-to-json-schema@^3.22.3:
version "3.25.0"
resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz#df504c957c4fb0feff467c74d03e6aab0b013e1c"
integrity sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==
version "3.25.1"
resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz#7f24962101a439ddade2bf1aeab3c3bfec7d84ba"
integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==
zod@^3.24.1, zod@^3.25.32:
zod@^3.22.4, zod@^3.24.1, zod@^3.25.32:
version "3.25.76"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34"
integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==