feat: Добавлены формы деплоя модулей DLE с полными настройками
- Создана форма деплоя TreasuryModule с детальными настройками казны - Создана форма деплоя TimelockModule с настройками временных задержек - Создана форма деплоя DLEReader с простой конфигурацией - Добавлены маршруты и индексы для всех модулей - Исправлены пути импорта BaseLayout - Добавлены авторские права во все файлы - Улучшена архитектура деплоя модулей отдельно от основного DLE
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract FactoryDeployer {
|
||||
event Deployed(address addr, bytes32 salt);
|
||||
|
||||
function deploy(bytes32 salt, bytes memory creationCode) external payable returns (address addr) {
|
||||
require(creationCode.length != 0, "init code empty");
|
||||
// solhint-disable-next-line no-inline-assembly
|
||||
assembly {
|
||||
addr := create2(callvalue(), add(creationCode, 0x20), mload(creationCode), salt)
|
||||
}
|
||||
require(addr != address(0), "CREATE2 failed");
|
||||
emit Deployed(addr, salt);
|
||||
}
|
||||
|
||||
function computeAddress(bytes32 salt, bytes32 initCodeHash) external view returns (address) {
|
||||
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash));
|
||||
return address(uint160(uint256(hash)));
|
||||
}
|
||||
|
||||
function computeAddressWithCreationCode(bytes32 salt, bytes memory creationCode) external view returns (address) {
|
||||
bytes32 initCodeHash = keccak256(creationCode);
|
||||
bytes32 hash = keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash));
|
||||
return address(uint160(uint256(hash)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
// SPDX-License-Identifier: PROPRIETARY
|
||||
// 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
|
||||
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
/**
|
||||
* @title MockNoop
|
||||
* @dev Простой мок-контракт для тестирования FactoryDeployer
|
||||
* @dev Простой мок-контракт для тестирования
|
||||
*/
|
||||
contract MockNoop {
|
||||
uint256 public value;
|
||||
|
||||
138
backend/contracts/MockPaymaster.sol
Normal file
138
backend/contracts/MockPaymaster.sol
Normal file
@@ -0,0 +1,138 @@
|
||||
// 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/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
|
||||
// ERC-4337 интерфейсы для тестирования
|
||||
interface IPaymaster {
|
||||
function validatePaymasterUserOp(
|
||||
UserOperation calldata userOp,
|
||||
bytes32 userOpHash,
|
||||
uint256 maxCost
|
||||
) external returns (bytes memory context, uint256 validationData);
|
||||
|
||||
function postOp(
|
||||
PostOpMode mode,
|
||||
bytes calldata context,
|
||||
uint256 actualGasCost
|
||||
) external;
|
||||
}
|
||||
|
||||
struct UserOperation {
|
||||
address sender;
|
||||
uint256 nonce;
|
||||
bytes initCode;
|
||||
bytes callData;
|
||||
uint256 callGasLimit;
|
||||
uint256 verificationGasLimit;
|
||||
uint256 preVerificationGas;
|
||||
uint256 maxFeePerGas;
|
||||
uint256 maxPriorityFeePerGas;
|
||||
bytes paymasterAndData;
|
||||
bytes signature;
|
||||
}
|
||||
|
||||
enum PostOpMode {
|
||||
opSucceeded,
|
||||
opReverted,
|
||||
postOpReverted
|
||||
}
|
||||
|
||||
/**
|
||||
* @title MockPaymaster
|
||||
* @dev Mock контракт для тестирования ERC-4337 Paymaster функциональности
|
||||
*/
|
||||
contract MockPaymaster is IPaymaster {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
// События для тестирования
|
||||
event PaymasterValidated(address indexed sender, uint256 maxCost);
|
||||
event PostOpCalled(PostOpMode mode, uint256 actualGasCost);
|
||||
event TokenReceived(address indexed token, uint256 amount);
|
||||
|
||||
// Статистика для тестирования
|
||||
uint256 public totalValidations;
|
||||
uint256 public totalPostOps;
|
||||
mapping(address => uint256) public tokenReceived;
|
||||
|
||||
/**
|
||||
* @dev Валидация UserOperation (всегда успешна для тестов)
|
||||
*/
|
||||
function validatePaymasterUserOp(
|
||||
UserOperation calldata userOp,
|
||||
bytes32 userOpHash,
|
||||
uint256 maxCost
|
||||
) external override returns (bytes memory context, uint256 validationData) {
|
||||
// Используем userOpHash для избежания предупреждения
|
||||
userOpHash;
|
||||
totalValidations++;
|
||||
emit PaymasterValidated(userOp.sender, maxCost);
|
||||
|
||||
// Возвращаем пустой контекст и 0 (успешная валидация)
|
||||
return (abi.encode(userOp.sender, maxCost), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Post-operation обработка
|
||||
*/
|
||||
function postOp(
|
||||
PostOpMode mode,
|
||||
bytes calldata context,
|
||||
uint256 actualGasCost
|
||||
) external override {
|
||||
// Используем context для избежания предупреждения
|
||||
context;
|
||||
totalPostOps++;
|
||||
emit PostOpCalled(mode, actualGasCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Получить токены (для тестирования)
|
||||
*/
|
||||
function receiveTokens(address tokenAddress, uint256 amount) external payable {
|
||||
if (tokenAddress == address(0)) {
|
||||
// Нативные токены
|
||||
require(msg.value == amount, "Incorrect native amount");
|
||||
} else {
|
||||
// ERC20 токены
|
||||
IERC20(tokenAddress).safeTransferFrom(msg.sender, address(this), amount);
|
||||
}
|
||||
|
||||
tokenReceived[tokenAddress] += amount;
|
||||
emit TokenReceived(tokenAddress, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Получить нативные токены
|
||||
*/
|
||||
receive() external payable {
|
||||
tokenReceived[address(0)] += msg.value;
|
||||
emit TokenReceived(address(0), msg.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Получить статистику
|
||||
*/
|
||||
function getStats() external view returns (
|
||||
uint256 validations,
|
||||
uint256 postOps,
|
||||
uint256 nativeReceived
|
||||
) {
|
||||
return (
|
||||
totalValidations,
|
||||
totalPostOps,
|
||||
tokenReceived[address(0)]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,14 @@
|
||||
// SPDX-License-Identifier: PROPRIETARY
|
||||
// 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
|
||||
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
|
||||
@@ -16,6 +16,41 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
|
||||
// ERC-4337 интерфейсы для оплаты газа любым токеном
|
||||
interface IPaymaster {
|
||||
function validatePaymasterUserOp(
|
||||
UserOperation calldata userOp,
|
||||
bytes32 userOpHash,
|
||||
uint256 maxCost
|
||||
) external returns (bytes memory context, uint256 validationData);
|
||||
|
||||
function postOp(
|
||||
PostOpMode mode,
|
||||
bytes calldata context,
|
||||
uint256 actualGasCost
|
||||
) external;
|
||||
}
|
||||
|
||||
struct UserOperation {
|
||||
address sender;
|
||||
uint256 nonce;
|
||||
bytes initCode;
|
||||
bytes callData;
|
||||
uint256 callGasLimit;
|
||||
uint256 verificationGasLimit;
|
||||
uint256 preVerificationGas;
|
||||
uint256 maxFeePerGas;
|
||||
uint256 maxPriorityFeePerGas;
|
||||
bytes paymasterAndData;
|
||||
bytes signature;
|
||||
}
|
||||
|
||||
enum PostOpMode {
|
||||
opSucceeded,
|
||||
opReverted,
|
||||
postOpReverted
|
||||
}
|
||||
|
||||
/**
|
||||
* @title TreasuryModule
|
||||
* @dev Модуль казны для управления активами DLE
|
||||
@@ -72,6 +107,11 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
// Система экстренного останова
|
||||
bool public emergencyPaused;
|
||||
address public emergencyAdmin;
|
||||
|
||||
// ERC-4337 Paymaster для оплаты газа любым токеном
|
||||
address public paymaster;
|
||||
mapping(address => bool) public gasPaymentTokens; // Токены, которыми можно платить за газ
|
||||
mapping(address => uint256) public gasTokenRates; // Курсы обмена токенов на нативную монету
|
||||
|
||||
// События
|
||||
event TokenAdded(
|
||||
@@ -103,6 +143,10 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
);
|
||||
event EmergencyPauseToggled(bool isPaused, address admin);
|
||||
event BalanceUpdated(address indexed tokenAddress, uint256 oldBalance, uint256 newBalance);
|
||||
event PaymasterUpdated(address indexed oldPaymaster, address indexed newPaymaster);
|
||||
event GasPaymentTokenAdded(address indexed tokenAddress, uint256 rate);
|
||||
event GasPaymentTokenRemoved(address indexed tokenAddress);
|
||||
event GasPaidWithToken(address indexed tokenAddress, uint256 tokenAmount, uint256 nativeAmount);
|
||||
|
||||
// Модификаторы
|
||||
modifier onlyDLE() {
|
||||
@@ -143,6 +187,11 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
*/
|
||||
receive() external payable {
|
||||
if (msg.value > 0) {
|
||||
// Автоматически добавляем нативную монету, если её нет
|
||||
if (!supportedTokens[address(0)].isActive) {
|
||||
_addNativeToken();
|
||||
}
|
||||
|
||||
_updateTokenBalance(address(0), supportedTokens[address(0)].balance + msg.value);
|
||||
emit FundsDeposited(address(0), msg.sender, msg.value, supportedTokens[address(0)].balance);
|
||||
}
|
||||
@@ -373,6 +422,169 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
emergencyPaused = !emergencyPaused;
|
||||
emit EmergencyPauseToggled(emergencyPaused, msg.sender);
|
||||
}
|
||||
|
||||
// ===== ФУНКЦИИ ДЛЯ ОПЛАТЫ ГАЗА ЛЮБЫМ ТОКЕНОМ =====
|
||||
|
||||
/**
|
||||
* @dev Установить Paymaster для ERC-4337 (только через DLE governance)
|
||||
* @param _paymaster Адрес Paymaster контракта
|
||||
*/
|
||||
function setPaymaster(address _paymaster) external onlyDLE {
|
||||
require(_paymaster != address(0), "Paymaster cannot be zero");
|
||||
address oldPaymaster = paymaster;
|
||||
paymaster = _paymaster;
|
||||
emit PaymasterUpdated(oldPaymaster, _paymaster);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Добавить токен для оплаты газа (только через DLE governance)
|
||||
* @param tokenAddress Адрес токена
|
||||
* @param rate Курс обмена (сколько токенов за 1 нативную монету)
|
||||
*/
|
||||
function addGasPaymentToken(address tokenAddress, uint256 rate) external onlyDLE {
|
||||
require(rate > 0, "Rate must be positive");
|
||||
|
||||
// Для нативной монеты проверяем, что она активна
|
||||
if (tokenAddress == address(0)) {
|
||||
require(supportedTokens[tokenAddress].isActive, "Native token must be supported");
|
||||
} else {
|
||||
require(supportedTokens[tokenAddress].isActive, "Token must be supported");
|
||||
}
|
||||
|
||||
gasPaymentTokens[tokenAddress] = true;
|
||||
gasTokenRates[tokenAddress] = rate;
|
||||
|
||||
emit GasPaymentTokenAdded(tokenAddress, rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Удалить токен для оплаты газа (только через DLE governance)
|
||||
* @param tokenAddress Адрес токена
|
||||
*/
|
||||
function removeGasPaymentToken(address tokenAddress) external onlyDLE {
|
||||
require(gasPaymentTokens[tokenAddress], "Token not set for gas payment");
|
||||
|
||||
gasPaymentTokens[tokenAddress] = false;
|
||||
gasTokenRates[tokenAddress] = 0;
|
||||
|
||||
emit GasPaymentTokenRemoved(tokenAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Обновить курс обмена токена (только через DLE governance)
|
||||
* @param tokenAddress Адрес токена
|
||||
* @param newRate Новый курс обмена
|
||||
*/
|
||||
function updateGasTokenRate(address tokenAddress, uint256 newRate) external onlyDLE {
|
||||
require(gasPaymentTokens[tokenAddress], "Token not set for gas payment");
|
||||
require(newRate > 0, "Rate must be positive");
|
||||
|
||||
gasTokenRates[tokenAddress] = newRate;
|
||||
emit GasPaymentTokenAdded(tokenAddress, newRate); // Переиспользуем событие
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Оплатить газ токенами (через ERC-4337 Paymaster)
|
||||
* @param tokenAddress Адрес токена для оплаты (0x0 для нативной монеты)
|
||||
* @param gasAmount Количество газа для оплаты
|
||||
* @param userOp UserOperation для ERC-4337
|
||||
*/
|
||||
function payGasWithToken(
|
||||
address tokenAddress,
|
||||
uint256 gasAmount,
|
||||
UserOperation calldata userOp
|
||||
) external onlyDLE whenNotPaused nonReentrant {
|
||||
_payGasWithToken(tokenAddress, gasAmount, userOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Проверить, можно ли оплатить газ токеном
|
||||
* @param tokenAddress Адрес токена (0x0 для нативной монеты)
|
||||
* @param gasAmount Количество газа
|
||||
* @return canPay Можно ли оплатить
|
||||
* @return tokenAmount Количество токенов для оплаты
|
||||
*/
|
||||
function canPayGasWithToken(
|
||||
address tokenAddress,
|
||||
uint256 gasAmount
|
||||
) external view returns (bool canPay, uint256 tokenAmount) {
|
||||
if (!gasPaymentTokens[tokenAddress] || !supportedTokens[tokenAddress].isActive) {
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
tokenAmount = (gasAmount * gasTokenRates[tokenAddress]) / 1e18;
|
||||
canPay = supportedTokens[tokenAddress].balance >= tokenAmount;
|
||||
|
||||
return (canPay, tokenAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Проверить, можно ли оплатить газ нативной монетой
|
||||
* @param gasAmount Количество газа
|
||||
* @return canPay Можно ли оплатить
|
||||
* @return nativeAmount Количество нативной монеты для оплаты
|
||||
*/
|
||||
function canPayGasWithNative(
|
||||
uint256 gasAmount
|
||||
) external view returns (bool canPay, uint256 nativeAmount) {
|
||||
return this.canPayGasWithToken(address(0), gasAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Оплатить газ нативной монетой (упрощенная версия)
|
||||
* @param gasAmount Количество газа для оплаты
|
||||
* @param userOp UserOperation для ERC-4337
|
||||
*/
|
||||
function payGasWithNative(
|
||||
uint256 gasAmount,
|
||||
UserOperation calldata userOp
|
||||
) external onlyDLE whenNotPaused nonReentrant {
|
||||
// Используем нативную монету (address(0))
|
||||
_payGasWithToken(address(0), gasAmount, userOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Внутренняя функция для оплаты газа токенами
|
||||
* @param tokenAddress Адрес токена для оплаты (0x0 для нативной монеты)
|
||||
* @param gasAmount Количество газа для оплаты
|
||||
* @param userOp UserOperation для ERC-4337
|
||||
*/
|
||||
function _payGasWithToken(
|
||||
address tokenAddress,
|
||||
uint256 gasAmount,
|
||||
UserOperation calldata userOp
|
||||
) internal {
|
||||
require(gasPaymentTokens[tokenAddress], "Token not supported for gas payment");
|
||||
require(paymaster != address(0), "Paymaster not set");
|
||||
|
||||
TokenInfo storage tokenInfo = supportedTokens[tokenAddress];
|
||||
require(tokenInfo.isActive, "Token not active");
|
||||
|
||||
// Вычисляем количество токенов для оплаты газа
|
||||
uint256 tokenAmount = (gasAmount * gasTokenRates[tokenAddress]) / 1e18;
|
||||
require(tokenInfo.balance >= tokenAmount, "Insufficient token balance");
|
||||
|
||||
// Обновляем баланс токена
|
||||
_updateTokenBalance(tokenAddress, tokenInfo.balance - tokenAmount);
|
||||
|
||||
// Переводим токены на Paymaster (поддержка нативных и ERC20 токенов)
|
||||
if (tokenInfo.isNative) {
|
||||
// Для нативных токенов (ETH, BNB, MATIC и т.д.)
|
||||
payable(paymaster).sendValue(tokenAmount);
|
||||
} else {
|
||||
// Для ERC20 токенов
|
||||
IERC20(tokenAddress).safeTransfer(paymaster, tokenAmount);
|
||||
}
|
||||
|
||||
// Вызываем Paymaster для оплаты газа
|
||||
IPaymaster(paymaster).validatePaymasterUserOp(
|
||||
userOp,
|
||||
keccak256(abi.encode(userOp)),
|
||||
gasAmount
|
||||
);
|
||||
|
||||
emit GasPaidWithToken(tokenAddress, tokenAmount, gasAmount);
|
||||
}
|
||||
|
||||
// ===== VIEW ФУНКЦИИ =====
|
||||
|
||||
@@ -396,17 +608,28 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
function getActiveTokens() external view returns (address[] memory) {
|
||||
uint256 activeCount = 0;
|
||||
|
||||
// Считаем активные токены
|
||||
// Считаем активные токены (включая нативную монету)
|
||||
for (uint256 i = 0; i < tokenList.length; i++) {
|
||||
if (supportedTokens[tokenList[i]].isActive) {
|
||||
activeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Нативная монета всегда активна
|
||||
if (address(this).balance > 0 || supportedTokens[address(0)].isActive) {
|
||||
activeCount++;
|
||||
}
|
||||
|
||||
// Создаём массив активных токенов
|
||||
address[] memory activeTokens = new address[](activeCount);
|
||||
uint256 index = 0;
|
||||
|
||||
// Добавляем нативную монету первой, если есть баланс
|
||||
if (address(this).balance > 0 || supportedTokens[address(0)].isActive) {
|
||||
activeTokens[index] = address(0);
|
||||
index++;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < tokenList.length; i++) {
|
||||
if (supportedTokens[tokenList[i]].isActive) {
|
||||
activeTokens[index] = tokenList[i];
|
||||
@@ -421,6 +644,10 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
* @dev Получить баланс токена
|
||||
*/
|
||||
function getTokenBalance(address tokenAddress) external view returns (uint256) {
|
||||
// Для нативной монеты возвращаем реальный баланс, если токен не зарегистрирован
|
||||
if (tokenAddress == address(0) && !supportedTokens[address(0)].isActive) {
|
||||
return address(this).balance;
|
||||
}
|
||||
return supportedTokens[tokenAddress].balance;
|
||||
}
|
||||
|
||||
@@ -439,6 +666,10 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
* @dev Проверить, поддерживается ли токен
|
||||
*/
|
||||
function isTokenSupported(address tokenAddress) external view returns (bool) {
|
||||
// Нативная монета всегда поддерживается
|
||||
if (tokenAddress == address(0)) {
|
||||
return true;
|
||||
}
|
||||
return supportedTokens[tokenAddress].isActive;
|
||||
}
|
||||
|
||||
@@ -449,13 +680,25 @@ contract TreasuryModule is ReentrancyGuard {
|
||||
uint256 totalTokens,
|
||||
uint256 totalTxs,
|
||||
uint256 currentChainId,
|
||||
bool isPaused
|
||||
bool isPaused,
|
||||
address paymasterAddress,
|
||||
uint256 gasPaymentTokensCount
|
||||
) {
|
||||
// Считаем количество токенов для оплаты газа
|
||||
uint256 gasTokensCount = 0;
|
||||
for (uint256 i = 0; i < tokenList.length; i++) {
|
||||
if (gasPaymentTokens[tokenList[i]]) {
|
||||
gasTokensCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
totalTokensSupported,
|
||||
totalTransactions,
|
||||
chainId,
|
||||
emergencyPaused
|
||||
emergencyPaused,
|
||||
paymaster,
|
||||
gasTokensCount
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user