162 lines
5.0 KiB
Solidity
162 lines
5.0 KiB
Solidity
// SPDX-License-Identifier: MIT
|
||
pragma solidity ^0.8.0;
|
||
|
||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||
|
||
/**
|
||
* @title YTToken
|
||
* @notice YT代币示例实现(Yield Token)
|
||
* @dev 展示如何实现价格接口供YTPriceFeed读取
|
||
*/
|
||
contract YTToken is ERC20, Ownable {
|
||
|
||
error NotUpdater();
|
||
error InvalidUpdater();
|
||
error IntervalTooLong();
|
||
error UpdateTooFrequent();
|
||
error InvalidYield();
|
||
error InvalidAmount();
|
||
error InsufficientAssets();
|
||
|
||
uint256 public constant PRICE_PRECISION = 10 ** 30;
|
||
|
||
uint256 public totalAssets;
|
||
uint256 public accumulatedYield;
|
||
|
||
// 价格变量
|
||
uint256 public assetPrice;
|
||
uint256 public lastPriceUpdate;
|
||
|
||
// 价格更新控制
|
||
address public priceUpdater;
|
||
uint256 public minUpdateInterval = 5 minutes; // 最小更新间隔
|
||
|
||
event PriceUpdated(uint256 oldPrice, uint256 newPrice, uint256 timestamp);
|
||
event YieldAccumulated(uint256 amount, uint256 timestamp);
|
||
event PriceUpdaterSet(address indexed updater);
|
||
event MinUpdateIntervalSet(uint256 interval);
|
||
|
||
modifier onlyPriceUpdater() {
|
||
if (msg.sender != priceUpdater && msg.sender != owner()) revert NotUpdater();
|
||
_;
|
||
}
|
||
|
||
constructor(
|
||
string memory name,
|
||
string memory symbol,
|
||
address _priceUpdater
|
||
) ERC20(name, symbol) Ownable(msg.sender) {
|
||
assetPrice = PRICE_PRECISION; // 初始价格为1
|
||
lastPriceUpdate = block.timestamp;
|
||
priceUpdater = _priceUpdater;
|
||
}
|
||
|
||
/**
|
||
* @notice 设置价格更新者
|
||
*/
|
||
function setPriceUpdater(address _updater) external onlyOwner {
|
||
if (_updater == address(0)) revert InvalidUpdater();
|
||
priceUpdater = _updater;
|
||
emit PriceUpdaterSet(_updater);
|
||
}
|
||
|
||
/**
|
||
* @notice 设置最小更新间隔
|
||
*/
|
||
function setMinUpdateInterval(uint256 _interval) external onlyOwner {
|
||
if (_interval > 1 hours) revert IntervalTooLong();
|
||
minUpdateInterval = _interval;
|
||
emit MinUpdateIntervalSet(_interval);
|
||
}
|
||
|
||
/**
|
||
* @notice 更新代币价格
|
||
* @dev 只能由授权的updater调用,有最小时间间隔限制
|
||
*/
|
||
function updatePrice() public onlyPriceUpdater {
|
||
if (block.timestamp < lastPriceUpdate + minUpdateInterval) revert UpdateTooFrequent();
|
||
|
||
uint256 oldPrice = assetPrice;
|
||
uint256 supply = totalSupply();
|
||
|
||
if (supply == 0) {
|
||
assetPrice = PRICE_PRECISION;
|
||
} else {
|
||
uint256 totalValue = totalAssets + accumulatedYield;
|
||
// 计算每个token对应的USDC价值(18位精度)
|
||
uint256 usdcPerToken = totalValue * 1e18 / supply;
|
||
// 转换为30位精度的价格
|
||
assetPrice = usdcPerToken * PRICE_PRECISION / 1e18;
|
||
}
|
||
|
||
lastPriceUpdate = block.timestamp;
|
||
|
||
emit PriceUpdated(oldPrice, assetPrice, block.timestamp);
|
||
}
|
||
|
||
/**
|
||
* @notice 累积收益并更新价格
|
||
* @dev 当从收益策略中收到新收益时调用
|
||
*/
|
||
function updateYield(uint256 _newYield) external onlyPriceUpdater {
|
||
if (_newYield == 0) revert InvalidYield();
|
||
|
||
accumulatedYield += _newYield;
|
||
|
||
emit YieldAccumulated(_newYield, block.timestamp);
|
||
|
||
// 收益更新后立即更新价格
|
||
if (block.timestamp >= lastPriceUpdate + minUpdateInterval) {
|
||
uint256 oldPrice = assetPrice;
|
||
uint256 supply = totalSupply();
|
||
|
||
if (supply > 0) {
|
||
uint256 totalValue = totalAssets + accumulatedYield;
|
||
uint256 usdcPerToken = totalValue * 1e18 / supply;
|
||
assetPrice = usdcPerToken * PRICE_PRECISION / 1e18;
|
||
}
|
||
|
||
lastPriceUpdate = block.timestamp;
|
||
emit PriceUpdated(oldPrice, assetPrice, block.timestamp);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @notice 存入资产(模拟)
|
||
* @dev 实际实现中应该处理真实的USDC存款
|
||
*/
|
||
function deposit(uint256 _amount) external onlyOwner {
|
||
if (_amount == 0) revert InvalidAmount();
|
||
totalAssets += _amount;
|
||
_mint(msg.sender, _amount);
|
||
}
|
||
|
||
/**
|
||
* @notice 提取资产(模拟)
|
||
* @dev 实际实现中应该处理真实的USDC提款
|
||
*/
|
||
function withdraw(uint256 _amount) external onlyOwner {
|
||
if (_amount == 0) revert InvalidAmount();
|
||
if (totalAssets < _amount) revert InsufficientAssets();
|
||
totalAssets -= _amount;
|
||
_burn(msg.sender, _amount);
|
||
}
|
||
|
||
/**
|
||
* @notice 获取当前价格信息
|
||
*/
|
||
function getPriceInfo() external view returns (
|
||
uint256 price,
|
||
uint256 lastUpdate,
|
||
uint256 timeSinceUpdate,
|
||
uint256 totalVal
|
||
) {
|
||
price = assetPrice;
|
||
lastUpdate = lastPriceUpdate;
|
||
timeSinceUpdate = block.timestamp - lastPriceUpdate;
|
||
totalVal = totalAssets + accumulatedYield;
|
||
}
|
||
}
|
||
|