440 lines
14 KiB
Solidity
440 lines
14 KiB
Solidity
// SPDX-License-Identifier: MIT
|
||
pragma solidity ^0.8.0;
|
||
|
||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
||
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
||
import "./YTAssetVault.sol";
|
||
|
||
/**
|
||
* @title YTAssetFactory
|
||
* @notice 用于批量创建和管理YT资产金库合约的工厂
|
||
* @dev UUPS可升级合约
|
||
*/
|
||
contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable {
|
||
|
||
error InvalidAddress();
|
||
error VaultNotExists();
|
||
error InvalidHardCap();
|
||
|
||
/// @custom:oz-upgrades-unsafe-allow constructor
|
||
constructor() {
|
||
_disableInitializers();
|
||
}
|
||
|
||
/// @notice YTAssetVault实现合约地址
|
||
address public vaultImplementation;
|
||
|
||
/// @notice 所有创建的vault地址列表
|
||
address[] public allVaults;
|
||
|
||
/// @notice vault地址 => 是否存在
|
||
mapping(address => bool) public isVault;
|
||
|
||
/// @notice 默认硬顶值(0表示无限制)
|
||
uint256 public defaultHardCap;
|
||
|
||
event VaultCreated(
|
||
address indexed vault,
|
||
address indexed manager,
|
||
string name,
|
||
string symbol,
|
||
uint256 hardCap,
|
||
uint256 index
|
||
);
|
||
event VaultImplementationUpdated(address indexed newImplementation);
|
||
event DefaultHardCapSet(uint256 newDefaultHardCap);
|
||
event HardCapSet(address indexed vault, uint256 newHardCap);
|
||
event PricesUpdated(address indexed vault, uint256 ytPrice);
|
||
event NextRedemptionTimeSet(address indexed vault, uint256 redemptionTime);
|
||
|
||
/**
|
||
* @notice 初始化工厂
|
||
* @param _vaultImplementation YTAssetVault实现合约地址
|
||
* @param _defaultHardCap 默认硬顶值
|
||
*/
|
||
function initialize(
|
||
address _vaultImplementation,
|
||
uint256 _defaultHardCap
|
||
) external initializer {
|
||
if (_vaultImplementation == address(0)) revert InvalidAddress();
|
||
|
||
__Ownable_init(msg.sender);
|
||
__UUPSUpgradeable_init();
|
||
|
||
vaultImplementation = _vaultImplementation;
|
||
defaultHardCap = _defaultHardCap;
|
||
}
|
||
|
||
/**
|
||
* @notice 授权升级(仅owner可调用)
|
||
* @param newImplementation 新实现合约地址
|
||
*/
|
||
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
|
||
|
||
/**
|
||
* @notice 更新YTAssetVault实现合约
|
||
* @param _newImplementation 新的实现合约地址
|
||
*/
|
||
function setVaultImplementation(address _newImplementation) external onlyOwner {
|
||
if (_newImplementation == address(0)) revert InvalidAddress();
|
||
vaultImplementation = _newImplementation;
|
||
emit VaultImplementationUpdated(_newImplementation);
|
||
}
|
||
|
||
/**
|
||
* @notice 设置默认硬顶
|
||
* @param _defaultHardCap 新的默认硬顶值
|
||
*/
|
||
function setDefaultHardCap(uint256 _defaultHardCap) external onlyOwner {
|
||
defaultHardCap = _defaultHardCap;
|
||
emit DefaultHardCapSet(_defaultHardCap);
|
||
}
|
||
|
||
/**
|
||
* @notice 创建新的YTAssetVault
|
||
* @param _name YT代币名称
|
||
* @param _symbol YT代币符号
|
||
* @param _manager 管理员地址
|
||
* @param _hardCap 硬顶限制(0表示使用默认值)
|
||
* @param _usdc USDC代币地址(传0使用默认地址)
|
||
* @param _redemptionTime 赎回时间(Unix时间戳)
|
||
* @param _initialYtPrice 初始YT价格(精度1e30,传0则使用默认值1.0)
|
||
* @param _usdcPriceFeed Chainlink USDC价格Feed地址
|
||
* @return vault 新创建的vault地址
|
||
*/
|
||
function createVault(
|
||
string memory _name,
|
||
string memory _symbol,
|
||
address _manager,
|
||
uint256 _hardCap,
|
||
address _usdc,
|
||
uint256 _redemptionTime,
|
||
uint256 _initialYtPrice,
|
||
address _usdcPriceFeed
|
||
) external onlyOwner returns (address vault) {
|
||
if (_manager == address(0)) revert InvalidAddress();
|
||
|
||
// 如果传入0,使用默认硬顶
|
||
uint256 actualHardCap = _hardCap == 0 ? defaultHardCap : _hardCap;
|
||
|
||
// 编码初始化数据
|
||
bytes memory initData = abi.encodeWithSelector(
|
||
YTAssetVault.initialize.selector,
|
||
_name,
|
||
_symbol,
|
||
_manager,
|
||
actualHardCap,
|
||
_usdc,
|
||
_redemptionTime,
|
||
_initialYtPrice,
|
||
_usdcPriceFeed
|
||
);
|
||
|
||
// 部署代理合约
|
||
vault = address(new ERC1967Proxy(vaultImplementation, initData));
|
||
|
||
// 记录vault信息
|
||
allVaults.push(vault);
|
||
isVault[vault] = true;
|
||
|
||
emit VaultCreated(
|
||
vault,
|
||
_manager,
|
||
_name,
|
||
_symbol,
|
||
actualHardCap,
|
||
allVaults.length - 1
|
||
);
|
||
}
|
||
|
||
/**
|
||
* @notice 批量创建vault
|
||
* @param _names YT代币名称数组
|
||
* @param _symbols YT代币符号数组
|
||
* @param _managers 管理员地址数组
|
||
* @param _hardCaps 硬顶数组
|
||
* @param _usdc USDC代币地址(传0使用默认地址)
|
||
* @param _redemptionTimes 赎回时间数组(Unix时间戳)
|
||
* @param _initialYtPrices 初始YT价格数组(精度1e30)
|
||
* @param _usdcPriceFeed Chainlink USDC价格Feed地址
|
||
* @return vaults 创建的vault地址数组
|
||
*/
|
||
function createVaultBatch(
|
||
string[] memory _names,
|
||
string[] memory _symbols,
|
||
address[] memory _managers,
|
||
uint256[] memory _hardCaps,
|
||
address _usdc,
|
||
uint256[] memory _redemptionTimes,
|
||
uint256[] memory _initialYtPrices,
|
||
address _usdcPriceFeed
|
||
) external onlyOwner returns (address[] memory vaults) {
|
||
require(
|
||
_names.length == _symbols.length &&
|
||
_names.length == _managers.length &&
|
||
_names.length == _hardCaps.length &&
|
||
_names.length == _redemptionTimes.length &&
|
||
_names.length == _initialYtPrices.length,
|
||
"Length mismatch"
|
||
);
|
||
|
||
vaults = new address[](_names.length);
|
||
|
||
for (uint256 i = 0; i < _names.length; i++) {
|
||
vaults[i] = this.createVault(
|
||
_names[i],
|
||
_symbols[i],
|
||
_managers[i],
|
||
_hardCaps[i],
|
||
_usdc,
|
||
_redemptionTimes[i],
|
||
_initialYtPrices[i],
|
||
_usdcPriceFeed
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 设置指定vault的硬顶
|
||
* @param _vault vault地址
|
||
* @param _hardCap 新的硬顶值
|
||
*/
|
||
function setHardCap(address _vault, uint256 _hardCap) external onlyOwner {
|
||
if (!isVault[_vault]) revert VaultNotExists();
|
||
|
||
YTAssetVault(_vault).setHardCap(_hardCap);
|
||
emit HardCapSet(_vault, _hardCap);
|
||
}
|
||
|
||
/**
|
||
* @notice 批量设置硬顶
|
||
* @param _vaults vault地址数组
|
||
* @param _hardCaps 硬顶值数组
|
||
*/
|
||
function setHardCapBatch(
|
||
address[] memory _vaults,
|
||
uint256[] memory _hardCaps
|
||
) external onlyOwner {
|
||
require(_vaults.length == _hardCaps.length, "Length mismatch");
|
||
|
||
for (uint256 i = 0; i < _vaults.length; i++) {
|
||
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
||
YTAssetVault(_vaults[i]).setHardCap(_hardCaps[i]);
|
||
emit HardCapSet(_vaults[i], _hardCaps[i]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 设置vault的管理员
|
||
* @param _vault vault地址
|
||
* @param _manager 新管理员地址
|
||
*/
|
||
function setVaultManager(address _vault, address _manager) external onlyOwner {
|
||
if (!isVault[_vault]) revert VaultNotExists();
|
||
if (_manager == address(0)) revert InvalidAddress();
|
||
|
||
YTAssetVault(_vault).setManager(_manager);
|
||
}
|
||
|
||
/**
|
||
* @notice 设置vault的下一个赎回时间
|
||
* @param _vault vault地址
|
||
* @param _nextRedemptionTime 赎回时间(Unix时间戳)
|
||
*/
|
||
function setVaultNextRedemptionTime(address _vault, uint256 _nextRedemptionTime) external onlyOwner {
|
||
if (!isVault[_vault]) revert VaultNotExists();
|
||
|
||
YTAssetVault(_vault).setNextRedemptionTime(_nextRedemptionTime);
|
||
emit NextRedemptionTimeSet(_vault, _nextRedemptionTime);
|
||
}
|
||
|
||
/**
|
||
* @notice 批量设置赎回时间
|
||
* @param _vaults vault地址数组
|
||
* @param _nextRedemptionTime 统一的赎回时间
|
||
*/
|
||
function setVaultNextRedemptionTimeBatch(
|
||
address[] memory _vaults,
|
||
uint256 _nextRedemptionTime
|
||
) external onlyOwner {
|
||
for (uint256 i = 0; i < _vaults.length; i++) {
|
||
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
||
YTAssetVault(_vaults[i]).setNextRedemptionTime(_nextRedemptionTime);
|
||
emit NextRedemptionTimeSet(_vaults[i], _nextRedemptionTime);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 暂停vault(紧急情况)
|
||
* @param _vault vault地址
|
||
*/
|
||
function pauseVault(address _vault) external onlyOwner {
|
||
if (!isVault[_vault]) revert VaultNotExists();
|
||
|
||
YTAssetVault(_vault).pause();
|
||
}
|
||
|
||
/**
|
||
* @notice 恢复vault
|
||
* @param _vault vault地址
|
||
*/
|
||
function unpauseVault(address _vault) external onlyOwner {
|
||
if (!isVault[_vault]) revert VaultNotExists();
|
||
|
||
YTAssetVault(_vault).unpause();
|
||
}
|
||
|
||
/**
|
||
* @notice 批量暂停vaults
|
||
* @param _vaults vault地址数组
|
||
*/
|
||
function pauseVaultBatch(address[] memory _vaults) external onlyOwner {
|
||
for (uint256 i = 0; i < _vaults.length; i++) {
|
||
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
||
YTAssetVault(_vaults[i]).pause();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 批量恢复vaults
|
||
* @param _vaults vault地址数组
|
||
*/
|
||
function unpauseVaultBatch(address[] memory _vaults) external onlyOwner {
|
||
for (uint256 i = 0; i < _vaults.length; i++) {
|
||
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
||
YTAssetVault(_vaults[i]).unpause();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 更新vault价格
|
||
* @param _vault vault地址
|
||
* @param _ytPrice YT价格(精度1e30)
|
||
*/
|
||
function updateVaultPrices(
|
||
address _vault,
|
||
uint256 _ytPrice
|
||
) external onlyOwner {
|
||
if (!isVault[_vault]) revert VaultNotExists();
|
||
|
||
YTAssetVault(_vault).updatePrices(_ytPrice);
|
||
emit PricesUpdated(_vault, _ytPrice);
|
||
}
|
||
|
||
/**
|
||
* @notice 批量更新价格
|
||
* @param _vaults vault地址数组
|
||
* @param _ytPrices YT价格数组(精度1e30)
|
||
*/
|
||
function updateVaultPricesBatch(
|
||
address[] memory _vaults,
|
||
uint256[] memory _ytPrices
|
||
) external onlyOwner {
|
||
require(_vaults.length == _ytPrices.length, "Length mismatch");
|
||
|
||
for (uint256 i = 0; i < _vaults.length; i++) {
|
||
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
||
YTAssetVault(_vaults[i]).updatePrices(_ytPrices[i]);
|
||
emit PricesUpdated(_vaults[i], _ytPrices[i]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 升级指定vault
|
||
* @param _vault vault地址
|
||
* @param _newImplementation 新实现地址
|
||
*/
|
||
function upgradeVault(address _vault, address _newImplementation) external onlyOwner {
|
||
if (!isVault[_vault]) revert VaultNotExists();
|
||
if (_newImplementation == address(0)) revert InvalidAddress();
|
||
|
||
YTAssetVault(_vault).upgradeToAndCall(_newImplementation, "");
|
||
}
|
||
|
||
/**
|
||
* @notice 批量升级vault
|
||
* @param _vaults vault地址数组
|
||
* @param _newImplementation 新实现地址
|
||
*/
|
||
function upgradeVaultBatch(
|
||
address[] memory _vaults,
|
||
address _newImplementation
|
||
) external onlyOwner {
|
||
if (_newImplementation == address(0)) revert InvalidAddress();
|
||
|
||
for (uint256 i = 0; i < _vaults.length; i++) {
|
||
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
||
YTAssetVault(_vaults[i]).upgradeToAndCall(_newImplementation, "");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 获取所有vault数量
|
||
*/
|
||
function getVaultCount() external view returns (uint256) {
|
||
return allVaults.length;
|
||
}
|
||
|
||
/**
|
||
* @notice 获取指定范围的vault地址
|
||
* @param _start 起始索引
|
||
* @param _end 结束索引(不包含)
|
||
*/
|
||
function getVaults(uint256 _start, uint256 _end)
|
||
external
|
||
view
|
||
returns (address[] memory vaults)
|
||
{
|
||
require(_start < _end && _end <= allVaults.length, "Invalid range");
|
||
|
||
vaults = new address[](_end - _start);
|
||
for (uint256 i = _start; i < _end; i++) {
|
||
vaults[i - _start] = allVaults[i];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 获取所有vault地址
|
||
*/
|
||
function getAllVaults() external view returns (address[] memory) {
|
||
return allVaults;
|
||
}
|
||
|
||
/**
|
||
* @notice 获取vault详细信息
|
||
* @param _vault vault地址
|
||
*/
|
||
function getVaultInfo(address _vault) external view returns (
|
||
bool exists,
|
||
uint256 totalAssets,
|
||
uint256 idleAssets,
|
||
uint256 managedAssets,
|
||
uint256 totalSupply,
|
||
uint256 hardCap,
|
||
uint256 usdcPrice,
|
||
uint256 ytPrice,
|
||
uint256 nextRedemptionTime
|
||
) {
|
||
exists = isVault[_vault];
|
||
if (!exists) return (false, 0, 0, 0, 0, 0, 0, 0, 0);
|
||
(
|
||
totalAssets,
|
||
idleAssets,
|
||
managedAssets,
|
||
totalSupply,
|
||
hardCap,
|
||
usdcPrice,
|
||
ytPrice,
|
||
nextRedemptionTime
|
||
) = YTAssetVault(_vault).getVaultInfo();
|
||
}
|
||
|
||
/**
|
||
* @dev 预留存储空间,用于未来升级时添加新的状态变量
|
||
* 50个slot = 50 * 32 bytes = 1600 bytes
|
||
*/
|
||
uint256[50] private __gap;
|
||
}
|