Since our vault follows ERC 4626 stadard, YToken can be minted instantly using Primary Asset YToken directly by calling deposit function on Vault contract.
Users who want to mint YTokens using non-primary assets can do so by calling the deposit function on the Manager contract (along with passing _referralCode if they have any).
Upon deposit, the user receives a receipt NFT as confirmation of the order placement.
This request is then picked up by the Keeper bot, which sends a transaction on-chain to burn the receipt NFT and mint YTokens to the user's address.
/**
* @notice Deposit the asset to the vault
* @param _yToken address of the yToken
* @param _asset address of the asset
* @param _amount amount of the asset
* @param _receiver address of the receiver
* @param _callback address of the callback
* @param _callbackData data of the callback
* @param _referralCode bytes32 referral code for on-chain tracking
* @dev This function is used to deposit the asset to the vault
*/
function deposit(address _yToken, address _asset, uint256 _amount, address _receiver, address _callback, bytes calldata _callbackData, bytes32 _referralCode) external notPaused nonReentrant {
_validate(msg.sender, _receiver, _yToken, _asset, _amount, true);
IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);
// get absolute exchange rate
uint256 exchangeRateInUnderlying = IYToken(_yToken).exchangeRate();
// For all deposit via Manager, we mint the receipt and follow 2 step process
// callback and callbackData are only for future use and can only be enabled by upgrading the contract
uint256 receiptId = IReceipt(receipt).mint(msg.sender, Order(true, msg.sender, _asset, _receiver, _yToken, _amount, block.timestamp, exchangeRateInUnderlying, address(0), "", _referralCode));
emit OrderRequest(msg.sender, _yToken, _asset, _receiver, _amount, true, exchangeRateInUnderlying, receiptId, _referralCode);
}
Redeem YToken using non-Primary Asset via Manager
Users can also place a redeem request for YUSD through the Manager contract.
These requests are maintained in a queue until the cooldown period is complete.
Once the cooldown period ends, the Keeper bot processes the redeem request and transfers the underlying asset to the user.
/**
* @notice Redeem the asset from the vault
* @param _yToken address of the yToken
* @param _asset address of the asset
* @param _shares amount of the shares
* @param _receiver address of the receiver
* @param _callback address of the callback
* @param _callbackData data of the callback
* @dev This function is used to redeem the asset from the vault
*
* @dev Example:
* When redeeming 100e18 YToken with exchange rate 1 YToken = 1.1 USDC:
* 1. Calculate vaultAssetAmount = 110e6 USDC (using convertToAssets)
* 2. Update yToken's total assets accounting to reflect the withdrawn amount
* 3. Burn the yToken shares immediately to stop yield accrual
* 4. Create a receipt for the withdrawal that can be executed after the waiting period
*/
function redeem(address caller, address _yToken, address _asset, uint256 _shares, address _receiver, address _callback, bytes calldata _callbackData) external notPaused nonReentrant {
if (msg.sender == _yToken) {
_validate(caller, _receiver, _yToken, _asset, _shares, false);
} else {
require(caller == msg.sender, "!caller");
_validate(msg.sender, _receiver, _yToken, _asset, _shares, false);
}
// Calculate the equivalent vault asset amount
uint256 vaultAssetAmount = IERC4626(_yToken).convertToAssets(_shares);
// update equivalent total assets
IYToken(_yToken).updateTotalAssets(vaultAssetAmount, false);
// burn shares from caller
IYToken(_yToken).burnYToken(caller, _shares);
/**
* Post burning token, check the remaining shares for caller is either 0 or >= minSharesInYToken
* This is to ensure future redeems are not blocked due to low shares
*/
uint256 remainingShares = IERC20(_yToken).balanceOf(caller);
require(remainingShares == 0 || remainingShares >= minSharesInYToken[_yToken], "0 < remainingShares < minShares");
// get exchange rate in underlying
uint256 exchangeRateInUnderlying = IYToken(_yToken).exchangeRate();
// callback and callbackData are only for future use and can only be enabled by upgrading the contract
uint256 receiptId = IReceipt(receipt).mint(caller, Order(false, caller, _asset, _receiver, _yToken, _shares, block.timestamp, exchangeRateInUnderlying, address(0), "", ""));
emit OrderRequest(caller, _yToken, _asset, _receiver, _shares, false, exchangeRateInUnderlying, receiptId, "");
}