commit
This commit is contained in:
443
contracts/vault/YTAssetFactory.sol
Normal file
443
contracts/vault/YTAssetFactory.sol
Normal file
@@ -0,0 +1,443 @@
|
||||
// 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();
|
||||
|
||||
/// @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 wusdPrice, 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 _wusd WUSD代币地址(传0使用默认地址)
|
||||
* @param _redemptionTime 赎回时间(Unix时间戳)
|
||||
* @param _initialWusdPrice 初始WUSD价格(精度1e30,传0则使用默认值1.0)
|
||||
* @param _initialYtPrice 初始YT价格(精度1e30,传0则使用默认值1.0)
|
||||
* @return vault 新创建的vault地址
|
||||
*/
|
||||
function createVault(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
address _manager,
|
||||
uint256 _hardCap,
|
||||
address _wusd,
|
||||
uint256 _redemptionTime,
|
||||
uint256 _initialWusdPrice,
|
||||
uint256 _initialYtPrice
|
||||
) 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,
|
||||
_wusd,
|
||||
_redemptionTime,
|
||||
_initialWusdPrice,
|
||||
_initialYtPrice
|
||||
);
|
||||
|
||||
// 部署代理合约
|
||||
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 _wusd WUSD代币地址(传0使用默认地址)
|
||||
* @param _redemptionTimes 赎回时间数组(Unix时间戳)
|
||||
* @param _initialWusdPrices 初始WUSD价格数组(精度1e30)
|
||||
* @param _initialYtPrices 初始YT价格数组(精度1e30)
|
||||
* @return vaults 创建的vault地址数组
|
||||
*/
|
||||
function createVaultBatch(
|
||||
string[] memory _names,
|
||||
string[] memory _symbols,
|
||||
address[] memory _managers,
|
||||
uint256[] memory _hardCaps,
|
||||
address _wusd,
|
||||
uint256[] memory _redemptionTimes,
|
||||
uint256[] memory _initialWusdPrices,
|
||||
uint256[] memory _initialYtPrices
|
||||
) external returns (address[] memory vaults) {
|
||||
require(
|
||||
_names.length == _symbols.length &&
|
||||
_names.length == _managers.length &&
|
||||
_names.length == _hardCaps.length &&
|
||||
_names.length == _redemptionTimes.length &&
|
||||
_names.length == _initialWusdPrices.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],
|
||||
_wusd,
|
||||
_redemptionTimes[i],
|
||||
_initialWusdPrices[i],
|
||||
_initialYtPrices[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 _wusdPrice WUSD价格(精度1e30)
|
||||
* @param _ytPrice YT价格(精度1e30)
|
||||
*/
|
||||
function updateVaultPrices(
|
||||
address _vault,
|
||||
uint256 _wusdPrice,
|
||||
uint256 _ytPrice
|
||||
) external onlyOwner {
|
||||
if (!isVault[_vault]) revert VaultNotExists();
|
||||
|
||||
YTAssetVault(_vault).updatePrices(_wusdPrice, _ytPrice);
|
||||
emit PricesUpdated(_vault, _wusdPrice, _ytPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 批量更新价格
|
||||
* @param _vaults vault地址数组
|
||||
* @param _wusdPrices WUSD价格数组(精度1e30)
|
||||
* @param _ytPrices YT价格数组(精度1e30)
|
||||
*/
|
||||
function updateVaultPricesBatch(
|
||||
address[] memory _vaults,
|
||||
uint256[] memory _wusdPrices,
|
||||
uint256[] memory _ytPrices
|
||||
) external onlyOwner {
|
||||
require(
|
||||
_vaults.length == _wusdPrices.length &&
|
||||
_vaults.length == _ytPrices.length,
|
||||
"Length mismatch"
|
||||
);
|
||||
|
||||
for (uint256 i = 0; i < _vaults.length; i++) {
|
||||
if (!isVault[_vaults[i]]) revert VaultNotExists();
|
||||
YTAssetVault(_vaults[i]).updatePrices(_wusdPrices[i], _ytPrices[i]);
|
||||
emit PricesUpdated(_vaults[i], _wusdPrices[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 wusdPrice,
|
||||
uint256 ytPrice,
|
||||
uint256 nextRedemptionTime
|
||||
) {
|
||||
exists = isVault[_vault];
|
||||
if (!exists) return (false, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
(
|
||||
totalAssets,
|
||||
idleAssets,
|
||||
managedAssets,
|
||||
totalSupply,
|
||||
hardCap,
|
||||
wusdPrice,
|
||||
ytPrice,
|
||||
nextRedemptionTime
|
||||
) = YTAssetVault(_vault).getVaultInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 预留存储空间,用于未来升级时添加新的状态变量
|
||||
* 50个slot = 50 * 32 bytes = 1600 bytes
|
||||
*/
|
||||
uint256[50] private __gap;
|
||||
}
|
||||
375
contracts/vault/YTAssetVault.sol
Normal file
375
contracts/vault/YTAssetVault.sol
Normal file
@@ -0,0 +1,375 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
|
||||
/**
|
||||
* @title YTAssetVault
|
||||
* @notice 基于价格的资产金库,用户根据WUSD和YT代币价格进行兑换
|
||||
* @dev UUPS可升级合约,YT是份额代币
|
||||
*/
|
||||
contract YTAssetVault is
|
||||
Initializable,
|
||||
ERC20Upgradeable,
|
||||
UUPSUpgradeable,
|
||||
ReentrancyGuardUpgradeable,
|
||||
PausableUpgradeable
|
||||
{
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
error Forbidden();
|
||||
error HardCapExceeded();
|
||||
error InvalidAmount();
|
||||
error InvalidHardCap();
|
||||
error InvalidPrice();
|
||||
error InsufficientWUSD();
|
||||
error InsufficientYTA();
|
||||
error StillInLockPeriod();
|
||||
|
||||
/// @notice 工厂合约地址
|
||||
address public factory;
|
||||
|
||||
/// @notice 管理员地址
|
||||
address public manager;
|
||||
|
||||
/// @notice YT代币硬顶(最大可铸造的YT数量)
|
||||
uint256 public hardCap;
|
||||
|
||||
/// @notice 已提取用于管理的WUSD数量
|
||||
uint256 public managedAssets;
|
||||
|
||||
/// @notice WUSD代币地址
|
||||
address public wusdAddress;
|
||||
|
||||
/// @notice WUSD价格(精度1e30)
|
||||
uint256 public wusdPrice;
|
||||
|
||||
/// @notice YT价格(精度1e30)
|
||||
uint256 public ytPrice;
|
||||
|
||||
/// @notice 价格精度
|
||||
uint256 public constant PRICE_PRECISION = 1e30;
|
||||
|
||||
/// @notice 下一个赎回开放时间(所有用户统一)
|
||||
uint256 public nextRedemptionTime;
|
||||
|
||||
event HardCapSet(uint256 newHardCap);
|
||||
event ManagerSet(address indexed newManager);
|
||||
event AssetsWithdrawn(address indexed to, uint256 amount);
|
||||
event AssetsDeposited(uint256 amount);
|
||||
event PriceUpdated(uint256 wusdPrice, uint256 ytPrice, uint256 timestamp);
|
||||
event Buy(address indexed user, uint256 wusdAmount, uint256 ytAmount);
|
||||
event Sell(address indexed user, uint256 ytAmount, uint256 wusdAmount);
|
||||
event NextRedemptionTimeSet(uint256 newRedemptionTime);
|
||||
|
||||
modifier onlyFactory() {
|
||||
if (msg.sender != factory) revert Forbidden();
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyManager() {
|
||||
if (msg.sender != manager) revert Forbidden();
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 初始化金库
|
||||
* @param _name YT代币名称
|
||||
* @param _symbol YT代币符号
|
||||
* @param _manager 管理员地址
|
||||
* @param _hardCap 硬顶限制
|
||||
* @param _wusd WUSD代币地址(可选,传0则使用默认地址)
|
||||
* @param _redemptionTime 赎回时间(Unix时间戳)
|
||||
* @param _initialWusdPrice 初始WUSD价格(精度1e30,传0则使用默认值1.0)
|
||||
* @param _initialYtPrice 初始YT价格(精度1e30,传0则使用默认值1.0)
|
||||
*
|
||||
* @dev 价格精度为1e30
|
||||
*/
|
||||
function initialize(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
address _manager,
|
||||
uint256 _hardCap,
|
||||
address _wusd,
|
||||
uint256 _redemptionTime,
|
||||
uint256 _initialWusdPrice,
|
||||
uint256 _initialYtPrice
|
||||
) external initializer {
|
||||
wusdAddress = _wusd == address(0)
|
||||
? 0x7Cd017ca5ddb86861FA983a34b5F495C6F898c41
|
||||
: _wusd;
|
||||
|
||||
__ERC20_init(_name, _symbol);
|
||||
__UUPSUpgradeable_init();
|
||||
__ReentrancyGuard_init();
|
||||
__Pausable_init();
|
||||
|
||||
factory = msg.sender;
|
||||
manager = _manager;
|
||||
hardCap = _hardCap;
|
||||
|
||||
// 使用传入的初始价格,如果为0则使用默认值1.0
|
||||
wusdPrice = _initialWusdPrice == 0 ? PRICE_PRECISION : _initialWusdPrice;
|
||||
ytPrice = _initialYtPrice == 0 ? PRICE_PRECISION : _initialYtPrice;
|
||||
|
||||
// 设置赎回时间
|
||||
nextRedemptionTime = _redemptionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 授权升级(仅factory可调用)
|
||||
* @param newImplementation 新实现合约地址
|
||||
*/
|
||||
function _authorizeUpgrade(address newImplementation) internal override onlyFactory {}
|
||||
|
||||
/**
|
||||
* @notice 设置硬顶
|
||||
* @param _hardCap 新的硬顶值
|
||||
*/
|
||||
function setHardCap(uint256 _hardCap) external onlyFactory {
|
||||
if (_hardCap < totalSupply()) revert InvalidHardCap();
|
||||
hardCap = _hardCap;
|
||||
emit HardCapSet(_hardCap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 设置管理员
|
||||
* @param _manager 新管理员地址
|
||||
*/
|
||||
function setManager(address _manager) external onlyFactory {
|
||||
manager = _manager;
|
||||
emit ManagerSet(_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 暂停合约(仅factory可调用)
|
||||
* @dev 暂停后,所有资金流动操作将被禁止
|
||||
*/
|
||||
function pause() external onlyFactory {
|
||||
_pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 恢复合约(仅factory可调用)
|
||||
*/
|
||||
function unpause() external onlyFactory {
|
||||
_unpause();
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 设置下一个赎回开放时间(仅factory可调用)
|
||||
* @param _nextRedemptionTime 下一个赎回时间(Unix时间戳)
|
||||
* @dev 所有用户统一在此时间后才能赎回,类似基金的赎回日
|
||||
*/
|
||||
function setNextRedemptionTime(uint256 _nextRedemptionTime) external onlyFactory {
|
||||
nextRedemptionTime = _nextRedemptionTime;
|
||||
emit NextRedemptionTimeSet(_nextRedemptionTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 更新价格(仅manager可调用)
|
||||
* @param _wusdPrice WUSD价格(精度1e30)
|
||||
* @param _ytPrice YT价格(精度1e30)
|
||||
*/
|
||||
function updatePrices(uint256 _wusdPrice, uint256 _ytPrice) external onlyFactory {
|
||||
if (_wusdPrice == 0 || _ytPrice == 0) revert InvalidPrice();
|
||||
|
||||
wusdPrice = _wusdPrice;
|
||||
ytPrice = _ytPrice;
|
||||
|
||||
emit PriceUpdated(_wusdPrice, _ytPrice, block.timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 用WUSD购买YT
|
||||
* @param _wusdAmount 支付的WUSD数量
|
||||
* @return ytAmount 实际获得的YT数量
|
||||
* @dev 首次购买时,YT价格 = WUSD价格(1:1兑换)
|
||||
*/
|
||||
function depositYT(uint256 _wusdAmount)
|
||||
external
|
||||
nonReentrant
|
||||
whenNotPaused
|
||||
returns (uint256 ytAmount)
|
||||
{
|
||||
if (_wusdAmount == 0) revert InvalidAmount();
|
||||
|
||||
// 计算可以购买的YT数量
|
||||
ytAmount = (_wusdAmount * wusdPrice) / ytPrice;
|
||||
|
||||
// 检查硬顶
|
||||
if (hardCap > 0 && totalSupply() + ytAmount > hardCap) {
|
||||
revert HardCapExceeded();
|
||||
}
|
||||
|
||||
// 转入WUSD
|
||||
IERC20(wusdAddress).safeTransferFrom(msg.sender, address(this), _wusdAmount);
|
||||
|
||||
// 铸造YT
|
||||
_mint(msg.sender, ytAmount);
|
||||
|
||||
emit Buy(msg.sender, _wusdAmount, ytAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 卖出YT换取WUSD(需要等到统一赎回时间)
|
||||
* @param _ytAmount 卖出的YT数量
|
||||
* @return wusdAmount 实际获得的WUSD数量
|
||||
*/
|
||||
function withdrawYT(uint256 _ytAmount)
|
||||
external
|
||||
nonReentrant
|
||||
whenNotPaused
|
||||
returns (uint256 wusdAmount)
|
||||
{
|
||||
if (_ytAmount == 0) revert InvalidAmount();
|
||||
if (balanceOf(msg.sender) < _ytAmount) revert InsufficientYTA();
|
||||
|
||||
// 检查是否到达统一赎回时间
|
||||
if (block.timestamp < nextRedemptionTime) {
|
||||
revert StillInLockPeriod();
|
||||
}
|
||||
|
||||
// 计算可以换取的WUSD数量
|
||||
wusdAmount = (_ytAmount * ytPrice) / wusdPrice;
|
||||
|
||||
// 检查合约是否有足够的WUSD
|
||||
uint256 availableWUSD = IERC20(wusdAddress).balanceOf(address(this));
|
||||
if (wusdAmount > availableWUSD) revert InsufficientWUSD();
|
||||
|
||||
// 销毁YT
|
||||
_burn(msg.sender, _ytAmount);
|
||||
|
||||
// 转出WUSD
|
||||
IERC20(wusdAddress).safeTransfer(msg.sender, wusdAmount);
|
||||
|
||||
emit Sell(msg.sender, _ytAmount, wusdAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 查询距离下次赎回开放还需等待多久
|
||||
* @return remainingTime 剩余时间(秒),0表示可以赎回
|
||||
*/
|
||||
function getTimeUntilNextRedemption() external view returns (uint256 remainingTime) {
|
||||
if (block.timestamp >= nextRedemptionTime) {
|
||||
return 0;
|
||||
}
|
||||
return nextRedemptionTime - block.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 检查当前是否可以赎回
|
||||
* @return 是否可以赎回
|
||||
*/
|
||||
function canRedeemNow() external view returns (bool) {
|
||||
return block.timestamp >= nextRedemptionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 提取WUSD用于外部投资
|
||||
* @param _to 接收地址
|
||||
* @param _amount 提取数量
|
||||
*/
|
||||
function withdrawForManagement(address _to, uint256 _amount) external onlyManager nonReentrant whenNotPaused {
|
||||
if (_amount == 0) revert InvalidAmount();
|
||||
|
||||
uint256 availableAssets = IERC20(wusdAddress).balanceOf(address(this));
|
||||
if (_amount > availableAssets) revert InvalidAmount();
|
||||
|
||||
managedAssets += _amount;
|
||||
IERC20(wusdAddress).safeTransfer(_to, _amount);
|
||||
|
||||
emit AssetsWithdrawn(_to, _amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 将管理的资产归还到金库(可以归还更多,产生收益)
|
||||
* @param _amount 归还数量
|
||||
*/
|
||||
function depositManagedAssets(uint256 _amount) external onlyManager nonReentrant whenNotPaused {
|
||||
if (_amount == 0) revert InvalidAmount();
|
||||
|
||||
// 先更新状态(遵循CEI模式)
|
||||
if (_amount >= managedAssets) {
|
||||
// 归还金额 >= 已管理资产,managedAssets归零,多余部分是收益
|
||||
managedAssets = 0;
|
||||
} else {
|
||||
// 归还金额 < 已管理资产,部分归还
|
||||
managedAssets -= _amount;
|
||||
}
|
||||
|
||||
// 从manager转入WUSD到合约
|
||||
IERC20(wusdAddress).safeTransferFrom(msg.sender, address(this), _amount);
|
||||
|
||||
emit AssetsDeposited(_amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 获取总资产(包含被管理的资产)
|
||||
* @return 总资产 = 合约余额 + 被管理的资产
|
||||
*/
|
||||
function totalAssets() public view returns (uint256) {
|
||||
return IERC20(wusdAddress).balanceOf(address(this)) + managedAssets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 获取空闲资产(可用于提取的资产)
|
||||
* @return 合约中实际持有的WUSD数量
|
||||
*/
|
||||
function idleAssets() public view returns (uint256) {
|
||||
return IERC20(wusdAddress).balanceOf(address(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 预览购买:计算支付指定WUSD可获得的YT数量
|
||||
* @param _wusdAmount 支付的WUSD数量
|
||||
* @return ytAmount 可获得的YT数量
|
||||
*/
|
||||
function previewBuy(uint256 _wusdAmount) external view returns (uint256 ytAmount) {
|
||||
ytAmount = (_wusdAmount * wusdPrice) / ytPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 预览卖出:计算卖出指定YT可获得的WUSD数量
|
||||
* @param _ytAmount 卖出的YT数量
|
||||
* @return wusdAmount 可获得的WUSD数量
|
||||
*/
|
||||
function previewSell(uint256 _ytAmount) external view returns (uint256 wusdAmount) {
|
||||
wusdAmount = (_ytAmount * ytPrice) / wusdPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice 获取金库信息
|
||||
*/
|
||||
function getVaultInfo() external view returns (
|
||||
uint256 _totalAssets,
|
||||
uint256 _idleAssets,
|
||||
uint256 _managedAssets,
|
||||
uint256 _totalSupply,
|
||||
uint256 _hardCap,
|
||||
uint256 _wusdPrice,
|
||||
uint256 _ytPrice,
|
||||
uint256 _nextRedemptionTime
|
||||
) {
|
||||
_totalAssets = totalAssets();
|
||||
_idleAssets = idleAssets();
|
||||
_managedAssets = managedAssets;
|
||||
_totalSupply = totalSupply();
|
||||
_hardCap = hardCap;
|
||||
_wusdPrice = wusdPrice;
|
||||
_ytPrice = ytPrice;
|
||||
_nextRedemptionTime = nextRedemptionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev 预留存储空间,用于未来升级时添加新的状态变量
|
||||
* 50个slot = 50 * 32 bytes = 1600 bytes
|
||||
*/
|
||||
uint256[50] private __gap;
|
||||
}
|
||||
Reference in New Issue
Block a user