// SPDX-License-Identifier: MIT
pragma solidity 0.8.14;
import {ERC20} from "yield-utils-v2/contracts/token/ERC20.sol";
import {MinimalTransferHelper} from "yield-utils-v2/contracts/token/MinimalTransferHelper.sol";
contract ERC5095 is ERC20 {
using MinimalTransferHelper for ERC20;
/* EVENTS
*****************************************************************************************************************/
event Redeem(address indexed from, address indexed to, uint256 underlyingAmount);
/* MODIFIERS
*****************************************************************************************************************/
/// @notice A modifier that ensures the current block timestamp is at or after maturity.
// @notice 确保当前区块时间戳在到期日或之后的修饰符。
modifier afterMaturity() virtual {
require(block.timestamp >= maturity, "BEFORE_MATURITY");
_;
}
/* IMMUTABLES
*****************************************************************************************************************/
ERC20 public immutable underlying;
uint256 public immutable maturity;
/* CONSTRUCTOR
*****************************************************************************************************************/
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_,
ERC20 underlying_,
uint256 maturity_
) ERC20(name_, symbol_, decimals_) {
underlying = underlying_;
maturity = maturity_;
}
/* CORE FUNCTIONS
*****************************************************************************************************************/
/// @notice Burns an exact amount of principal tokens in exchange for an amount of underlying.
// @notice 燃烧确切数量的本金代币以换取一定数量的基础资产。
/// @dev This reverts if before maturity.
// @dev 如果在到期之前,则会回退。
/// @param principalAmount The exact amount of principal tokens to be burned.
// @param principalAmount 要燃烧的确切数量的本金代币。
/// @param from The owner of the principal tokens to be redeemed. If not msg.sender then must have prior approval.
// @param from 要赎回的本金代币的所有者。如果不是 msg.sender,则必须事先获得批准。
/// @param to The address to send the underlying tokens.
// @param to 发送基础代币的地址。
/// @return underlyingAmount The total amount of underlying tokens sent.
// @return underlyingAmount 发送的基础代币的总量。
function redeem(
uint256 principalAmount,
address from,
address to
) public virtual afterMaturity returns (uint256 underlyingAmount) {
_decreaseAllowance(from, principalAmount);
// Check for rounding error since we round down in previewRedeem.
// 检查舍入误差,因为我们在 previewRedeem 中向下舍入。
require((underlyingAmount = _previewRedeem(principalAmount)) != 0, "ZERO_ASSETS");
_burn(from, principalAmount);
emit Redeem(from, to, principalAmount);
_transferOut(to, underlyingAmount);
}
/// @notice Burns a calculated amount of principal tokens in exchange for an exact amount of underlying.
// @notice 燃烧计算数量的本金代币以换取确切数量的基础资产。
/// @dev This reverts if before maturity.
// @dev 如果在到期之前,则会回退。
/// @param underlyingAmount The exact amount of underlying tokens to be received.
// @param underlyingAmount 要接收的基础代币的精确金额。
/// @param from The owner of the principal tokens to be redeemed. If not msg.sender then must have prior approval.
// @param from 要赎回的本金代币的所有者。如果不是 msg.sender,则必须事先获得批准。
/// @param to The address to send the underlying tokens.
// @param to 发送基础代币的地址。
/// @return principalAmount The total amount of underlying tokens redeemed.
// @return principalAmount 赎回的基础代币的总量。
function withdraw(
uint256 underlyingAmount,
address from,
address to
) public virtual afterMaturity returns (uint256 principalAmount) {
principalAmount = _previewWithdraw(underlyingAmount); // No need to check for rounding error, previewWithdraw rounds up.
// 无需检查舍入误差,previewWithdraw 向上舍入。
_decreaseAllowance(from, principalAmount);
_burn(from, principalAmount);
emit Redeem(from, to, principalAmount);
_transferOut(to, underlyingAmount);
}
/// @notice An internal, overridable transfer function.
// @notice 内部可覆盖的 transfer 函数。
/// @dev Reverts on failed transfer.
// @dev 传输失败时回退。
/// @param to The recipient of the transfer.
// @param to transfer 的接收者。
/// @param amount The amount of the transfer.
// @param amount transfer 的数额。
function _transferOut(address to, uint256 amount) internal virtual {
underlying.safeTransfer(to, amount);
}
/* ACCOUNTING FUNCTIONS
*****************************************************************************************************************/
/// @notice Calculates the amount of underlying tokens that would be exchanged for a given amount of principal tokens.
// @notice 计算将为给定数量的本金代币交换的基础代币的数量。
/// @dev Before maturity, it converts to underlying as if at maturity.
// @dev 到期之前,它会转换为基础,就像在到期时一样。
/// @param principalAmount The amount principal on which to calculate conversion.
// @param principalAmount 用于计算转换的金本数额。
/// @return underlyingAmount The total amount of underlying that would be received for the given principal amount..
// @return underlyingAmount 将为给定的本金数额收到的基础代币的总量。
function convertToUnderlying(uint256 principalAmount) external view returns (uint256 underlyingAmount) {
return _convertToUnderlying(principalAmount);
}
function _convertToUnderlying(uint256 principalAmount) internal view virtual returns (uint256 underlyingAmount) {
return principalAmount;
}
/// @notice Converts a given amount of underlying tokens to principal exclusive of fees.
// @notice 将给定数量的基础代币转换为本金,不包括费用。
/// @dev Before maturity, it converts to principal as if at maturity.
// @dev 到期之前,它会转换为本金,就像在到期时一样。
/// @param underlyingAmount The total amount of underlying on which to calculate the conversion.
// @param underlyingAmount 用于计算转换的基础代币的总量。
/// @return principalAmount The amount principal tokens required to provide the given amount of underlying.
// @return principalAmount 提供给定数量基础代币所需的本金代币的数量。
function convertToPrincipal(uint256 underlyingAmount) external view returns (uint256 principalAmount) {
return _convertToPrincipal(underlyingAmount);
}
function _convertToPrincipal(uint256 underlyingAmount) internal view virtual returns (uint256 principalAmount) {
return underlyingAmount;
}
/// @notice Allows user to simulate redemption of a given amount of principal tokens, inclusive of fees and other
// @notice 允许用户模拟赎回给定数量的本金代币,包括费用和其他
/// current block conditions.
// 当前区块条件。
/// @dev This reverts if before maturity.
// @dev 如果在到期之前,则会回退。
/// @param principalAmount The amount of principal that would be redeemed.
// @param principalAmount 将被赎回的本金的数量。
/// @return underlyingAmount The amount of underlying that would be received.
// @return underlyingAmount 将收到的基础代币的数量。
function previewRedeem(uint256 principalAmount) external view afterMaturity returns (uint256 underlyingAmount) {
return _previewRedeem(principalAmount);
}
function _previewRedeem(uint256 principalAmount) internal view virtual returns (uint256 underlyingAmount) {
return _convertToUnderlying(principalAmount); // should include fees/slippage
// 应包括费用/滑点
}
/// @notice Calculates the maximum amount of principal tokens that an owner could redeem.
// @notice 计算所有者可以赎回的最大本金代币数量。
/// @dev This returns 0 if before maturity.
// @dev 如果在到期之前,则返回 0。
/// @param owner The address for which the redemption is being calculated.
// @param owner 正在计算赎回的地址。
/// @return maxPrincipalAmount The maximum amount of principal tokens that can be redeemed by the given owner.
// @return maxPrincipalAmount 给定所有者可以赎回的最大本金代币数量。
function maxRedeem(address owner) public view returns (uint256 maxPrincipalAmount) {
return block.timestamp >= maturity ? _balanceOf[owner] : 0;
}
/// @notice Allows user to simulate withdraw of a given amount of underlying tokens.
// @notice 允许用户模拟提取给定数量的基础代币。
/// @dev This reverts if before maturity.
// @dev 如果在到期之前,则会回退。
/// @param underlyingAmount The amount of underlying tokens that would be withdrawn.
// @param underlyingAmount 将被提取的基础代币的数量。
/// @return principalAmount The amount of principal tokens that would be redeemed.
// @return principalAmount 将被赎回的本金代币的数量。
function previewWithdraw(uint256 underlyingAmount) external view afterMaturity returns (uint256 principalAmount) {
return _previewWithdraw(underlyingAmount);
}
function _previewWithdraw(uint256 underlyingAmount) internal view virtual returns (uint256 principalAmount) {
return _convertToPrincipal(underlyingAmount); // should include fees/slippage
// 应包括费用/滑点
}
/// @notice Calculates the maximum amount of underlying tokens that can be withdrawn by a given owner.
// @notice 计算给定所有者可以提取的最大基础代币数量。
/// @dev This returns 0 if before maturity.
// @dev 如果在到期之前,则返回 0。
/// @param owner The address for which the withdraw is being calculated.
// @param owner 正在计算提取的地址。
/// @return maxUnderlyingAmount The maximum amount of underlying tokens that can be withdrawn by a given owner.
// @return maxUnderlyingAmount 给定所有者可以提取的最大基础代币数量。
function maxWithdraw(address owner) public view returns (uint256 maxUnderlyingAmount) {
return _previewWithdraw(maxRedeem(owner));
}
}