// 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"; contract YTAssetFactory is Initializable, UUPSUpgradeable, OwnableUpgradeable { error InvalidAddress(); error VaultNotExists(); error InvalidHardCap(); /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } address public vaultImplementation; address[] public allVaults; mapping(address => bool) public isVault; 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); function initialize( address _vaultImplementation, uint256 _defaultHardCap ) external initializer { if (_vaultImplementation == address(0)) revert InvalidAddress(); __Ownable_init(msg.sender); __UUPSUpgradeable_init(); vaultImplementation = _vaultImplementation; defaultHardCap = _defaultHardCap; } function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} function setVaultImplementation(address _newImplementation) external onlyOwner { if (_newImplementation == address(0)) revert InvalidAddress(); vaultImplementation = _newImplementation; emit VaultImplementationUpdated(_newImplementation); } function setDefaultHardCap(uint256 _defaultHardCap) external onlyOwner { defaultHardCap = _defaultHardCap; emit DefaultHardCapSet(_defaultHardCap); } 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(); 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)); allVaults.push(vault); isVault[vault] = true; emit VaultCreated( vault, _manager, _name, _symbol, actualHardCap, allVaults.length - 1 ); } 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 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 ); } } function setHardCap(address _vault, uint256 _hardCap) external onlyOwner { if (!isVault[_vault]) revert VaultNotExists(); YTAssetVault(_vault).setHardCap(_hardCap); emit HardCapSet(_vault, _hardCap); } 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]); } } function setVaultManager(address _vault, address _manager) external onlyOwner { if (!isVault[_vault]) revert VaultNotExists(); if (_manager == address(0)) revert InvalidAddress(); YTAssetVault(_vault).setManager(_manager); } function setVaultNextRedemptionTime(address _vault, uint256 _nextRedemptionTime) external onlyOwner { if (!isVault[_vault]) revert VaultNotExists(); YTAssetVault(_vault).setNextRedemptionTime(_nextRedemptionTime); emit NextRedemptionTimeSet(_vault, _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); } } function pauseVault(address _vault) external onlyOwner { if (!isVault[_vault]) revert VaultNotExists(); YTAssetVault(_vault).pause(); } function unpauseVault(address _vault) external onlyOwner { if (!isVault[_vault]) revert VaultNotExists(); YTAssetVault(_vault).unpause(); } 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(); } } 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(); } } function updateVaultPrices( address _vault, uint256 _ytPrice ) external onlyOwner { if (!isVault[_vault]) revert VaultNotExists(); YTAssetVault(_vault).updatePrices(_ytPrice); emit PricesUpdated(_vault, _ytPrice); } 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]); } } function upgradeVault(address _vault, address _newImplementation) external onlyOwner { if (!isVault[_vault]) revert VaultNotExists(); if (_newImplementation == address(0)) revert InvalidAddress(); YTAssetVault(_vault).upgradeToAndCall(_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, ""); } } function getVaultCount() external view returns (uint256) { return allVaults.length; } 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]; } } function getAllVaults() external view returns (address[] memory) { return allVaults; } 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(); } uint256[50] private __gap; }