// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import "../SYBaseUpg.sol";
import "../../../interfaces/IERC4626.sol";
contract PendleERC4626UpgSYV2 is SYBaseUpg {
using PMath for uint256;
SYBaseUpg.SYBaseUpg contains utility such as validations, event emission, and especially it is upgradable.
SYBase for non-upgradable contract. address public immutable asset;
constructor(address _erc4626) SYBaseUpg(_erc4626) {
asset = IERC4626(_erc4626).asset();
}
function initialize(string memory _name, string memory _symbol) external virtual initializer {
__SYBaseUpg_init(_name, _symbol);
_safeApproveInf(asset, yieldToken);
}
decimals() function of the SY contract returns the same decimals of the yieldToken (the ERC4626 in this case).initialize function is required because it is upgradable._safeApproveInf once, so we don’t need to call it again during the SY’s lifetime. function _deposit(
address tokenIn,
uint256 amountDeposited
) internal virtual override returns (uint256 /*amountSharesOut*/) {
if (tokenIn == yieldToken) {
return amountDeposited;
} else {
return IERC4626(yieldToken).deposit(amountDeposited, address(this));
}
}
function _redeem(
address receiver,
address tokenOut,
uint256 amountSharesToRedeem
) internal virtual override returns (uint256 amountTokenOut) {
if (tokenOut == yieldToken) {
amountTokenOut = amountSharesToRedeem;
_transferOut(yieldToken, receiver, amountTokenOut);
} else {
amountTokenOut = IERC4626(yieldToken).redeem(amountSharesToRedeem, receiver, address(this));
}
}
deposit() and redeem() functions.
deposit() is to obtain ERC4626 into SY's address.redeem() is to convert ERC4626 in SY's address to targeted tokentokenIn and tokenOut was done in the base contract.
List of token used in validation is defined here [ function getTokensIn() public view virtual override returns (address[] memory res) { res = new address; res[0] = asset; res[1] = yieldToken; }
function getTokensOut() public view virtual override returns (address[] memory res) { res = new address; res[0] = asset; res[1] = yieldToken; }
function isValidTokenIn(address token) public view virtual override returns (bool) { return token == yieldToken || token == asset; }
function isValidTokenOut(address token) public view virtual override returns (bool) { return token == yieldToken || token == asset; }](https://pendle.notion.site/function-getTokensIn-public-view-virtual-override-returns-address-memory-res-res-n-1f2567a21d3780b8995ac792a3061453)
yieldToken (the ERC4626)assetERC4626.deposit()/ ERC4626.redeem() for the asset.yieldToken (the vault), then we don’t do anything.
_redeem we still need to actually _transferOut the token. function exchangeRate() public view virtual override returns (uint256) {
return IERC4626(yieldToken).convertToAssets(PMath.ONE);
}
yieldToken (the ERC4626 in this case) against the asset
IERC4626.convertToAssets is linear at a given time.
convertToAssets(2 * X) / 2 should be exactly convertToAssets(X)exchangeRate() function is not always in 18-decimals, but it is based on the SY decimals and the asset decimals.
Let $\mathrm{sd}$, $\mathrm{ad}$, and $\mathrm{ed}$ be the decimals of SY, asset and exchangeRate, respectively.
3 numbers must satisfied the following equation.
$$ \mathrm{sd} + \mathrm{ed} = 18 + \mathrm{ad} $$
function _previewDeposit(
address tokenIn,
uint256 amountTokenToDeposit
) internal view virtual override returns (uint256 /*amountSharesOut*/) {
if (tokenIn == yieldToken) return amountTokenToDeposit;
else return IERC4626(yieldToken).previewDeposit(amountTokenToDeposit);
}
function _previewRedeem(
address tokenOut,
uint256 amountSharesToRedeem
) internal view virtual override returns (uint256 /*amountTokenOut*/) {
if (tokenOut == yieldToken) return amountSharesToRedeem;
else return IERC4626(yieldToken).previewRedeem(amountSharesToRedeem);
}
redeem() and deposit(), the validation of tokenIn and tokenOut are done in the base contract.deposit() and redeem() function getTokensIn() public view virtual override returns (address[] memory res) {
res = new address[](2);
res[0] = asset;
res[1] = yieldToken;
}
function getTokensOut() public view virtual override returns (address[] memory res) {
res = new address[](2);
res[0] = asset;
res[1] = yieldToken;
}
function isValidTokenIn(address token) public view virtual override returns (bool) {
return token == yieldToken || token == asset;
}
function isValidTokenOut(address token) public view virtual override returns (bool) {
return token == yieldToken || token == asset;
}
function assetInfo()
external
view
virtual
returns (AssetType assetType, address assetAddress, uint8 assetDecimals)
{
return (AssetType.TOKEN, asset, IERC20Metadata(asset).decimals());
}
}
Please refer to ‣