2025-12-18 13:07:35 +08:00
|
|
|
// 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();
|
|
|
|
|
|
2025-12-23 14:05:41 +08:00
|
|
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
|
|
|
constructor() {
|
|
|
|
|
_disableInitializers();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-18 13:07:35 +08:00
|
|
|
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);
|
2025-12-24 16:41:26 +08:00
|
|
|
event PricesUpdated(address indexed vault, uint256 ytPrice);
|
2025-12-18 13:07:35 +08:00
|
|
|
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;
|
|
|
|
|
}
|
2025-12-25 13:29:35 +08:00
|
|
|
|
2025-12-18 13:07:35 +08:00
|
|
|
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
|
2025-12-25 13:29:35 +08:00
|
|
|
|
2025-12-18 13:07:35 +08:00
|
|
|
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,
|
2025-12-24 16:41:26 +08:00
|
|
|
address _usdc,
|
2025-12-18 13:07:35 +08:00
|
|
|
uint256 _redemptionTime,
|
2025-12-24 16:41:26 +08:00
|
|
|
uint256 _initialYtPrice,
|
|
|
|
|
address _usdcPriceFeed
|
2025-12-18 13:07:35 +08:00
|
|
|
) external onlyOwner returns (address vault) {
|
|
|
|
|
if (_manager == address(0)) revert InvalidAddress();
|
2025-12-25 13:29:35 +08:00
|
|
|
|
2025-12-18 13:07:35 +08:00
|
|
|
uint256 actualHardCap = _hardCap == 0 ? defaultHardCap : _hardCap;
|
|
|
|
|
|
|
|
|
|
bytes memory initData = abi.encodeWithSelector(
|
|
|
|
|
YTAssetVault.initialize.selector,
|
|
|
|
|
_name,
|
|
|
|
|
_symbol,
|
|
|
|
|
_manager,
|
|
|
|
|
actualHardCap,
|
2025-12-24 16:41:26 +08:00
|
|
|
_usdc,
|
2025-12-18 13:07:35 +08:00
|
|
|
_redemptionTime,
|
2025-12-24 16:41:26 +08:00
|
|
|
_initialYtPrice,
|
|
|
|
|
_usdcPriceFeed
|
2025-12-18 13:07:35 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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,
|
2025-12-24 16:41:26 +08:00
|
|
|
address _usdc,
|
2025-12-18 13:07:35 +08:00
|
|
|
uint256[] memory _redemptionTimes,
|
2025-12-24 16:41:26 +08:00
|
|
|
uint256[] memory _initialYtPrices,
|
|
|
|
|
address _usdcPriceFeed
|
2025-12-18 13:07:35 +08:00
|
|
|
) 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],
|
2025-12-24 16:41:26 +08:00
|
|
|
_usdc,
|
2025-12-18 13:07:35 +08:00
|
|
|
_redemptionTimes[i],
|
2025-12-24 16:41:26 +08:00
|
|
|
_initialYtPrices[i],
|
|
|
|
|
_usdcPriceFeed
|
2025-12-18 13:07:35 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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(
|
2025-12-24 16:41:26 +08:00
|
|
|
address _vault,
|
2025-12-18 13:07:35 +08:00
|
|
|
uint256 _ytPrice
|
|
|
|
|
) external onlyOwner {
|
|
|
|
|
if (!isVault[_vault]) revert VaultNotExists();
|
|
|
|
|
|
2025-12-24 16:41:26 +08:00
|
|
|
YTAssetVault(_vault).updatePrices(_ytPrice);
|
|
|
|
|
emit PricesUpdated(_vault, _ytPrice);
|
2025-12-18 13:07:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateVaultPricesBatch(
|
|
|
|
|
address[] memory _vaults,
|
|
|
|
|
uint256[] memory _ytPrices
|
|
|
|
|
) external onlyOwner {
|
2025-12-24 16:41:26 +08:00
|
|
|
require(_vaults.length == _ytPrices.length, "Length mismatch");
|
2025-12-18 13:07:35 +08:00
|
|
|
|
|
|
|
|
for (uint256 i = 0; i < _vaults.length; i++) {
|
|
|
|
|
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
2025-12-24 16:41:26 +08:00
|
|
|
YTAssetVault(_vaults[i]).updatePrices(_ytPrices[i]);
|
|
|
|
|
emit PricesUpdated(_vaults[i], _ytPrices[i]);
|
2025-12-18 13:07:35 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
2025-12-24 16:41:26 +08:00
|
|
|
uint256 usdcPrice,
|
2025-12-18 13:07:35 +08:00
|
|
|
uint256 ytPrice,
|
|
|
|
|
uint256 nextRedemptionTime
|
|
|
|
|
) {
|
|
|
|
|
exists = isVault[_vault];
|
|
|
|
|
if (!exists) return (false, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
|
|
|
(
|
|
|
|
|
totalAssets,
|
|
|
|
|
idleAssets,
|
|
|
|
|
managedAssets,
|
|
|
|
|
totalSupply,
|
|
|
|
|
hardCap,
|
2025-12-24 16:41:26 +08:00
|
|
|
usdcPrice,
|
2025-12-18 13:07:35 +08:00
|
|
|
ytPrice,
|
|
|
|
|
nextRedemptionTime
|
|
|
|
|
) = YTAssetVault(_vault).getVaultInfo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint256[50] private __gap;
|
2025-12-25 13:29:35 +08:00
|
|
|
}
|