diff --git a/.gitmodules b/.gitmodules index e1cb0965..efd864a7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/solmate"] path = lib/solmate url = https://github.com/transmissions11/solmate.git +[submodule "lib/bytes-utils"] + path = lib/bytes-utils + url = git@github.com:GNSPS/solidity-bytes-utils.git diff --git a/lib/bytes-utils b/lib/bytes-utils new file mode 160000 index 00000000..c433288c --- /dev/null +++ b/lib/bytes-utils @@ -0,0 +1 @@ +Subproject commit c433288c2907d383630864b6032e885d5603a654 diff --git a/src/feeds/PendleNAVFeed.sol b/src/feeds/PendleNAVFeed.sol new file mode 100644 index 00000000..ce9c290a --- /dev/null +++ b/src/feeds/PendleNAVFeed.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +contract PendleNAVFeed { + uint256 private constant SECONDS_PER_YEAR = 365 days; + uint256 private constant ONE = 1e18; + + address public immutable PT; + uint256 public immutable maturity; + uint256 public immutable baseDiscountPerYear; // 100% = 1e18 + + constructor(address _pt, uint256 _baseDiscountPerYear) { + require(_baseDiscountPerYear <= 1e18, "invalid discount"); + require(_pt != address(0), "zero address"); + + PT = _pt; + maturity = PTExpiry(PT).expiry(); + baseDiscountPerYear = _baseDiscountPerYear; + } + + function latestRoundData() + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + uint256 timeLeft = (maturity > block.timestamp) ? maturity - block.timestamp : 0; + uint256 discount = getDiscount(timeLeft); + + require(discount <= ONE, "discount overflow"); + + return (0, int256(ONE - discount), 0, block.timestamp, 0); + } + + function decimals() external pure returns (uint8) { + return 18; + } + + function getDiscount(uint256 timeLeft) public view returns (uint256) { + return (timeLeft * baseDiscountPerYear) / SECONDS_PER_YEAR; + } +} + +interface PTExpiry { + function expiry() external view returns (uint256); +} \ No newline at end of file diff --git a/src/feeds/USDeNavBeforeMaturityFeed.sol b/src/feeds/USDeNavBeforeMaturityFeed.sol new file mode 100644 index 00000000..ee1a8680 --- /dev/null +++ b/src/feeds/USDeNavBeforeMaturityFeed.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {IChainlinkBasePriceFeed} from "src/interfaces/IChainlinkFeed.sol"; +import {IERC4626} from "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; + +interface INavFeed { + function maturity() external view returns (uint256); + function decimals() external view returns (uint8); + function latestRoundData() + external + view + returns (uint80, int256, uint256, uint256, uint80); +} +/// @title USDeFeed Before Maturity using NAV +/// @notice A contract to get the USDe price using sUSDe Chainlink Wrapper feed and sUSDe/USDe rate and NAV +contract USDeNavBeforeMaturityFeed { + error DecimalsMismatch(); + error MaturityPassed(); + + IChainlinkBasePriceFeed public immutable sUSDeFeed; + IERC4626 public immutable sUSDe; + INavFeed public immutable navFeed; + + string public description; + + constructor(address _sUSDeFeed, address _sUSDe, address _navFeed) { + sUSDeFeed = IChainlinkBasePriceFeed(_sUSDeFeed); + sUSDe = IERC4626(_sUSDe); + navFeed = INavFeed(_navFeed); + if (sUSDeFeed.decimals() != 18 || sUSDe.decimals() != 18 || navFeed.decimals() != 18) + revert DecimalsMismatch(); + if(navFeed.maturity() <= block.timestamp) revert MaturityPassed(); + description = string( + abi.encodePacked( + "USDe/USD Feed using sUSDe Chainlink feed and sUSDe/USDe rate with NAV" + ) + ); + } + + /** + * @return roundId The round ID of sUSDe Chainlink price feed + * @return USDeUsdPrice The latest USDe price in USD using NAV + * @return startedAt The timestamp when the latest round of Chainlink price feed started + * @return updatedAt The timestamp when the latest round of Chainlink price feed was updated + * @return answeredInRound The round ID in which the answer was computed + */ + function latestRoundData() + public + view + returns (uint80, int256, uint256, uint256, uint80) + { + ( + uint80 roundId, + int256 sUSDePrice, + uint startedAt, + uint updatedAt, + uint80 answeredInRound + ) = sUSDeFeed.latestRoundData(); + + uint256 sUSDeToUSDeRate = sUSDe.convertToAssets(1e18); + + // divide sUSDe/USD by sUSDe/USDe rate to get USDe/USD price + int256 USDeUsdPrice = (sUSDePrice * 1e18) / int256(sUSDeToUSDeRate); + + (,int256 navDiscountedPrice,,,)= navFeed.latestRoundData(); + int256 usdeDiscountPrice = (USDeUsdPrice * navDiscountedPrice) / 1e18; + + return (roundId, usdeDiscountPrice, startedAt, updatedAt, answeredInRound); + } + + /** + @notice Retrieves the latest USDe price + @return price The latest USDe price + */ + function latestAnswer() external view returns (int256) { + (, int256 price, , , ) = latestRoundData(); + return price; + } + + /** + * @notice Retrieves number of decimals for the price feed + * @return decimals The number of decimals for the price feed + */ + function decimals() public pure returns (uint8) { + return 18; + } +} diff --git a/src/interfaces/IMultiMarketConvertHelper.sol b/src/interfaces/IMultiMarketConvertHelper.sol new file mode 100644 index 00000000..620e609f --- /dev/null +++ b/src/interfaces/IMultiMarketConvertHelper.sol @@ -0,0 +1,48 @@ +//SPDX-License-Identifier: None +pragma solidity ^0.8.0; + +interface IMultiMarketConvertHelper { + struct Permit { + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + } + + function convertToCollateral( + address user, + uint256 amount, + bytes calldata data + ) external returns (uint256 collateralAmount); + + function convertToCollateral( + uint256 amount, + address recipient, + bytes calldata data + ) external returns (uint256 collateralAmount); + + function convertToCollateralAndDeposit( + uint256 amount, + address recipient, + bytes calldata data + ) external returns (uint256 collateralAmount); + + function convertFromCollateral( + address user, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function convertFromCollateral( + uint256 amount, + address recipient, + bytes calldata data + ) external returns (uint256); + + function withdrawAndConvertFromCollateral( + uint256 amount, + address recipient, + Permit calldata permit, + bytes calldata data + ) external returns (uint256 underlyingAmount); +} diff --git a/src/interfaces/IPendleHelper.sol b/src/interfaces/IPendleHelper.sol new file mode 100644 index 00000000..b9c8bc03 --- /dev/null +++ b/src/interfaces/IPendleHelper.sol @@ -0,0 +1,47 @@ +//SPDX-License-Identifier: None +pragma solidity ^0.8.0; + +interface IPendleHelper { + struct Permit { + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + } + + function convertToCollateral( + address user, + uint256 amount, + bytes calldata data + ) external returns (uint256 collateralAmount); + + function convertToCollateral( + uint256 amount, + bytes calldata data + ) external returns (uint256 collateralAmount); + + function convertToCollateralAndDeposit( + uint256 amount, + address recipient, + bytes calldata data + ) external returns (uint256 collateralAmount); + + function convertFromCollateral( + address user, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function convertFromCollateral( + uint256 amount, + address recipient, + bytes calldata data + ) external returns (uint256); + + function withdrawAndConvertFromCollateral( + uint256 amount, + address recipient, + Permit calldata permit, + bytes calldata data + ) external returns (uint256 underlyingAmount); +} diff --git a/src/util/ALEV2.sol b/src/util/ALEV2.sol new file mode 100644 index 00000000..37894e71 --- /dev/null +++ b/src/util/ALEV2.sol @@ -0,0 +1,722 @@ +//SPDX-License-Identifier: None +pragma solidity ^0.8.0; + +import "src/interfaces/IMarket.sol"; +import "src/interfaces/IPendleHelper.sol"; +import {CurveDBRHelper} from "src/util/CurveDBRHelper.sol"; +import {Ownable} from "lib/openzeppelin-contracts/contracts/access/Ownable.sol"; +import {ReentrancyGuard} from "lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol"; +import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; + +interface IDBR { + function markets(address) external view returns (bool); +} + +interface IERC3156FlashBorrower { + /** + * @dev Receive a flash loan. + * @param initiator The initiator of the loan. + * @param token The loan currency. + * @param amount The amount of tokens lent. + * @param fee The additional amount of tokens to repay. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" + */ + function onFlashLoan( + address initiator, + address token, + uint256 amount, + uint256 fee, + bytes calldata data + ) external returns (bytes32); +} + +interface IERC3156FlashLender { + /** + * @dev Initiate a flash loan. + * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. + * @param token The loan currency. + * @param value The amount of tokens lent. + * @param data Arbitrary data structure, intended to contain user-defined parameters. + */ + function flashLoan( + IERC3156FlashBorrower receiver, + address token, + uint256 value, + bytes calldata data + ) external returns (bool); +} + +// Accelerated leverage engine +contract ALEV2 is + Ownable, + ReentrancyGuard, + CurveDBRHelper, + IERC3156FlashBorrower +{ + using SafeERC20 for IERC20; + error CollateralNotSet(); + error MarketNotSet(address market); + error SwapFailed(); + error DOLAInvalidBorrow(uint256 expected, uint256 actual); + error DOLAInvalidRepay(uint256 expected, uint256 actual); + error InvalidProxyAddress(); + error InvalidHelperAddress(); + error InvalidAction(bytes32 action); + error NotFlashMinter(address caller); + error NotALE(address caller); + error NothingToDeposit(); + error DepositFailed(uint256 expected, uint256 actual); + error WithdrawFailed(uint256 expected, uint256 actual); + error TotalSupplyChanged(uint256 expected, uint256 actual); + error CollateralIsZero(); + error NoMarket(address market); + error MarketSetupFailed( + address market, + address buySellToken, + address collateral, + address helper + ); + + mapping(address => bool) public isExchangeProxy; + + IDBR public constant DBR = IDBR(0xAD038Eb671c44b853887A7E32528FaB35dC5D710); + + IERC3156FlashLender public constant flash = + IERC3156FlashLender(0x6C5Fdc0c53b122Ae0f15a863C349f3A481DE8f1F); + + bytes32 public constant CALLBACK_SUCCESS = + keccak256("ERC3156FlashBorrower.onFlashLoan"); + + bytes32 public constant LEVERAGE = keccak256("LEVERAGE"); + bytes32 public constant DELEVERAGE = keccak256("DELEVERAGE"); + + struct Market { + IERC20 buySellToken; + IERC20 collateral; + IPendleHelper helper; + bool useProxy; + } + + struct Permit { + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + } + + struct DBRHelper { + uint256 amountIn; // DOLA or DBR + uint256 minOut; // DOLA or DBR + uint256 dola; // DOLA to extra borrow or extra repay + } + + event LeverageUp( + address indexed market, + address indexed account, + uint256 dolaFlashMinted, // DOLA flash minted for buying collateral only + uint256 collateralDeposited, // amount of collateral deposited into the escrow + uint256 dolaBorrowed, // amount of DOLA borrowed on behalf of the user + uint256 dolaForDBR // amount of DOLA used for buying DBR + ); + + event LeverageDown( + address indexed market, + address indexed account, + uint256 dolaFlashMinted, // Flash minted DOLA for repaying leverage only + uint256 collateralSold, // amount of collateral/underlying sold + uint256 dolaUserRepaid, // amount of DOLA deposited by the user as part of the repay + uint256 dbrSoldForDola // amount of DBR sold for DOLA + ); + + event Deposit( + address indexed market, + address indexed account, + address indexed token, // token used for initial deposit (could be collateral or buySellToken) + uint256 depositAmount + ); + + event NewMarket( + address indexed market, + address indexed buySellToken, + address collateral, + address indexed helper + ); + + event NewHelper(address indexed market, address indexed helper); + + // Mapping of market to Market structs + // NOTE: in normal cases sellToken/buyToken is the collateral token, + // in other cases it could be different (eg. st-yCRV is collateral, yCRV is the token to be swapped from/to DOLA) + // or with DOLA curve LPs, LP token is the collateral and DOLA is the token to be swapped from/to + mapping(address => Market) public markets; + + constructor( + address _pool + ) Ownable(msg.sender) CurveDBRHelper(_pool) { + dola.approve(address(flash), type(uint).max); + } + + /// @notice Allow an exchange proxy + /// @param _proxy The proxy address + function allowProxy(address _proxy) external onlyOwner { + if (_proxy == address(0)) revert InvalidProxyAddress(); + isExchangeProxy[_proxy] = true; + } + + /// @notice Deny an exchange proxy + /// @param _proxy The proxy address + function denyProxy(address _proxy) external onlyOwner { + if (_proxy == address(0)) revert InvalidProxyAddress(); + isExchangeProxy[_proxy] = false; + } + + /// @notice Set the market for a collateral token + /// @param _buySellToken The token which will be bought/sold (usually the collateral token), probably underlying if there's a helper + /// @param _market The market contract + /// @param _helper Optional helper contract to transform collateral to buySelltoken and viceversa + /// @param useProxy Whether to use the Exchange Proxy or not + function setMarket( + address _market, + address _buySellToken, + address _helper, + bool useProxy + ) external onlyOwner { + if (!DBR.markets(_market)) revert NoMarket(_market); + + address collateral = IMarket(_market).collateral(); + if (_helper == address(0) && _buySellToken != collateral) { + revert MarketSetupFailed( + _market, + _buySellToken, + IMarket(_market).collateral(), + _helper + ); + } + + markets[_market].buySellToken = IERC20(_buySellToken); + markets[_market].collateral = IERC20(collateral); + markets[_market].buySellToken.approve(_market, type(uint256).max); + + if ( _buySellToken != collateral) { + markets[_market].collateral.approve(_market, type(uint256).max); + } + + if (_helper != address(0)) { + markets[_market].helper = IPendleHelper(_helper); + markets[_market].buySellToken.approve(_helper, type(uint256).max); + markets[_market].collateral.approve(_helper, type(uint256).max); + } + + + markets[_market].useProxy = useProxy; + emit NewMarket(_market, _buySellToken, collateral, _helper); + } + + /// @notice Update the helper contract + /// @param _market The market we want to update the helper contract for + /// @param _helper The helper contract + function updateMarketHelper( + address _market, + address _helper + ) external onlyOwner { + if (address(markets[_market].buySellToken) == address(0)) + revert MarketNotSet(_market); + if (_helper == address(0)) revert InvalidHelperAddress(); + + address oldHelper = address(markets[_market].helper); + markets[_market].buySellToken.approve(oldHelper, 0); + markets[_market].collateral.approve(oldHelper, 0); + + markets[_market].helper = IPendleHelper(_helper); + markets[_market].buySellToken.approve(_helper, type(uint256).max); + markets[_market].collateral.approve(_helper, type(uint256).max); + + emit NewHelper(_market, _helper); + } + + /// @notice Leverage user position by minting DOLA, buying collateral, deposting into the user escrow and borrow DOLA on behalf to repay the minted DOLA + /// @dev Requires user to sign message to permit the contract to borrow DOLA on behalf + /// @param value Amount of DOLA to flash mint/burn + /// @param market The market contract + /// @param exchangeProxy The exchange proxy contract if any + /// @param swapCallData The `data` field from the API response. + /// @param permit Permit data + /// @param helperData Optional helper data in case the collateral needs to be transformed + /// @param dbrData Optional data in case the user wants to buy DBR and also withdraw some DOLA + function leveragePosition( + uint256 value, + address market, + address exchangeProxy, + bytes calldata swapCallData, + Permit calldata permit, + bytes calldata helperData, + DBRHelper calldata dbrData + ) public payable nonReentrant { + if (address(markets[market].buySellToken) == address(0)) + revert MarketNotSet(market); + + bytes memory data = abi.encode( + LEVERAGE, + msg.sender, + market, + exchangeProxy, + 0, // unused + swapCallData, + permit, + helperData, + dbrData + ); + + flash.flashLoan( + IERC3156FlashBorrower(address(this)), + address(dola), + value, + data + ); + } + + /// @notice Deposit collateral and instantly leverage user position by minting DOLA, buying collateral, deposting into the user escrow and borrow DOLA on behalf to repay the minted DOLA + /// @dev Requires user to sign message to permit the contract to borrow DOLA on behalf + /// @param initialDeposit Amount of collateral or underlying (in case of helper) to deposit + /// @param value Amount of DOLA to borrow + /// @param market The market address + /// @param exchangeProxy The exchange proxy contract if any + /// @param swapCallData The `data` field from the API response. + /// @param permit Permit data + /// @param helperData Optional helper data in case the collateral needs to be transformed + /// @param dbrData Optional data in case the user wants to buy DBR and also withdraw some DOLA + /// @param depositCollateral Whether the initialDeposit is the collateral or the underlying entry asset + function depositAndLeveragePosition( + uint256 initialDeposit, + uint256 value, + address market, + address exchangeProxy, + bytes calldata swapCallData, + Permit calldata permit, + bytes calldata helperData, + DBRHelper calldata dbrData, + bool depositCollateral + ) external payable { + if (initialDeposit == 0) revert NothingToDeposit(); + + IERC20 depositToken; + + if (depositCollateral) { + depositToken = markets[market].collateral; + } else { + depositToken = markets[market].buySellToken; + } + + depositToken.safeTransferFrom( + msg.sender, + address(this), + initialDeposit + ); + emit Deposit(market, msg.sender, address(depositToken), initialDeposit); + + leveragePosition( + value, + market, + exchangeProxy, + swapCallData, + permit, + helperData, + dbrData + ); + } + + /// @notice Repay a DOLA loan and withdraw collateral from the escrow + /// @dev Requires user to sign message to permit the contract to withdraw collateral from the escrow + /// @param value Amount of DOLA to repay + /// @param market The market contract + /// @param exchangeProxy The exchange proxy contract if any + /// @param collateralAmount Collateral amount to withdraw from the escrow + /// @param swapCallData The `data` field from the API response. + /// @param permit Permit data + /// @param helperData Optional helper data in case collateral needs to be transformed + /// @param dbrData Optional data in case the user wants to sell DBR + function deleveragePosition( + uint256 value, + address market, + address exchangeProxy, + uint256 collateralAmount, + bytes calldata swapCallData, + Permit calldata permit, + bytes calldata helperData, + DBRHelper calldata dbrData + ) external payable nonReentrant { + if (address(markets[market].buySellToken) == address(0)) + revert MarketNotSet(market); + + bytes memory data = abi.encode( + DELEVERAGE, + msg.sender, + market, + exchangeProxy, + collateralAmount, + swapCallData, + permit, + helperData, + dbrData + ); + + flash.flashLoan( + IERC3156FlashBorrower(address(this)), + address(dola), + value, + data + ); + } + + function onFlashLoan( + address initiator, + address, + uint256 amount, + uint256, + bytes calldata data + ) external returns (bytes32) { + if (initiator != address(this)) revert NotALE(initiator); + if (msg.sender != address(flash)) revert NotFlashMinter(msg.sender); + + (bytes32 ACTION, , , , , , , , ) = abi.decode( + data, + ( + bytes32, + address, + address, + address, + uint256, + bytes, + Permit, + bytes, + DBRHelper + ) + ); + + if (ACTION == LEVERAGE) _onFlashLoanLeverage(amount, data); + else if (ACTION == DELEVERAGE) _onFlashLoanDeleverage(amount, data); + else revert InvalidAction(bytes32(ACTION)); + + return CALLBACK_SUCCESS; + } + + function _onFlashLoanLeverage(uint256 _value, bytes memory data) internal { + ( + , + address _user, + address _market, + address _proxy, + , + bytes memory _swapCallData, + Permit memory _permit, + bytes memory _helperData, + DBRHelper memory _dbrData + ) = abi.decode( + data, + ( + bytes32, + address, + address, + address, + uint256, + bytes, + Permit, + bytes, + DBRHelper + ) + ); + // Call the encoded swap function call on the contract at `swapTarget`, + // passing along any ETH attached to this function call to cover protocol fees. + if (markets[_market].useProxy) { + if(!isExchangeProxy[_proxy]) revert InvalidProxyAddress(); + dola.approve(_proxy, _value); + (bool success, ) = payable(_proxy).call{value: msg.value}( + _swapCallData + ); + if (!success) revert SwapFailed(); + } + + // Actual collateral/buyToken bought + uint256 collateralAmount = markets[_market].buySellToken.balanceOf( + address(this) + ); + if (collateralAmount == 0) revert CollateralIsZero(); + + // If there's a helper contract, the buyToken has to be transformed + if (address(markets[_market].helper) != address(0)) { + collateralAmount = _convertToCollateral( + _user, + collateralAmount, + _market, + _helperData + ); + } + + // Deposit and borrow on behalf + IMarket(_market).deposit( + _user, + markets[_market].collateral.balanceOf(address(this)) + ); + + _borrowDola(_user, _value, _permit, _dbrData, IMarket(_market)); + + if (_dbrData.dola != 0) dola.transfer(_user, _dbrData.dola); + + if (_dbrData.amountIn > 0 && _dbrData.minOut > 0) + _buyDbr(_dbrData.amountIn, _dbrData.minOut, _user); + + _refundExcess(_user, _value); + + emit LeverageUp( + _market, + _user, + _value, + collateralAmount, + _dbrData.dola, + _dbrData.amountIn + ); + } + + function _onFlashLoanDeleverage( + uint256 _value, + bytes memory data + ) internal { + ( + , + address _user, + address _market, + address _proxy, + uint256 _collateralAmount, + bytes memory _swapCallData, + Permit memory _permit, + bytes memory _helperData, + DBRHelper memory _dbrData + ) = abi.decode( + data, + ( + bytes32, + address, + address, + address, + uint256, + bytes, + Permit, + bytes, + DBRHelper + ) + ); + + _repayAndWithdraw( + _user, + _value, + _collateralAmount, + _permit, + _dbrData, + IMarket(_market) + ); + + IERC20 sellToken = markets[_market].buySellToken; + + // If there's a helper contract, the collateral has to be transformed + if (address(markets[_market].helper) != address(0)) { + _collateralAmount = _convertToAsset( + _user, + _collateralAmount, + _market, + sellToken, + _helperData + ); + // Reimburse leftover collateral from conversion if any + uint256 collateralLeft = markets[_market].collateral.balanceOf( + address(this) + ); + + if (collateralLeft != 0) { + markets[_market].collateral.safeTransfer(_user, collateralLeft); + } + } + + // Call the encoded swap function call on the contract at `swapTarget`, + // passing along any ETH attached to this function call to cover protocol fees. + // NOTE: This will swap the collateral or helperCollateral for DOLA + if (markets[_market].useProxy) { + if(!isExchangeProxy[_proxy]) revert InvalidProxyAddress(); + // Approve sellToken for exchangeProxy + sellToken.approve(_proxy, 0); + sellToken.approve(_proxy, _collateralAmount); + (bool success, ) = payable(_proxy).call{value: msg.value}( + _swapCallData + ); + if (!success) revert SwapFailed(); + } + + if (address(markets[_market].helper) == address(0)) { + uint256 collateralAvailable = markets[_market].collateral.balanceOf( + address(this) + ); + + if (collateralAvailable != 0) { + markets[_market].collateral.safeTransfer( + _user, + collateralAvailable + ); + } + } else if (address(sellToken) != address(dola)) { + uint256 sellTokenBal = sellToken.balanceOf(address(this)); + // Send any leftover sellToken to the sender + if (sellTokenBal != 0) sellToken.safeTransfer(_user, sellTokenBal); + } + + if (_dbrData.amountIn > 0 && _dbrData.minOut > 0) { + dbr.transferFrom(_user, address(this), _dbrData.amountIn); + _sellDbr(_dbrData.amountIn, _dbrData.minOut, _user); + } + + _refundExcess(_user, _value); + + emit LeverageDown( + _market, + _user, + _value, + _collateralAmount, + _dbrData.dola, + _dbrData.amountIn + ); + } + + /// @notice Borrow DOLA on behalf of the user + /// @param _value Amount of DOLA to borrow + /// @param _permit Permit data + /// @param _dbrData DBR data + /// @param market The market contract + function _borrowDola( + address _user, + uint256 _value, + Permit memory _permit, + DBRHelper memory _dbrData, + IMarket market + ) internal { + uint256 dolaToBorrow = _value + _dbrData.dola + _dbrData.amountIn; + // We borrow the amount of DOLA we minted before plus the amount for buying DBR if any + market.borrowOnBehalf( + _user, + dolaToBorrow, + _permit.deadline, + _permit.v, + _permit.r, + _permit.s + ); + + if (dola.balanceOf(address(this)) < dolaToBorrow) + revert DOLAInvalidBorrow( + dolaToBorrow, + dola.balanceOf(address(this)) + ); + } + + /// @notice Repay DOLA loan and withdraw collateral from the escrow + /// @param _value Amount of DOLA to repay + /// @param _collateralAmount Collateral amount to withdraw from the escrow + /// @param _permit Permit data + /// @param _dbrData DBR data + /// @param market The market contract + function _repayAndWithdraw( + address _user, + uint256 _value, + uint256 _collateralAmount, + Permit memory _permit, + DBRHelper memory _dbrData, + IMarket market + ) internal { + if (_dbrData.dola != 0) { + dola.transferFrom(_user, address(this), _dbrData.dola); + _value += _dbrData.dola; + } + dola.approve(address(market), _value); + market.repay(_user, _value); + + // withdraw amount from ZERO EX quote + market.withdrawOnBehalf( + _user, + _collateralAmount, + _permit.deadline, + _permit.v, + _permit.r, + _permit.s + ); + } + + /// @notice convert a collateral amount into the underlying asset + /// @param _user The user address + /// @param _collateralAmount Collateral amount to convert + /// @param _market The market contract + /// @param sellToken The sell token (the underlying asset) + /// @param _helperData Optional helper data + /// @return assetAmount The amount of sellToken/underlying after the conversion + function _convertToAsset( + address _user, + uint256 _collateralAmount, + address _market, + IERC20 sellToken, + bytes memory _helperData + ) internal returns (uint256) { + // Collateral amount is now converted into sellToken + uint256 assetAmount = markets[_market].helper.convertFromCollateral( + _user, + _collateralAmount, + _helperData + ); + uint256 actualAssetAmount = sellToken.balanceOf(address(this)); + + if (actualAssetAmount < assetAmount) + revert WithdrawFailed(assetAmount, actualAssetAmount); + + return actualAssetAmount; + } + + /// @notice convert the underlying asset amount into the collateral + /// @param _user The user address + /// @param _assetAmount The amount of sellToken/underlying to convert + /// @param _market The market contract + /// @param _helperData Optional helper data + /// @return collateralAmount The amount of collateral after the conversion + function _convertToCollateral( + address _user, + uint256 _assetAmount, + address _market, + bytes memory _helperData + ) internal returns (uint256) { + // Collateral amount is now converted + uint256 collateralAmount = markets[_market].helper.convertToCollateral( + _user, + _assetAmount, + _helperData + ); + + uint256 actualCollateralAmount = markets[_market].collateral.balanceOf( + address(this) + ); + if (actualCollateralAmount < collateralAmount) + revert DepositFailed(collateralAmount, actualCollateralAmount); + + return actualCollateralAmount; + } + + /// @notice Send any extra DOLA and ETH to the user + /// @param _user The user address + /// @param _value The amount of flash borrowed DOLA to be repaid + function _refundExcess(address _user, uint256 _value) internal { + uint256 balance = dola.balanceOf(address(this)); + if (balance < _value) revert DOLAInvalidRepay(_value, balance); + // Send any extra DOLA to the sender + if (balance > _value) dola.transfer(_user, balance - _value); + // Refund any unspent protocol fees to the sender. + if (address(this).balance > 0) + payable(_user).transfer(address(this).balance); + } + + // solhint-disable-next-line no-empty-blocks + receive() external payable {} +} diff --git a/src/util/CurveDolaLPHelper.sol b/src/util/CurveDolaLPHelper.sol index 7419fa8d..b919c8b3 100644 --- a/src/util/CurveDolaLPHelper.sol +++ b/src/util/CurveDolaLPHelper.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.13; import {IMarket} from "src/interfaces/IMarket.sol"; import {Sweepable, SafeERC20, IERC20} from "src/util/Sweepable.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {IMultiMarketConvertHelper} from "src/interfaces/IMultiMarketConvertHelper.sol"; import {ICurvePool} from "src/interfaces/ICurvePool.sol"; import {IYearnVaultV2} from "src/interfaces/IYearnVaultV2.sol"; @@ -14,7 +14,7 @@ import {IYearnVaultV2} from "src/interfaces/IYearnVaultV2.sol"; * Can also be used by anyone to perform add/remove liquidity from and to DOLA and deposit/withdraw operations. **/ -contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { +contract CurveDolaLPHelper is Sweepable, IMultiMarketConvertHelper { using SafeERC20 for IERC20; error InsufficientLP(); @@ -61,11 +61,12 @@ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of LP token received. */ - function transformToCollateral( + function convertToCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 collateralAmount) { - collateralAmount = transformToCollateral(amount, msg.sender, data); + collateralAmount = convertToCollateral(amount, msg.sender, data); } /** @@ -76,7 +77,7 @@ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of LP or Yearn token received. */ - function transformToCollateral( + function convertToCollateral( uint256 amount, address recipient, bytes calldata data @@ -115,11 +116,12 @@ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function transformFromCollateral( + function convertFromCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 dolaAmount) { - dolaAmount = transformFromCollateral(amount, msg.sender, data); + dolaAmount = convertFromCollateral(amount, msg.sender, data); } /** @@ -130,7 +132,7 @@ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function transformFromCollateral( + function convertFromCollateral( uint256 amount, address recipient, bytes calldata data @@ -171,7 +173,7 @@ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of collateral deposited into the market. */ - function transformToCollateralAndDeposit( + function convertToCollateralAndDeposit( uint256 assets, address recipient, bytes calldata data @@ -180,7 +182,7 @@ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { _revertIfMarketNotSet(market); // Convert DOLA to LP or Yearn token - uint256 amount = transformToCollateral(assets, address(this), data); + uint256 amount = convertToCollateral(assets, address(this), data); IYearnVaultV2 vault = markets[market].vault; @@ -212,7 +214,7 @@ contract CurveDolaLPHelper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function withdrawAndTransformFromCollateral( + function withdrawAndConvertFromCollateral( uint256 amount, address recipient, Permit calldata permit, diff --git a/src/util/CurveDolaLPHelperDynamic.sol b/src/util/CurveDolaLPHelperDynamic.sol index cd3eecf8..a9df6be9 100644 --- a/src/util/CurveDolaLPHelperDynamic.sol +++ b/src/util/CurveDolaLPHelperDynamic.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.13; import {IMarket} from "src/interfaces/IMarket.sol"; import {Sweepable, SafeERC20, IERC20} from "src/util/Sweepable.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {IMultiMarketConvertHelper} from "src/interfaces/IMultiMarketConvertHelper.sol"; import {ICurvePool} from "src/interfaces/ICurvePool.sol"; import {IYearnVaultV2} from "src/interfaces/IYearnVaultV2.sol"; @@ -14,7 +14,7 @@ import {IYearnVaultV2} from "src/interfaces/IYearnVaultV2.sol"; * Can also be used by anyone to perform add/remove liquidity from and to DOLA and deposit/withdraw operations. **/ -contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { +contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketConvertHelper { using SafeERC20 for IERC20; error InsufficientLP(); @@ -60,11 +60,12 @@ contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of LP token received. */ - function transformToCollateral( + function convertToCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 collateralAmount) { - collateralAmount = transformToCollateral(amount, msg.sender, data); + collateralAmount = convertToCollateral(amount, msg.sender, data); } /** @@ -75,7 +76,7 @@ contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of LP or Yearn token received. */ - function transformToCollateral( + function convertToCollateral( uint256 amount, address recipient, bytes calldata data @@ -114,11 +115,12 @@ contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function transformFromCollateral( + function convertFromCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 dolaAmount) { - dolaAmount = transformFromCollateral(amount, msg.sender, data); + dolaAmount = convertFromCollateral(amount, msg.sender, data); } /** @@ -129,7 +131,7 @@ contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function transformFromCollateral( + function convertFromCollateral( uint256 amount, address recipient, bytes calldata data @@ -170,7 +172,7 @@ contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of collateral deposited into the market. */ - function transformToCollateralAndDeposit( + function convertToCollateralAndDeposit( uint256 assets, address recipient, bytes calldata data @@ -179,7 +181,7 @@ contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { _revertIfMarketNotSet(market); // Convert DOLA to LP or Yearn token - uint256 amount = transformToCollateral(assets, address(this), data); + uint256 amount = convertToCollateral(assets, address(this), data); IYearnVaultV2 vault = markets[market].vault; @@ -211,7 +213,7 @@ contract CurveDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function withdrawAndTransformFromCollateral( + function withdrawAndConvertFromCollateral( uint256 amount, address recipient, Permit calldata permit, diff --git a/src/util/CurveSDolaLPHelperDynamic.sol b/src/util/CurveSDolaLPHelperDynamic.sol index a9b82378..688e0028 100644 --- a/src/util/CurveSDolaLPHelperDynamic.sol +++ b/src/util/CurveSDolaLPHelperDynamic.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.13; import {IMarket} from "src/interfaces/IMarket.sol"; import {Sweepable, SafeERC20, IERC20} from "src/util/Sweepable.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {IMultiMarketConvertHelper} from "src/interfaces/IMultiMarketConvertHelper.sol"; import {ICurvePool} from "src/interfaces/ICurvePool.sol"; import {IYearnVaultV2} from "src/interfaces/IYearnVaultV2.sol"; import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; @@ -14,7 +14,7 @@ import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol"; * Can also be used by anyone to perform add/remove liquidity from and to DOLA and deposit/withdraw operations. **/ -contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { +contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketConvertHelper { using SafeERC20 for IERC20; error InsufficientLP(); @@ -64,11 +64,12 @@ contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of LP token received. */ - function transformToCollateral( + function convertToCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 collateralAmount) { - collateralAmount = transformToCollateral(amount, msg.sender, data); + collateralAmount = convertToCollateral(amount, msg.sender, data); } /** @@ -79,7 +80,7 @@ contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of LP or Yearn token received. */ - function transformToCollateral( + function convertToCollateral( uint256 amount, address recipient, bytes calldata data @@ -118,11 +119,12 @@ contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function transformFromCollateral( + function convertFromCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 dolaAmount) { - return transformFromCollateral(amount, msg.sender, data); + return convertFromCollateral(amount, msg.sender, data); } /** @@ -133,7 +135,7 @@ contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function transformFromCollateral( + function convertFromCollateral( uint256 amount, address recipient, bytes calldata data @@ -174,7 +176,7 @@ contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return collateralAmount The amount of collateral deposited into the market. */ - function transformToCollateralAndDeposit( + function convertToCollateralAndDeposit( uint256 assets, address recipient, bytes calldata data @@ -183,7 +185,7 @@ contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { _revertIfMarketNotSet(market); // Convert DOLA to LP or Yearn token - uint256 amount = transformToCollateral(assets, address(this), data); + uint256 amount = convertToCollateral(assets, address(this), data); IYearnVaultV2 vault = markets[market].vault; @@ -215,7 +217,7 @@ contract CurveSDolaLPHelperDynamic is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return dolaAmount The amount of DOLA redeemed. */ - function withdrawAndTransformFromCollateral( + function withdrawAndConvertFromCollateral( uint256 amount, address recipient, Permit calldata permit, diff --git a/src/util/ERC4626Helper.sol b/src/util/ERC4626Helper.sol index 7aba41e2..6b16d36c 100644 --- a/src/util/ERC4626Helper.sol +++ b/src/util/ERC4626Helper.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.13; import {IERC4626} from "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; import {IMarket} from "src/interfaces/IMarket.sol"; import {Sweepable, SafeERC20, IERC20} from "src/util/Sweepable.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {IMultiMarketConvertHelper} from "src/interfaces/IMultiMarketConvertHelper.sol"; /** * @title ERC4626 Accelerated Leverage Engine Helper @@ -13,7 +13,7 @@ import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformH * Can also be used by anyone to perform wrap/unwrap and deposit/withdraw operations. **/ -contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { +contract ERC4626Helper is Sweepable, IMultiMarketConvertHelper { using SafeERC20 for IERC20; error InsufficientShares(); @@ -48,11 +48,12 @@ contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return shares The amount of ERC4626 token received. */ - function transformToCollateral( + function convertToCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 shares) { - shares = transformToCollateral(amount, msg.sender, data); + shares = convertToCollateral(amount, msg.sender, data); } /** @@ -63,7 +64,7 @@ contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return shares The amount of ERC4626 token received. */ - function transformToCollateral( + function convertToCollateral( uint256 amount, address recipient, bytes calldata data @@ -86,11 +87,12 @@ contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return assets The amount of underlying token redeemed. */ - function transformFromCollateral( + function convertFromCollateral( + address, uint256 amount, bytes calldata data ) external override returns (uint256 assets) { - assets = transformFromCollateral(amount, msg.sender, data); + assets = convertFromCollateral(amount, msg.sender, data); } /** @@ -102,7 +104,7 @@ contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return assets The amount of underlying token redeemed. */ - function transformFromCollateral( + function convertFromCollateral( uint256 amount, address recipient, bytes calldata data @@ -128,7 +130,7 @@ contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return shares The amount of ERC4626 token deposited into the market. */ - function transformToCollateralAndDeposit( + function convertToCollateralAndDeposit( uint256 assets, address recipient, bytes calldata data @@ -138,7 +140,7 @@ contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { IERC4626 vault = markets[market].vault; - shares = transformToCollateral(assets, address(this), data); + shares = convertToCollateral(assets, address(this), data); uint256 actualShares = vault.balanceOf(address(this)); if (shares > actualShares) revert InsufficientShares(); @@ -155,7 +157,7 @@ contract ERC4626Helper is Sweepable, IMultiMarketTransformHelper { * @param data The encoded address of the market. * @return assets The amount of underlying token withdrawn from the ERC4626 vault. */ - function withdrawAndTransformFromCollateral( + function withdrawAndConvertFromCollateral( uint256 amount, address recipient, Permit calldata permit, diff --git a/src/util/PendlePTHelper.sol b/src/util/PendlePTHelper.sol new file mode 100644 index 00000000..df65a1fa --- /dev/null +++ b/src/util/PendlePTHelper.sol @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {IMarket} from "src/interfaces/IMarket.sol"; +import {Sweepable, SafeERC20, IERC20} from "src/util/Sweepable.sol"; +import {IPendleHelper} from "src/interfaces/IPendleHelper.sol"; +import "bytes-utils/BytesLib.sol"; + +interface IPendlePT { + function expiry() external view returns (uint256); +} +/** + * @title Pendle PT ALE and market helper + * @notice This contract is a generalized ALE and market helper contract for Pendle PT tokens from and to DOLA. + * @dev Carefully prepare the router calldata from Pendle API when using it from the ALE: + * When converting TO collateral, the receiver in Pendle API has to be set to this contract address + * When converting FROM collateral, the receiver in Pendle API has to be set to the ALE address. + * The Pendle Router can either SWAP DOLA for PT or MINT PT and YT as well SWAP PT for DOLA or REDEEM PT (using YT before maturity) for DOLA. + * Do not use this contract for other routes otherwise won't work properly. + **/ + +contract PendlePTHelper is Sweepable, IPendleHelper { + using SafeERC20 for IERC20; + using BytesLib for bytes; + + error InsufficientDOLA(); + error InsufficientPT(); + error InsufficientYT(); + error MarketNotSet(address market); + error PendleSwapFailed(); + error NotALE(); + error InvalidRecipient(); + error InvalidSelector(); + error Slice_OutOfBounds(); + + struct PT { + address pt; + uint96 maturity; + address yt; + } + + event MarketSet( + address indexed market, + address indexed pt, + address indexed yt, + uint256 maturity + ); + event MarketRemoved(address indexed market); + event PTOut( + uint256 dolaIn, + uint256 ptOut, + address indexed from, + address indexed to + ); + event DolaOut( + uint256 ptIn, + uint256 dolaOut, + address indexed from, + address indexed to + ); + event YTOut(uint256 amount, address indexed from, address indexed to); + event YTIn(uint256 amount, address indexed from); + + IERC20 public immutable DOLA; + address public immutable router; + address public immutable ale; + + /// @notice Mapping of market addresses to their associated PT and YT tokens. + mapping(address => PT) public markets; + + bytes4 public constant SWAP_PT = hex"c81f847a"; + bytes4 public constant MINT_PT = hex"d0f42385"; + bytes4 public constant SWAP_DOLA = hex"594a88cc"; + bytes4 public constant REDEEM_PT = hex"47f1de22"; + + /** @dev Constructor + @param _gov The address of Inverse Finance governance + @param _guardian The address of the guardian + @param _pendleRouter The address of the Pendle Router + **/ + constructor( + address _gov, + address _guardian, + address _dola, + address _pendleRouter, + address _ale + ) Sweepable(_gov, _guardian) { + DOLA = IERC20(_dola); + router = _pendleRouter; + ale = _ale; + } + + modifier onlyALE() { + if (msg.sender != ale) revert NotALE(); + _; + } + /** + * @notice Convert DOLA to PT or PT and YT + * @dev Can only be used by the ALE. Carefully review input data for Pendle API. + * The receiver in Pendle API has to be set to this contract address. + * If a MINT is performed, YT will be sent to the user. + * @param amount The amount of DOLA to be converted. + * @param data Encoded address of the market, minimum amount of PT to receive (and possibly YT), and Pendle callData. + * @return collateralAmount The amount of PT (and possibly YT) token received. + */ + function convertToCollateral( + address user, + uint256 amount, + bytes calldata data + ) external override onlyALE returns (uint256 collateralAmount) { + return _convertToCollateral(user, amount, msg.sender, data); + } + + /** + * @notice Helper function to convert DOLA to PT or PT and YT, sending PT and YT to msg.sender (probably better using directly the Pendle Router for saving gas) + * @dev The receiver in Pendle API has to be set to this contract address. + * @param amount The amount of DOLA to be converted. + * @param data Encoded address of the market, minimum amount of PT to receive (and possibly YT), and Pendle callData. + * @return collateralAmount The amount of PT (and possibly YT) token received. + */ + function convertToCollateral( + uint256 amount, + bytes calldata data + ) external override returns (uint256 collateralAmount) { + return _convertToCollateral(msg.sender, amount, msg.sender, data); + } + + /** + * @notice Convert DOLA to PT or PT and YT and deposit PT amount on behalf of recipient, sending YT to the msg.sender + * @dev The receiver in Pendle API has to be set to this contract address. + * @param assets Amount of DOLA to be converted and deposited + * @param recipient The address on behalf of which the PT tokens are deposited. + * @param data Encoded address of the market, minimum amount of PT to receive (and possibly YT), and Pendle callData. + * @return collateralAmount The amount of collateral deposited into the market. + */ + function convertToCollateralAndDeposit( + uint256 assets, + address recipient, + bytes calldata data + ) external override returns (uint256) { + (address market, , ) = abi.decode(data, (address, uint256, bytes)); + + // Convert DOLA to PT token + uint256 amount = _convertToCollateral( + msg.sender, + assets, + address(this), + data + ); + + // Deposit PT into Market + IERC20(markets[market].pt).approve(market, amount); + IMarket(market).deposit(recipient, amount); + + return amount; + } + + function _convertToCollateral( + address user, + uint256 amount, + address recipient, + bytes calldata data + ) internal returns (uint256 collateralAmount) { + ( + address market, + uint256 minOut, // Minimum amount of PT to receive (and possibly YT) + bytes memory callData + ) = abi.decode(data, (address, uint256, bytes)); + _revertIfMarketNotSet(market); + + bytes4 selector = bytes4(callData.slice(0, 4)); + if (selector != SWAP_PT && selector != MINT_PT) + revert InvalidSelector(); + + DOLA.safeTransferFrom(msg.sender, address(this), amount); + DOLA.approve(router, amount); + + _callRouter(callData); + + IERC20 pt = IERC20(markets[market].pt); + uint256 ptBal = pt.balanceOf(address(this)); + + if (ptBal < minOut) revert InsufficientPT(); + if (recipient != address(this)) pt.safeTransfer(recipient, ptBal); + + emit PTOut(amount, ptBal, msg.sender, recipient); + + if (selector == MINT_PT) { + IERC20 yt = IERC20(markets[market].yt); + // Send YT to user if minted + uint256 ytBalMinted = yt.balanceOf(address(this)); + if (ytBalMinted < minOut) revert InsufficientYT(); + yt.safeTransfer(user, ytBalMinted); + emit YTOut(ytBalMinted, msg.sender, user); + } + + return ptBal; + } + /** + * @notice Swap or Redeem PT token for DOLA (Redemption using YT if before maturity). + * @dev Can only be used by the ALE. Carefully review input data for Pendle API. + * The receiver in Pendle API has to be set same as the recipient (ALE) + * If a REDEEM is performed, ensure the user has enough balance and allowance for YT if before maturity + * @param amount The amount of PT token to be redeemed (and YT if specified). + * @param data Encoded address of the market, minimum amount of DOLA to receive and Pendle callData. + * @return dolaAmount The amount of DOLA redeemed. + */ + function convertFromCollateral( + address user, + uint256 amount, + bytes calldata data + ) external override onlyALE returns (uint256 dolaAmount) { + return _convertFromCollateral(user, amount, msg.sender, data); + } + + /** + * @notice Redeem Collateral for DOLA. + * @dev The receiver in Pendle API has to be set same as the recipient. + * If a REDEEM is performed, ensure msg.sender has enough balance and allowance for YT if before maturity. + * @param amount The amount of PT Token to be redeemed (and YT if specified). + * @param recipient The address to which the underlying token is transferred. + * @param data Encoded address of the market, minimum amount of DOLA to receive for the recipient and Pendle callData. + * @return dolaAmount The amount of DOLA redeemed. + */ + function convertFromCollateral( + uint256 amount, + address recipient, + bytes calldata data + ) public override returns (uint256 dolaAmount) { + return _convertFromCollateral(msg.sender, amount, recipient, data); + } + + function _convertFromCollateral( + address user, + uint256 amount, + address recipient, + bytes calldata data + ) internal returns (uint256 dolaAmount) { + ( + address market, + uint256 minOut, // Minimum amount of DOLA to receive + bytes memory callData + ) = abi.decode(data, (address, uint256, bytes)); + _revertIfMarketNotSet(market); + + bytes4 selector = bytes4(callData.slice(0, 4)); + if (selector != SWAP_DOLA && selector != REDEEM_PT) + revert InvalidSelector(); + + if (selector == REDEEM_PT) _handleYT(market, user, amount); + + IERC20 pt = IERC20(markets[market].pt); + pt.safeTransferFrom(msg.sender, address(this), amount); + pt.approve(router, amount); + + uint256 dolaBal = DOLA.balanceOf(recipient); + + _callRouter(callData); + // Ensure recipient received at least minOut DOLA + dolaAmount = DOLA.balanceOf(recipient) - dolaBal; + if (dolaAmount < minOut) revert InsufficientDOLA(); + emit DolaOut(amount, dolaAmount, msg.sender, recipient); + } + + /** + * @notice Withdraw the collateral from the market then convert to DOLA. + * @dev The receiver in Pendle API has to be set same as the recipient. + * If a REDEEM is performed Before maturity, ensure msg.sender has enough balance and allowance for YT + * @param amount The amount of PT token to be withdrawn from the market. + * @param recipient The address to which DOLA is transferred. + * @param permit The permit data for the Market. + * @param data Encoded address of the market, minimum amount of DOLA to receive for the recipient and Pendle callData. + * @return dolaAmount The amount of DOLA redeemed. + */ + function withdrawAndConvertFromCollateral( + uint256 amount, + address recipient, + Permit calldata permit, + bytes calldata data + ) external override returns (uint256 dolaAmount) { + (address market, uint256 minOut, bytes memory callData) = abi.decode( + data, + (address, uint256, bytes) + ); + _revertIfMarketNotSet(market); + + bytes4 selector = bytes4(callData.slice(0, 4)); + if (selector != SWAP_DOLA && selector != REDEEM_PT) + revert InvalidSelector(); + + if (selector == REDEEM_PT) _handleYT(market, msg.sender, amount); + + IMarket(market).withdrawOnBehalf( + msg.sender, + amount, + permit.deadline, + permit.v, + permit.r, + permit.s + ); + + IERC20 pt = IERC20(markets[market].pt); + pt.approve(router, amount); + + uint256 dolaBal = DOLA.balanceOf(recipient); + _callRouter(callData); + + dolaAmount = DOLA.balanceOf(recipient) - dolaBal; + if (dolaAmount < minOut) revert InsufficientDOLA(); + emit DolaOut(amount, dolaAmount, msg.sender, recipient); + } + + /** + * @notice Call router. + * @param pendleData to be called on the router. + */ + function _callRouter(bytes memory pendleData) internal { + (bool success, ) = router.call(pendleData); + if (!success) revert PendleSwapFailed(); + } + + /** + * @notice Handle YT, pulling it from the user and approving the router if before maturity + * @param market The market address. + * @param user The user address. + * @param amount The amount of YT to handle. + */ + function _handleYT(address market, address user, uint256 amount) internal { + // Check if PT is not expired, in which case YT is not needed + if (block.timestamp < markets[market].maturity) { + IERC20 yt = IERC20(markets[market].yt); + if (yt.balanceOf(user) < amount) revert InsufficientYT(); + yt.safeTransferFrom(user, address(this), amount); + yt.approve(router, amount); + emit YTIn(amount, user); + } + } + + function _revertIfMarketNotSet(address market) internal view { + if (address(markets[market].pt) == address(0)) + revert MarketNotSet(market); + } + + /// ADMIN FUNCTIONS + + /** + * @notice Set the market address and its associated Pendle PT and YT addresses. + * @dev Only callable by the governance. + * @param marketAddress The address of the market. + * @param ptAddress Pendle PT address + * @param ytAddress Pendle YT address + */ + function setMarket( + address marketAddress, + address ptAddress, + address ytAddress + ) external onlyGov { + uint96 maturity = uint96(IPendlePT(ptAddress).expiry()); + markets[marketAddress] = PT({ + pt: ptAddress, + maturity: maturity, + yt: ytAddress + }); + emit MarketSet(marketAddress, ptAddress, ytAddress, maturity); + } + + /** + * @notice Remove the market. + * @dev Only callable by the governance or the guardian. + * @param market The address of the market to be removed. + */ + function removeMarket(address market) external onlyGuardianOrGov { + delete markets[market]; + emit MarketRemoved(market); + } +} diff --git a/src/util/YVYCRVHelper.sol b/src/util/YVYCRVHelper.sol index cfc0068e..de01b379 100644 --- a/src/util/YVYCRVHelper.sol +++ b/src/util/YVYCRVHelper.sol @@ -40,7 +40,8 @@ contract YVYCRVHelper is Sweepable, ReentrancyGuard { /// @param _value Amount of underlying to transform /// @param _helperData Optional helper data in case the collateral needs to be transformed /// @return collateralAmount Amount of collateral received - function transformToCollateral( + function convertToCollateral( + address, uint256 _value, bytes calldata _helperData ) external nonReentrant returns (uint256 collateralAmount) { @@ -65,7 +66,8 @@ contract YVYCRVHelper is Sweepable, ReentrancyGuard { /// @param _value Amount of collateral to transform /// @param _helperData Optional helper data in case the collateral needs to be transformed /// @return underlyingAmount Amount of underlying received - function transformFromCollateral( + function convertFromCollateral( + address, uint256 _value, bytes calldata _helperData ) external nonReentrant returns (uint256 underlyingAmount) { @@ -87,7 +89,7 @@ contract YVYCRVHelper is Sweepable, ReentrancyGuard { /// @notice Transforms underlying to collateral and deposits it into the market /// @param amount Amount of underlying to transform /// @param data Optional helper data in case the collateral needs to be transformed - function transformToCollateralAndDeposit( + function convertToCollateralAndDeposit( uint256 amount, bytes calldata data ) external returns (uint256 collateralAmount) { @@ -101,7 +103,7 @@ contract YVYCRVHelper is Sweepable, ReentrancyGuard { /// @notice Withdraws collateral from the market and transforms it to underlying /// @param amount Amount of collateral to transform /// @param data Optional helper data in case the collateral needs to be transformed - function withdrawAndTransformFromCollateral( + function withdrawAndConvertFromCollateral( uint256 amount, Permit calldata permit, bytes calldata data diff --git a/test/ConfigAddr.sol b/test/ConfigAddr.sol index 3df32490..281acc36 100644 --- a/test/ConfigAddr.sol +++ b/test/ConfigAddr.sol @@ -20,7 +20,7 @@ contract ConfigAddr { // ALE address aleAddr = address(0x5233f4C2515ae21B540c438862Abb5603506dEBC); - + address aleV2Addr = address(0x4dF2EaA1658a220FDB415B9966a9ae7c3d16e240); // deployed at block 22241605 // Inverse Feeds address styEthFeedAddr = address(0xbBE5FaBbB55c2c79ae1efE6b5bd52048A199e166); @@ -116,10 +116,20 @@ contract ConfigAddr { address yearnDolaFraxBPFeedAddr = address(0x85f86F9e2dCc370c90d3a7bFC2B8E9a970D84850); // CurveLPYearnV2Feed for DolaFraxBP (use DolaFraxBP LP feed) + // ALE and Market Helpers // CurveDolaLPHelper Static Array address curveDolaLPHelperAddr = - address(0x6c592Fe4deA245B296476fd72863E8b2B739f911); // Helper for entering and exiting curve pools with Dola. Also support YearnV2 vaults for these LP. + address(0x80842059330945523B0Fa0592FD53568D813F8E0); // Helper for entering and exiting curve pools with Dola. Also support YearnV2 vaults for these LP. // CurveDolaLPHelper Dynamic Array address curveDolaLPHelperDynamicAddr = - address(0x0ABe42a1ad400500ac49bDAd38AE6b367982FC9B); // Helper for entering and exiting curve pools (dynamic array input) with Dola. Also support YearnV2 vaults for these LP. + address(0x20717e5EE263F2418bADAd9704B88D98cAFFaC8E); // Helper for entering and exiting curve pools (dynamic array input) with Dola. Also support YearnV2 vaults for these LP. + + address curveSDolaLPHelperDynamicAddr = + address(0x50671Bf561ED15A84066DB2415D1EF55Daf35037); // Helper for entering and exiting curve pools (dynamic array input) with sDola. Also support YearnV2 vaults for these LP. + address erc4626HelperAddr = + address(0xF2F9b6F0B28f77872Acd2da4187021B3b8C73ab8); // Helper for entering and exiting ERC4626 vaults with underlying. Also support YearnV2 vaults for these LP. + address yvyCRVHelperAddr = + address(0xa93DaC9b38aB4a23F1fA934EAbC7f9639a003C4C); // Helper for entering and exiting yvyCRV vaults with underlying. Also support YearnV2 vaults for these LP. + address pendlePTHelperAddr = + address(0x4809fE7d314c2AE5b2Eb7fa19C1B166434D29141); // Helper for entering and exiting Pendle PT vaults with DOLA. Also support Minting and Redeeming PTs. } diff --git a/test/escrowForkTests/BaseEscrowLPConvexTest.t.sol b/test/escrowForkTests/BaseEscrowLPConvexTest.t.sol index 902ed45f..3591a290 100644 --- a/test/escrowForkTests/BaseEscrowLPConvexTest.t.sol +++ b/test/escrowForkTests/BaseEscrowLPConvexTest.t.sol @@ -46,7 +46,6 @@ abstract contract BaseEscrowLPConvexTest is Test { struct ConvexInfo { uint256 pid; address rewardPool; - address depositToken; address stash; } @@ -62,7 +61,6 @@ abstract contract BaseEscrowLPConvexTest is Test { gauge = _gauge; pid = _convexInfo.pid; rewardPool = IRewardPool(_convexInfo.rewardPool); - depositToken = IERC20(_convexInfo.depositToken); stash = _convexInfo.stash; if (_addExtraDolaReward) { diff --git a/test/escrowForkTests/ConvexEscrowFork.t.sol b/test/escrowForkTests/ConvexEscrowFork.t.sol index 79267378..866c967e 100644 --- a/test/escrowForkTests/ConvexEscrowFork.t.sol +++ b/test/escrowForkTests/ConvexEscrowFork.t.sol @@ -5,9 +5,8 @@ import "forge-std/Test.sol"; import "src/escrows/ConvexEscrow.sol"; contract MockRewards { - IERC20 token; - constructor(address _token){ + constructor(address _token) { token = IERC20(_token); } @@ -16,25 +15,23 @@ contract MockRewards { } } -contract ConvexEscrowForkTest is Test{ - +contract ConvexEscrowForkTest is Test { address market = address(0xA); address beneficiary = address(0xB); address friend = address(0xC); - address holder = address(0x50BE13b54f3EeBBe415d20250598D81280e56772); + address holder = address(0x272b065A43EF59EA470fbfD9be76AD1b43aAB651); IERC20 dola = IERC20(0x865377367054516e17014CcdED1e7d814EDC9ce4); IERC20 cvx = IERC20(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B); IERC20 cvxCrv = IERC20(0x62B9c7356A2Dc64a1969e19C23e4f579F9810Aa7); - ICvxRewardPool rewardPool = ICvxRewardPool(0xCF50b810E57Ac33B91dCF525C6ddd9881B139332); - - ConvexEscrow escrow; + ICvxRewardPool rewardPool = + ICvxRewardPool(0xCF50b810E57Ac33B91dCF525C6ddd9881B139332); + ConvexEscrow escrow; function setUp() public { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); vm.createSelectFork(url, 22114296); - escrow = new ConvexEscrow(); vm.startPrank(market, market); escrow.initialize(address(cvx), beneficiary); @@ -46,13 +43,16 @@ contract ConvexEscrowForkTest is Test{ function testOnDeposit_successful_whenContractHoldsCvxCrv() public { uint balanceBefore = escrow.balance(); uint stakedBalanceBefore = rewardPool.balanceOf(address(escrow)); - + vm.prank(holder, holder); cvx.transfer(address(escrow), 1 ether); escrow.onDeposit(); assertEq(escrow.balance(), balanceBefore + 1 ether); - assertEq(rewardPool.balanceOf(address(escrow)), stakedBalanceBefore + 1 ether); + assertEq( + rewardPool.balanceOf(address(escrow)), + stakedBalanceBefore + 1 ether + ); } function testPay_successful_whenContractHasStakedCvxCrv() public { @@ -66,10 +66,15 @@ contract ConvexEscrowForkTest is Test{ vm.prank(market, market); escrow.pay(beneficiary, 1 ether); - assertEq(escrow.balance(), balanceBefore - 1 ether); - assertEq(rewardPool.balanceOf(address(escrow)), stakedBalanceBefore - 1 ether); - assertEq(cvx.balanceOf(beneficiary), beneficiaryBalanceBefore + 1 ether); + assertEq( + rewardPool.balanceOf(address(escrow)), + stakedBalanceBefore - 1 ether + ); + assertEq( + cvx.balanceOf(beneficiary), + beneficiaryBalanceBefore + 1 ether + ); } function testPay_failWithONLYMARKET_whenCalledByNonMarket() public { @@ -100,13 +105,17 @@ contract ConvexEscrowForkTest is Test{ vm.prank(holder, holder); cvx.transfer(address(escrow), 1 ether); escrow.onDeposit(); - + vm.startPrank(beneficiary); vm.warp(block.timestamp + 14 days); escrow.claim(); vm.stopPrank(); - assertGt(cvxCrv.balanceOf(beneficiary), cvxCrvBalanceBefore, "cvxCrv balance did not increase"); + assertGt( + cvxCrv.balanceOf(beneficiary), + cvxCrvBalanceBefore, + "cvxCrv balance did not increase" + ); } function testClaimTo_successful_whenExtraRewardsAdded() public { @@ -120,7 +129,7 @@ contract ConvexEscrowForkTest is Test{ deal(address(dola), address(reward), 10 ether); vm.prank(rewardPool.rewardManager()); rewardPool.addExtraReward(address(reward)); - + vm.startPrank(beneficiary); vm.warp(block.timestamp + 14 days); address[] memory rewards = new address[](1); @@ -128,8 +137,16 @@ contract ConvexEscrowForkTest is Test{ escrow.claimTo(beneficiary, rewards); vm.stopPrank(); - assertGt(cvxCrv.balanceOf(beneficiary), cvxCrvBalanceBefore, "cvxCrv balance did not increase"); - assertGt(dola.balanceOf(beneficiary), dolaBalanceBefore, "Dola extra reward balance did not increase"); + assertGt( + cvxCrv.balanceOf(beneficiary), + cvxCrvBalanceBefore, + "cvxCrv balance did not increase" + ); + assertGt( + dola.balanceOf(beneficiary), + dolaBalanceBefore, + "Dola extra reward balance did not increase" + ); } function testClaimTo_fails_whenTryingToClaimCollateral() public { @@ -141,7 +158,7 @@ contract ConvexEscrowForkTest is Test{ deal(address(cvx), address(reward), 10 ether); vm.prank(rewardPool.rewardManager()); rewardPool.addExtraReward(address(reward)); - + vm.startPrank(beneficiary); vm.warp(block.timestamp + 14 days); address[] memory rewards = new address[](1); @@ -160,7 +177,7 @@ contract ConvexEscrowForkTest is Test{ deal(address(cvx), address(reward), 10 ether); vm.prank(rewardPool.rewardManager()); rewardPool.addExtraReward(address(reward)); - + vm.startPrank(beneficiary); vm.warp(block.timestamp + 14 days); address[] memory rewards = new address[](2); @@ -237,10 +254,11 @@ contract ConvexEscrowForkTest is Test{ escrow.allowClaimOnBehalf(friend); } - function testDisallowClaimOnBehalf_fails_whenCalledByNonBeneficiary() public { + function testDisallowClaimOnBehalf_fails_whenCalledByNonBeneficiary() + public + { vm.prank(friend); vm.expectRevert("ONLY BENEFICIARY"); escrow.disallowClaimOnBehalf(friend); } - } diff --git a/test/escrowForkTests/CrvUSDDolaEscrowConvexFork.t.sol b/test/escrowForkTests/CrvUSDDolaEscrowConvexFork.t.sol index bd9ff15b..46d2d66b 100644 --- a/test/escrowForkTests/CrvUSDDolaEscrowConvexFork.t.sol +++ b/test/escrowForkTests/CrvUSDDolaEscrowConvexFork.t.sol @@ -12,7 +12,6 @@ contract DolaCrvUSDEscrowConvexForkTest is BaseEscrowLPConvexTest { // Convex uint256 _pid = 215; address _rewardPool = 0xC94208D230EEdC4cDC4F80141E21aA485A515660; - address _depositToken = 0x408abF1a02388A5EF19E3dB1e08db5eFdC510DFF; address _stash = 0x25F5Ccd892985Bf878327B15815bd90066EEf28d; function setUp() public { @@ -22,7 +21,6 @@ contract DolaCrvUSDEscrowConvexForkTest is BaseEscrowLPConvexTest { BaseEscrowLPConvexTest.ConvexInfo memory convexParams = ConvexInfo( _pid, _rewardPool, - _depositToken, _stash ); diff --git a/test/escrowForkTests/DolaDeUSDEscrowConvexFork.t.sol b/test/escrowForkTests/DolaDeUSDEscrowConvexFork.t.sol new file mode 100644 index 00000000..f08b2bf7 --- /dev/null +++ b/test/escrowForkTests/DolaDeUSDEscrowConvexFork.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {BaseEscrowLPConvexTest} from "test/escrowForkTests/BaseEscrowLPConvexTest.t.sol"; + +contract DolaDeUSDEscrowConvexForkTest is BaseEscrowLPConvexTest { + // Curve + address _dolaDeUSD = 0x6691DBb44154A9f23f8357C56FC9ff5548A8bdc4; + address _lpHolder = address(0xcb4a7b790eDB7Fa3e2731Efd7ED85275f92Fc74A); + address _gauge = 0xa48A3c91b062ca06Fd0d0569695432EB066f8c7E; + + // Convex + uint256 _pid = 419; + address _rewardPool = 0xD30E66cBc869Aa808eB9c81f8Aad8408767E3a3E; + address _stash = 0x074297Bf0dEA6925c27526E6E3E3151D8cE4edc7; + + function setUp() public { + //This will fail if there's no mainnet variable in foundry.toml + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url, 21826229); + BaseEscrowLPConvexTest.ConvexInfo memory convexParams = ConvexInfo( + _pid, + _rewardPool, + _stash + ); + + init(_dolaDeUSD, _lpHolder, _gauge, convexParams, true); + } +} diff --git a/test/escrowForkTests/DolaFraxBPEscrowConvexFork.t.sol b/test/escrowForkTests/DolaFraxBPEscrowConvexFork.t.sol index 82b5e3f8..612bf062 100644 --- a/test/escrowForkTests/DolaFraxBPEscrowConvexFork.t.sol +++ b/test/escrowForkTests/DolaFraxBPEscrowConvexFork.t.sol @@ -12,7 +12,6 @@ contract DolaFraxBPEscrowConvexForkTest is BaseEscrowLPConvexTest { // Convex uint256 _pid = 115; address _rewardPool = 0x0404d05F3992347d2f0dC3a97bdd147D77C85c1c; - address _depositToken = 0xf7eCC27CC9DB5d28110AF2d89b176A6623c7E351; address _stash = 0xe5A980F96c791c8Ea56c2585840Cab571441510e; function setUp() public { @@ -22,7 +21,6 @@ contract DolaFraxBPEscrowConvexForkTest is BaseEscrowLPConvexTest { BaseEscrowLPConvexTest.ConvexInfo memory convexParams = ConvexInfo( _pid, _rewardPool, - _depositToken, _stash ); diff --git a/test/escrowForkTests/DolaFraxPyUSDEscrowConvexFork.t.sol b/test/escrowForkTests/DolaFraxPyUSDEscrowConvexFork.t.sol index 4531e4d5..ed3c0e59 100644 --- a/test/escrowForkTests/DolaFraxPyUSDEscrowConvexFork.t.sol +++ b/test/escrowForkTests/DolaFraxPyUSDEscrowConvexFork.t.sol @@ -12,7 +12,6 @@ contract DolaFraxPyUSDEscrowForkTest is BaseEscrowLPConvexTest { // Convex uint256 _pid = 317; address _rewardPool = 0xE8cBdBFD4A1D776AB1146B63ABD1718b2F92a823; - address _depositToken = 0x430bE19e180fd8c2199eC5FAEabE2F5CDba68C94; address _stash = 0x6bCc4b00F2Cc9CdFF935E1A5D939f26A233Dd381; function setUp() public { @@ -22,7 +21,6 @@ contract DolaFraxPyUSDEscrowForkTest is BaseEscrowLPConvexTest { BaseEscrowLPConvexTest.ConvexInfo memory convexParams = ConvexInfo( _pid, _rewardPool, - _depositToken, _stash ); diff --git a/test/escrowForkTests/DolaUSREscrowConvexFork.t.sol b/test/escrowForkTests/DolaUSREscrowConvexFork.t.sol new file mode 100644 index 00000000..3d222470 --- /dev/null +++ b/test/escrowForkTests/DolaUSREscrowConvexFork.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {BaseEscrowLPConvexTest} from "test/escrowForkTests/BaseEscrowLPConvexTest.t.sol"; + +contract DolaUSREscrowConvexForkTest is BaseEscrowLPConvexTest { + // Curve + address _dolaUSR = 0x38De22a3175708D45E7c7c64CD78479C8B56f76E; + address _lpHolder = address(0x89836bB3a0471adBa7DEf2677292c07004308Feb); + address _gauge = 0xd303994a0Db9b74f3E8fF629ba3097fC7060C331; + + // Convex + uint256 _pid = 421; + address _rewardPool = 0xE694a5e9272ea7ed2DC25f0c6D21640fb8a83166; + address _stash = 0x63A8AE4C4fE19B8816dEa970544F8a446051cB89; + + function setUp() public { + //This will fail if there's no mainnet variable in foundry.toml + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url, 21969468); + BaseEscrowLPConvexTest.ConvexInfo memory convexParams = ConvexInfo( + _pid, + _rewardPool, + _stash + ); + + init(_dolaUSR, _lpHolder, _gauge, convexParams, true); + } +} diff --git a/test/feedForkTests/DolaDeUSDFeedFork.t.sol b/test/feedForkTests/DolaDeUSDFeedFork.t.sol new file mode 100644 index 00000000..3e8318e5 --- /dev/null +++ b/test/feedForkTests/DolaDeUSDFeedFork.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import "src/feeds/ChainlinkBasePriceFeed.sol"; +import {ChainlinkCurveFeed} from "src/feeds/ChainlinkCurveFeed.sol"; +import {ChainlinkCurve2CoinsFeed} from "src/feeds/ChainlinkCurve2CoinsFeed.sol"; +import "src/feeds/CurveLPPessimisticFeed.sol"; +import {DolaCurveLPPessimsticFeedBaseTest} from "test/feedForkTests/DolaCurveLPPessimsticFeedBaseTest.t.sol"; +import {ConfigAddr} from "test/ConfigAddr.sol"; + +contract DolaDeUSDFeedFork is DolaCurveLPPessimsticFeedBaseTest, ConfigAddr { + address clDeUSDFeed = address(0x471a6299C027Bd81ed4D66069dc510Bd0569f4F8); + uint256 deUSDHeartbeat = 86400; + + ICurvePool public constant dolaDeUSD = + ICurvePool(0x6691DBb44154A9f23f8357C56FC9ff5548A8bdc4); + + function setUp() public { + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url, 21826229); + + ChainlinkBasePriceFeed deUSDFeed = new ChainlinkBasePriceFeed( + gov, + clDeUSDFeed, + address(0), + deUSDHeartbeat + ); + init(address(0), address(deUSDFeed), address(dolaDeUSD)); + } +} diff --git a/test/feedForkTests/DolaUSRFeedFork.t.sol b/test/feedForkTests/DolaUSRFeedFork.t.sol new file mode 100644 index 00000000..1cf8303e --- /dev/null +++ b/test/feedForkTests/DolaUSRFeedFork.t.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import "src/feeds/ChainlinkBasePriceFeed.sol"; +import {ChainlinkCurveFeed} from "src/feeds/ChainlinkCurveFeed.sol"; +import {ChainlinkCurve2CoinsFeed} from "src/feeds/ChainlinkCurve2CoinsFeed.sol"; +import "src/feeds/CurveLPPessimisticFeed.sol"; +import {DolaCurveLPPessimsticFeedBaseTest} from "test/feedForkTests/DolaCurveLPPessimsticFeedBaseTest.t.sol"; +import {ConfigAddr} from "test/ConfigAddr.sol"; + +contract DolaUSRFeedFork is DolaCurveLPPessimsticFeedBaseTest, ConfigAddr { + address clUSRFeed = address(0x34ad75691e25A8E9b681AAA85dbeB7ef6561B42c); + uint256 usrHeartbeat = 86400; + + ICurvePool public constant dolaUSR = + ICurvePool(0x38De22a3175708D45E7c7c64CD78479C8B56f76E); + + function setUp() public { + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url, 21969468); + + ChainlinkBasePriceFeed usrFeed = new ChainlinkBasePriceFeed( + gov, + clUSRFeed, + address(0), + usrHeartbeat + ); + init(address(0), address(usrFeed), address(dolaUSR)); + } +} diff --git a/test/feedForkTests/USDeNavBeforeMaturityFeed.t.sol b/test/feedForkTests/USDeNavBeforeMaturityFeed.t.sol new file mode 100644 index 00000000..575e5d7e --- /dev/null +++ b/test/feedForkTests/USDeNavBeforeMaturityFeed.t.sol @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import "forge-std/Test.sol"; +import {USDeNavBeforeMaturityFeed} from "src/feeds/USDeNavBeforeMaturityFeed.sol"; +import {ChainlinkBasePriceFeed, IChainlinkFeed} from "src/feeds/ChainlinkBasePriceFeed.sol"; +import "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; +import "forge-std/console.sol"; +import {PendleNAVFeed} from "src/feeds/PendleNAVFeed.sol"; + +interface INavFeed { + function getDiscount(uint256 timeLeft) external view returns (uint256) ; + function maturity() external view returns (uint256); + function decimals() external view returns (uint8); +} + +contract USDeNavBeforeMaturityFeedTest is Test { + USDeNavBeforeMaturityFeed feed; + ChainlinkBasePriceFeed sUSDeWrappedFeed; + address sUSDeFeed = address(0xFF3BC18cCBd5999CE63E788A1c250a88626aD099); + IERC4626 sUSDe = IERC4626(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497); + address gov = address(0x926dF14a23BE491164dCF93f4c468A50ef659D5B); + address pendlePT = address(0xb7de5dFCb74d25c2f21841fbd6230355C50d9308); // PT sUSDe 29 May 25 + + function setUp() public { + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url, 22147231); + sUSDeWrappedFeed = new ChainlinkBasePriceFeed( + gov, + sUSDeFeed, + address(0), + 24 hours + ); + address navFeed = address(new PendleNAVFeed(pendlePT, 0.2 ether)); // 20% discount + feed = new USDeNavBeforeMaturityFeed( + address(sUSDeWrappedFeed), + address(sUSDe), + navFeed + ); + } + + function test_decimals() public { + assertEq(feed.sUSDeFeed().decimals(), 18); + assertEq(feed.sUSDe().decimals(), 18); + assertEq(feed.decimals(), 18); + } + + function test_description() public { + string memory expected = string( + abi.encodePacked( + "USDe/USD Feed using sUSDe Chainlink feed and sUSDe/USDe rate with NAV" + ) + ); + assertEq(feed.description(), expected); + } + + function test_latestRoundData() public { + ( + uint80 roundId, + int256 USDeUsdPrice, + uint startedAt, + uint updatedAt, + uint80 answeredInRound + ) = feed.latestRoundData(); + ( + uint80 roundIdCl, + int256 sUSDeUsdPrice, + uint startedAtCl, + uint updatedAtCl, + uint80 answeredInRoundCl + ) = sUSDeWrappedFeed.latestRoundData(); + assertEq(roundId, roundIdCl); + assertEq(startedAt, startedAtCl); + assertEq(updatedAt, updatedAtCl); + assertEq(answeredInRound, answeredInRoundCl); + + int256 USDeUsdPriceEst = (sUSDeUsdPrice * 1e18) / + int256(sUSDe.convertToAssets(1e18)); + (,int256 navDiscountedPrice,,,) = feed.navFeed().latestRoundData(); + int256 discountPrice = (USDeUsdPriceEst * navDiscountedPrice) / 1e18; + assertEq(discountPrice, USDeUsdPrice); + } + + function test_latestAnswer() public { + int256 USDeUsdPrice = feed.latestAnswer(); + int256 USDeUsdPriceEst = (sUSDeWrappedFeed.latestAnswer() * 1e18) / + int256(sUSDe.convertToAssets(1e18)); + (,int256 navDiscountedPrice,,,) = feed.navFeed().latestRoundData(); + int256 discountPrice = (USDeUsdPriceEst * navDiscountedPrice) / 1e18; + assertEq(discountPrice, USDeUsdPrice); + } + + function test_NAV() public { + uint256 maturity = INavFeed(address(feed.navFeed())).maturity(); + vm.warp(maturity - 365 days/6); //2 months before expiry + uint256 discount = INavFeed(address(feed.navFeed())).getDiscount(365 days/6); + assertApproxEqAbs(discount, 0.0333 ether, 0.0001 ether); + (,int256 navDiscountedPrice,,,) = feed.navFeed().latestRoundData(); + assertApproxEqAbs(navDiscountedPrice, 0.966666666 ether, 0.00000001 ether); + int256 USDeUsdPrice = feed.latestAnswer(); + int256 USDeUsdPriceEst = (sUSDeWrappedFeed.latestAnswer() * 1e18) / + int256(sUSDe.convertToAssets(1e18)); + int256 discountPrice = (USDeUsdPriceEst * navDiscountedPrice) / 1e18; + assertEq(discountPrice, USDeUsdPrice); + + vm.warp(maturity - 365 days/12); //1 months before expiry + uint256 discount2 = INavFeed(address(feed.navFeed())).getDiscount(365 days/12); + assertApproxEqAbs(discount2, 0.016666666 ether, 0.0001 ether); + (,int256 navDiscountedPrice2,,,) = feed.navFeed().latestRoundData(); + assertApproxEqAbs(navDiscountedPrice2, 0.983333333 ether, 0.00000001 ether); + int256 USDeUsdPrice2 = feed.latestAnswer(); + int256 USDeUsdPriceEst2 = (sUSDeWrappedFeed.latestAnswer() * 1e18) / + int256(sUSDe.convertToAssets(1e18)); + int256 discountPrice2 = (USDeUsdPriceEst2 * navDiscountedPrice2) / 1e18; + assertEq(discountPrice2, USDeUsdPrice2); + // Check if the discount is decreasing + assertGt(discount, discount2); + // Check if the price is increasing + assertLt(navDiscountedPrice, navDiscountedPrice2); + assertLt(USDeUsdPrice, USDeUsdPrice2); + assertLt(discountPrice, discountPrice2); + } + function test_STALE_sUSDeFeed() public { + vm.mockCall( + address(sUSDeFeed), + abi.encodeWithSelector(IChainlinkFeed.latestRoundData.selector), + abi.encode(0, 1.1e8, 0, 0, 0) + ); + ( + uint80 roundId, + int256 USDeUsdPrice, + uint startedAt, + uint updatedAt, + uint80 answeredInRound + ) = feed.latestRoundData(); + int256 USDeUsdPriceEst = (sUSDeWrappedFeed.latestAnswer() * 1e18) / + int256(sUSDe.convertToAssets(1e18)); + (,int256 navDiscountedPrice,,,) = feed.navFeed().latestRoundData(); + int256 discountPrice = (USDeUsdPriceEst * navDiscountedPrice) / 1e18; + assertEq(roundId, 0); + assertEq(USDeUsdPrice, discountPrice); + assertEq(startedAt, 0); + assertEq(updatedAt, 0); + assertEq(answeredInRound, 0); + } + + function test_maturity_passed() public { + uint256 maturity = INavFeed(address(feed.navFeed())).maturity(); + vm.warp(maturity); + address navFeed = address(new PendleNAVFeed(pendlePT, 0.2 ether)); + vm.expectRevert(USDeNavBeforeMaturityFeed.MaturityPassed.selector); + feed = new USDeNavBeforeMaturityFeed( + address(sUSDeWrappedFeed), + address(sUSDe), + navFeed + ); + } +} diff --git a/test/marketForkTests/CrvUSDDolaConvexMarketForkTest.t.sol b/test/marketForkTests/CrvUSDDolaConvexMarketForkTest.t.sol index abdf241b..8f551eea 100644 --- a/test/marketForkTests/CrvUSDDolaConvexMarketForkTest.t.sol +++ b/test/marketForkTests/CrvUSDDolaConvexMarketForkTest.t.sol @@ -68,7 +68,7 @@ contract CrvUSDDolaConvexMarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 20955256); + vm.createSelectFork(url); _advancedInit(crvUSDDolaConvexAddr, address(crvUSDDolaFeedAddr), true); diff --git a/test/marketForkTests/CrvUSDDolaYearnV2MarketForkTest.t.sol b/test/marketForkTests/CrvUSDDolaYearnV2MarketForkTest.t.sol index 4802a0be..b458f2fb 100644 --- a/test/marketForkTests/CrvUSDDolaYearnV2MarketForkTest.t.sol +++ b/test/marketForkTests/CrvUSDDolaYearnV2MarketForkTest.t.sol @@ -59,7 +59,7 @@ contract CrvUSDDolaYearnV2MarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 20955256); + vm.createSelectFork(url); escrow = new SimpleERC20Escrow(); diff --git a/test/marketForkTests/DolaDeUSDConvexMarketForkTest.t.sol b/test/marketForkTests/DolaDeUSDConvexMarketForkTest.t.sol new file mode 100644 index 00000000..d64d50f5 --- /dev/null +++ b/test/marketForkTests/DolaDeUSDConvexMarketForkTest.t.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {MarketBaseForkTest, IOracle, IDolaBorrowingRights, IERC20} from "./MarketBaseForkTest.sol"; +import {Market} from "src/Market.sol"; + +import {ConvexEscrowV2} from "src/escrows/ConvexEscrowV2.sol"; +import {CurveLPPessimisticFeed} from "src/feeds/CurveLPPessimisticFeed.sol"; +import {ChainlinkCurve2CoinsFeed, ICurvePool} from "src/feeds/ChainlinkCurve2CoinsFeed.sol"; +import {ChainlinkCurveFeed} from "src/feeds/ChainlinkCurveFeed.sol"; +import "src/feeds/ChainlinkBasePriceFeed.sol"; +import {console} from "forge-std/console.sol"; +import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; +import {DolaFixedPriceFeed} from "src/feeds/DolaFixedPriceFeed.sol"; +import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol"; + +contract DolaDeUSDConvexMarketForkTest is MarketBaseForkTest { + ConvexEscrowV2 escrow; + + CurveLPPessimisticFeed feedDolaDeUSD; + DolaFixedPriceFeed dolaFeed; + + ICurvePool public constant dolaDeUSD = + ICurvePool(0x6691DBb44154A9f23f8357C56FC9ff5548A8bdc4); + + address deUSDFeed = address(0x471a6299C027Bd81ed4D66069dc510Bd0569f4F8); + ChainlinkBasePriceFeed deUSDWrapper; + + address rewardPool = address(0xD30E66cBc869Aa808eB9c81f8Aad8408767E3a3E); + + address booster = address(0xF403C135812408BFbE8713b5A23a04b3D48AAE31); + + uint256 pid = 419; + + IERC20 public cvx = IERC20(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B); + IERC20 public crv = IERC20(0xD533a949740bb3306d119CC777fa900bA034cd52); + + ConvexEscrowV2 userEscrow; + + function setUp() public virtual { + //This will fail if there's no mainnet variable in foundry.toml + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url); + escrow = new ConvexEscrowV2( + rewardPool, + booster, + address(cvx), + address(crv), + pid + ); + feedDolaDeUSD = _deployDolaDeUSDFeed(); + market = new Market( + gov, + fedAddr, + pauseGuardian, + address(escrow), + IDolaBorrowingRights(address(dbrAddr)), + IERC20(address(dolaDeUSD)), + IOracle(address(oracleAddr)), + 5000, + 5000, + 1000, + true + ); + _advancedInit(address(market), address(feedDolaDeUSD), true); + + userEscrow = ConvexEscrowV2( + address(Market(address(market)).predictEscrow(user)) + ); + } + + function test_escrow_immutables() public { + testDeposit(); + assertEq( + address(userEscrow.rewardPool()), + address(rewardPool), + "Reward pool not set" + ); + assertEq( + address(userEscrow.booster()), + address(booster), + "Booster not set" + ); + + assertEq(address(userEscrow.cvx()), address(cvx), "CVX not set"); + assertEq(address(userEscrow.crv()), address(crv), "CRV not set"); + } + + function _deployDolaDeUSDFeed() + internal + returns (CurveLPPessimisticFeed feed) + { + deUSDWrapper = new ChainlinkBasePriceFeed( + gov, + address(deUSDFeed), + address(0), + 86400 + ); + feed = new CurveLPPessimisticFeed( + address(dolaDeUSD), + address(deUSDWrapper), + address(dolaFixedFeedAddr), + false + ); + } +} diff --git a/test/marketForkTests/DolaDeUSDYearnV2MarketForkTest.t.sol b/test/marketForkTests/DolaDeUSDYearnV2MarketForkTest.t.sol new file mode 100644 index 00000000..df63652b --- /dev/null +++ b/test/marketForkTests/DolaDeUSDYearnV2MarketForkTest.t.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {MarketBaseForkTest, IOracle, IDolaBorrowingRights, IERC20} from "./MarketBaseForkTest.sol"; +import {Market} from "src/Market.sol"; +import {SimpleERC20Escrow} from "src/escrows/SimpleERC20Escrow.sol"; +import {CurveLPYearnV2Feed} from "src/feeds/CurveLPYearnV2Feed.sol"; +import {ChainlinkCurve2CoinsFeed} from "src/feeds/ChainlinkCurve2CoinsFeed.sol"; +import {ChainlinkCurveFeed, ICurvePool} from "src/feeds/ChainlinkCurveFeed.sol"; +import "src/feeds/ChainlinkBasePriceFeed.sol"; +import "src/feeds/CurveLPYearnV2Feed.sol"; +import {console} from "forge-std/console.sol"; +import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; +import {CurveLPPessimisticFeed} from "src/feeds/CurveLPPessimisticFeed.sol"; +import {MockFeedDescription} from "test/mocks/MockFeedDescription.sol"; +import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol"; + +contract DolaDeUSDYearnV2MarketForkTest is MarketBaseForkTest { + CurveLPYearnV2Feed yearnFeed; + CurveLPPessimisticFeed lpFeed; + + address deUSDFeed = address(0x471a6299C027Bd81ed4D66069dc510Bd0569f4F8); + ChainlinkBasePriceFeed deUSDWrapper; + ICurvePool public constant dolaDeUSD = + ICurvePool(0x6691DBb44154A9f23f8357C56FC9ff5548A8bdc4); + + address public constant yearn = + address(0xc7C1B907BCD3194C0D9bFA2125251af98BdDAfbb); + + function setUp() public virtual { + //This will fail if there's no mainnet variable in foundry.toml + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url); + + Market market = new Market( + gov, + lender, + pauseGuardian, + address(simpleERC20EscrowAddr), + IDolaBorrowingRights(address(dbr)), + IERC20(address(yearn)), + IOracle(address(oracle)), + 5000, + 5000, + 1000, + false + ); + yearnFeed = _deployDolaDeUSDYearnV2Feed(); + _advancedInit(address(market), address(yearnFeed), true); + } + + function _deployDolaDeUSDYearnV2Feed() + internal + returns (CurveLPYearnV2Feed feed) + { + deUSDWrapper = new ChainlinkBasePriceFeed( + gov, + address(deUSDFeed), + address(0), + 86400 + ); + + lpFeed = new CurveLPPessimisticFeed( + address(dolaDeUSD), + address(deUSDWrapper), + address(dolaFixedFeedAddr), + false + ); + + feed = new CurveLPYearnV2Feed(address(yearn), address(lpFeed)); + } +} diff --git a/test/marketForkTests/DolaUSRConvexMarketForkTest.t.sol b/test/marketForkTests/DolaUSRConvexMarketForkTest.t.sol new file mode 100644 index 00000000..98b0bde5 --- /dev/null +++ b/test/marketForkTests/DolaUSRConvexMarketForkTest.t.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {MarketBaseForkTest, IOracle, IDolaBorrowingRights, IERC20} from "./MarketBaseForkTest.sol"; +import {Market} from "src/Market.sol"; + +import {ConvexEscrowV2} from "src/escrows/ConvexEscrowV2.sol"; +import {CurveLPPessimisticFeed} from "src/feeds/CurveLPPessimisticFeed.sol"; +import {ChainlinkCurve2CoinsFeed, ICurvePool} from "src/feeds/ChainlinkCurve2CoinsFeed.sol"; +import {ChainlinkCurveFeed} from "src/feeds/ChainlinkCurveFeed.sol"; +import "src/feeds/ChainlinkBasePriceFeed.sol"; +import {console} from "forge-std/console.sol"; +import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; +import {DolaFixedPriceFeed} from "src/feeds/DolaFixedPriceFeed.sol"; +import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol"; +import {MockFeedDescription} from "test/mocks/MockFeedDescription.sol"; + +contract DolaUSRConvexMarketForkTest is MarketBaseForkTest { + ConvexEscrowV2 escrow; + + CurveLPPessimisticFeed feedDolaUSR; + DolaFixedPriceFeed dolaFeed; + + ICurvePool public constant dolaUSR = + ICurvePool(0x38De22a3175708D45E7c7c64CD78479C8B56f76E); + + address public usrFeed = address(0x34ad75691e25A8E9b681AAA85dbeB7ef6561B42c); + + ChainlinkBasePriceFeed usrWrapper; + + address rewardPool = address(0xE694a5e9272ea7ed2DC25f0c6D21640fb8a83166); + + address booster = address(0xF403C135812408BFbE8713b5A23a04b3D48AAE31); + + uint256 pid = 421; + + IERC20 public cvx = IERC20(0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B); + IERC20 public crv = IERC20(0xD533a949740bb3306d119CC777fa900bA034cd52); + + ConvexEscrowV2 userEscrow; + + function setUp() public virtual { + //This will fail if there's no mainnet variable in foundry.toml + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url); + escrow = new ConvexEscrowV2( + rewardPool, + booster, + address(cvx), + address(crv), + pid + ); + feedDolaUSR = _deployDolaUSRFeed(); + market = new Market( + gov, + fedAddr, + pauseGuardian, + address(escrow), + IDolaBorrowingRights(address(dbrAddr)), + IERC20(address(dolaUSR)), + IOracle(address(oracleAddr)), + 5000, + 5000, + 1000, + true + ); + _advancedInit(address(market), address(feedDolaUSR), true); + + userEscrow = ConvexEscrowV2( + address(Market(address(market)).predictEscrow(user)) + ); + } + + function test_escrow_immutables() public { + testDeposit(); + assertEq( + address(userEscrow.rewardPool()), + address(rewardPool), + "Reward pool not set" + ); + assertEq( + address(userEscrow.booster()), + address(booster), + "Booster not set" + ); + + assertEq(address(userEscrow.cvx()), address(cvx), "CVX not set"); + assertEq(address(userEscrow.crv()), address(crv), "CRV not set"); + } + + function _deployDolaUSRFeed() + internal + returns (CurveLPPessimisticFeed feed) + { + + usrWrapper = new ChainlinkBasePriceFeed( + gov, + usrFeed, + address(0), + 86400 + ); + feed = new CurveLPPessimisticFeed( + address(dolaUSR), + address(usrWrapper), + address(dolaFixedFeedAddr), + false + ); + } +} diff --git a/test/marketForkTests/DolaUSRYearnV2MarketForkTest.t.sol b/test/marketForkTests/DolaUSRYearnV2MarketForkTest.t.sol new file mode 100644 index 00000000..b616b3ba --- /dev/null +++ b/test/marketForkTests/DolaUSRYearnV2MarketForkTest.t.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {MarketBaseForkTest, IOracle, IDolaBorrowingRights, IERC20} from "./MarketBaseForkTest.sol"; +import {Market} from "src/Market.sol"; +import {SimpleERC20Escrow} from "src/escrows/SimpleERC20Escrow.sol"; +import {CurveLPYearnV2Feed} from "src/feeds/CurveLPYearnV2Feed.sol"; +import {ChainlinkCurve2CoinsFeed} from "src/feeds/ChainlinkCurve2CoinsFeed.sol"; +import {ChainlinkCurveFeed, ICurvePool} from "src/feeds/ChainlinkCurveFeed.sol"; +import "src/feeds/ChainlinkBasePriceFeed.sol"; +import "src/feeds/CurveLPYearnV2Feed.sol"; +import {console} from "forge-std/console.sol"; +import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; +import {CurveLPPessimisticFeed} from "src/feeds/CurveLPPessimisticFeed.sol"; +import {MockFeedDescription} from "test/mocks/MockFeedDescription.sol"; +import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol"; + +contract DolaUSRYearnV2MarketForkTest is MarketBaseForkTest { + CurveLPYearnV2Feed yearnFeed; + CurveLPPessimisticFeed lpFeed; + + address public usrFeed = address(0x34ad75691e25A8E9b681AAA85dbeB7ef6561B42c); + + ChainlinkBasePriceFeed usrWrapper; + + ICurvePool public constant dolaUSR = + ICurvePool(0x38De22a3175708D45E7c7c64CD78479C8B56f76E); + + address public constant yearn = + address(0x57a2c7925bAA1894a939f9f6721Ea33F2EcFD0e2); + + function setUp() public virtual { + //This will fail if there's no mainnet variable in foundry.toml + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url); + + Market market = new Market( + gov, + lender, + pauseGuardian, + address(simpleERC20EscrowAddr), + IDolaBorrowingRights(address(dbr)), + IERC20(address(yearn)), + IOracle(address(oracle)), + 5000, + 5000, + 1000, + false + ); + yearnFeed = _deployDolaUSRYearnV2Feed(); + _advancedInit(address(market), address(yearnFeed), true); + } + + function _deployDolaUSRYearnV2Feed() + internal + returns (CurveLPYearnV2Feed feed) + { + usrWrapper = new ChainlinkBasePriceFeed( + gov, + address(usrFeed), + address(0), + 86400 + ); + + lpFeed = new CurveLPPessimisticFeed( + address(dolaUSR), + address(usrWrapper), + address(dolaFixedFeedAddr), + false + ); + + feed = new CurveLPYearnV2Feed(address(yearn), address(lpFeed)); + } +} diff --git a/test/marketForkTests/DolasUSDeConvexMarketForkTest.t.sol b/test/marketForkTests/DolasUSDeConvexMarketForkTest.t.sol index 1278bb16..b1fb0e70 100644 --- a/test/marketForkTests/DolasUSDeConvexMarketForkTest.t.sol +++ b/test/marketForkTests/DolasUSDeConvexMarketForkTest.t.sol @@ -51,7 +51,7 @@ contract DolasUSDeConvexMarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21239297); + vm.createSelectFork(url); escrow = new ConvexEscrowV2( rewardPool, booster, diff --git a/test/marketForkTests/DolasUSDeYearnV2MarketForkTest.t.sol b/test/marketForkTests/DolasUSDeYearnV2MarketForkTest.t.sol index 20c84630..47bf6232 100644 --- a/test/marketForkTests/DolasUSDeYearnV2MarketForkTest.t.sol +++ b/test/marketForkTests/DolasUSDeYearnV2MarketForkTest.t.sol @@ -47,7 +47,7 @@ contract DolasUSDeYearnV2MarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21239297); + vm.createSelectFork(url); escrow = new SimpleERC20Escrow(); // Setup YearnVault if needed diff --git a/test/marketForkTests/DolasUSDsConvexMarketForkTest.t.sol b/test/marketForkTests/DolasUSDsConvexMarketForkTest.t.sol index 071c3846..f2154c05 100644 --- a/test/marketForkTests/DolasUSDsConvexMarketForkTest.t.sol +++ b/test/marketForkTests/DolasUSDsConvexMarketForkTest.t.sol @@ -52,7 +52,7 @@ contract DolasUSDsConvexMarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21239299); + vm.createSelectFork(url); escrow = new ConvexEscrowV2( rewardPool, booster, diff --git a/test/marketForkTests/DolasUSDsYearnV2MarketForkTest.t.sol b/test/marketForkTests/DolasUSDsYearnV2MarketForkTest.t.sol index 115c0b97..d6f376b0 100644 --- a/test/marketForkTests/DolasUSDsYearnV2MarketForkTest.t.sol +++ b/test/marketForkTests/DolasUSDsYearnV2MarketForkTest.t.sol @@ -47,7 +47,7 @@ contract DolasUSDsYearnV2MarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21239299); + vm.createSelectFork(url); escrow = new SimpleERC20Escrow(); // Setup YearnVault if needed diff --git a/test/marketForkTests/DolascrvUSDConvexMarketForkTest.t.sol b/test/marketForkTests/DolascrvUSDConvexMarketForkTest.t.sol index f9100576..29c766df 100644 --- a/test/marketForkTests/DolascrvUSDConvexMarketForkTest.t.sol +++ b/test/marketForkTests/DolascrvUSDConvexMarketForkTest.t.sol @@ -36,7 +36,7 @@ contract DolascrvUSDConvexMarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21286440); + vm.createSelectFork(url); escrow = new ConvexEscrowV2( rewardPool, booster, diff --git a/test/marketForkTests/DolascrvUSDYearnV2MarketForkTest.t.sol b/test/marketForkTests/DolascrvUSDYearnV2MarketForkTest.t.sol index 037d157e..f4925ae6 100644 --- a/test/marketForkTests/DolascrvUSDYearnV2MarketForkTest.t.sol +++ b/test/marketForkTests/DolascrvUSDYearnV2MarketForkTest.t.sol @@ -28,7 +28,7 @@ contract DolascrvUSDYearnV2MarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21286440); + vm.createSelectFork(url); Market market = new Market( gov, diff --git a/test/marketForkTests/MarketBaseForkTest.sol b/test/marketForkTests/MarketBaseForkTest.sol index f8c64ece..4a5119ba 100644 --- a/test/marketForkTests/MarketBaseForkTest.sol +++ b/test/marketForkTests/MarketBaseForkTest.sol @@ -389,6 +389,42 @@ abstract contract MarketBaseForkTest is MarketForkTest { market.borrow(borrowAmount); } + function testBorrow_Fails_When_Exceeds_Threshold() public { + gibCollateral(user, testAmount); + gibDBR(user, testAmount); + vm.startPrank(user, user); + + deposit(testAmount); + + uint256 stalenessThreshold = borrowController.stalenessThreshold(address(market)); + vm.warp( + block.timestamp + stalenessThreshold + 1 + ); + + (IChainlinkFeed feed, ) = oracle.feeds(address(collateral)); + ( + , + , + , + uint updatedAt, + ) = feed.latestRoundData(); + + uint borrowAmount = market.getCreditLimit(user); + + if(block.timestamp - updatedAt > stalenessThreshold) { + vm.expectRevert("Denied by borrow controller"); + market.borrow(borrowAmount); + } else { + uint initialDolaBalance = DOLA.balanceOf(user); + market.borrow(borrowAmount); + assertEq( + DOLA.balanceOf(user), + initialDolaBalance + borrowAmount, + "User balance did not increase by borrowAmount" + ); + } + } + function testBorrow_Fails_When_DeniedByBorrowController() public { vm.startPrank(gov); market.setBorrowController( diff --git a/test/marketForkTests/MarketForkTest.sol b/test/marketForkTests/MarketForkTest.sol index 8473c1a2..76eaa28e 100644 --- a/test/marketForkTests/MarketForkTest.sol +++ b/test/marketForkTests/MarketForkTest.sol @@ -73,6 +73,10 @@ contract MarketForkTest is Test, ConfigAddr { ); borrowController.setDailyLimit(address(market), 10_000_000 * 1e18); borrowController.setMinDebt(address(market), 1); + borrowController.setStalenessThreshold( + address(market), + 1 days + ); dbr.addMarket(address(market)); fed.changeMarketCeiling(IMarket(address(market)), type(uint).max); fed.changeSupplyCeiling(type(uint).max); diff --git a/test/marketForkTests/PendlePTUSDeMarketForkTest.t.sol b/test/marketForkTests/PendlePTsUSDe27Mar25MarketForkTest.t.sol similarity index 94% rename from test/marketForkTests/PendlePTUSDeMarketForkTest.t.sol rename to test/marketForkTests/PendlePTsUSDe27Mar25MarketForkTest.t.sol index 3f0c5908..52587d75 100644 --- a/test/marketForkTests/PendlePTUSDeMarketForkTest.t.sol +++ b/test/marketForkTests/PendlePTsUSDe27Mar25MarketForkTest.t.sol @@ -8,7 +8,7 @@ import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol"; import {DolaFixedPriceFeed} from "src/feeds/DolaFixedPriceFeed.sol"; import {FeedSwitch} from "src/util/FeedSwitch.sol"; -contract PendlePTUSDeMarketForkTest is MarketBaseForkTest { +contract PendlePTsUSDe27Mar25MarketForkTest is MarketBaseForkTest { address USDeFeed = address(0xa569d910839Ae8865Da8F8e70FfFb0cBA869F961); address sUSDeFeed = address(0xFF3BC18cCBd5999CE63E788A1c250a88626aD099); address sUSDe = address(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497); @@ -24,10 +24,10 @@ contract PendlePTUSDeMarketForkTest is MarketBaseForkTest { address marketAddr = address(0x0DFE3D04536a74Dd532dd0cEf5005bA14c5f4112); address feedAddr = address(0xddB5653FaC7a215139141863B2FAd021D44d7Ee4); - function setUp() public { + function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21077341); + vm.createSelectFork(url, 21573122); _advancedInit(address(marketAddr), feedAddr, false); } diff --git a/test/marketForkTests/PendlePTsUSDe29May25MarketForkTest.t.sol b/test/marketForkTests/PendlePTsUSDe29May25MarketForkTest.t.sol new file mode 100644 index 00000000..4e63f749 --- /dev/null +++ b/test/marketForkTests/PendlePTsUSDe29May25MarketForkTest.t.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "./MarketBaseForkTest.sol"; +import {USDeNavBeforeMaturityFeed} from "src/feeds/USDeNavBeforeMaturityFeed.sol"; +import {ChainlinkBasePriceFeed} from "src/feeds/ChainlinkBasePriceFeed.sol"; +import {FeedSwitch} from "src/util/FeedSwitch.sol"; +import {PendleNAVFeed} from "src/feeds/PendleNAVFeed.sol"; +contract PendlePTsUSDe29May25MarketForkTest is MarketBaseForkTest { + address USDeFeed = address(0xa569d910839Ae8865Da8F8e70FfFb0cBA869F961); + address sUSDeFeed = address(0xFF3BC18cCBd5999CE63E788A1c250a88626aD099); + address sUSDe = address(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497); + address pendlePT = address(0xb7de5dFCb74d25c2f21841fbd6230355C50d9308); // PT sUSDe 29 May 25 + address pendlePTHolder = + address(0x8C0824fFccBE9A3CDda4c3d409A0b7447320F364); + + ChainlinkBasePriceFeed sUSDeWrappedFeed; + USDeNavBeforeMaturityFeed beforeMaturityFeed; + ChainlinkBasePriceFeed afterMaturityFeed; + address navFeed; + + uint256 baseDiscount = 0.2 ether; // 20% + FeedSwitch feedSwitch; + + address feedAddr = 0x8f5d8A77e6C1943218854B1eef22401760D4ca10; //FeedSwitch + address marketAddr = 0x2D4788893DE7a4fB42106D9Db36b65463428FBD9; + + function setUp() public virtual { + //This will fail if there's no mainnet variable in foundry.toml + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url,22241889); + + _advancedInit(marketAddr, feedAddr, false); + } + + function _deployFeed() internal returns (address feed) { + sUSDeWrappedFeed = new ChainlinkBasePriceFeed( + gov, + sUSDeFeed, + address(0), + 24 hours + ); + navFeed = address(new PendleNAVFeed(pendlePT, baseDiscount)); + beforeMaturityFeed = new USDeNavBeforeMaturityFeed( + address(sUSDeWrappedFeed), + address(sUSDe), + address(navFeed) + ); + afterMaturityFeed = new ChainlinkBasePriceFeed( + gov, + USDeFeed, + address(0), + 24 hours + ); + + feedSwitch = new FeedSwitch( + address(navFeed), + address(beforeMaturityFeed), + address(afterMaturityFeed), + 18 hours, + pendlePT, + pauseGuardian + ); + return address(feedSwitch); + } + + // Override the function to use the PendlePTHolder to avoid error revert: stdStorage find(StdStorage): Slot(s) not found + function gibCollateral( + address _address, + uint _amount + ) internal virtual override { + vm.prank(pendlePTHolder); + IERC20(pendlePT).transfer(_address, _amount); + } +} diff --git a/test/marketForkTests/SDolascrvUSDConvexMarketForkTest.t.sol b/test/marketForkTests/SDolascrvUSDConvexMarketForkTest.t.sol index b9d9fd12..170b93bc 100644 --- a/test/marketForkTests/SDolascrvUSDConvexMarketForkTest.t.sol +++ b/test/marketForkTests/SDolascrvUSDConvexMarketForkTest.t.sol @@ -36,7 +36,7 @@ contract SDolascrvUSDConvexMarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21386890); + vm.createSelectFork(url); escrow = new ConvexEscrowV2( rewardPool, booster, diff --git a/test/marketForkTests/SDolascrvUSDYearnV2MarketForkTest.t.sol b/test/marketForkTests/SDolascrvUSDYearnV2MarketForkTest.t.sol index 8cf71c72..f063ecda 100644 --- a/test/marketForkTests/SDolascrvUSDYearnV2MarketForkTest.t.sol +++ b/test/marketForkTests/SDolascrvUSDYearnV2MarketForkTest.t.sol @@ -43,7 +43,7 @@ contract SDolascrvUSDYearnV2MarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21391176); + vm.createSelectFork(url); // Setup YearnVault if needed if (yearn == address(0)) { diff --git a/test/marketForkTests/SdeUSDMarketForkTest.t.sol b/test/marketForkTests/SdeUSDMarketForkTest.t.sol index 7b335e62..adcfe610 100644 --- a/test/marketForkTests/SdeUSDMarketForkTest.t.sol +++ b/test/marketForkTests/SdeUSDMarketForkTest.t.sol @@ -17,7 +17,7 @@ contract SdeUSDMarketForkTest is MarketBaseForkTest { function setUp() public virtual { //This will fail if there's no mainnet variable in foundry.toml string memory url = vm.rpcUrl("mainnet"); - vm.createSelectFork(url, 21880783); + vm.createSelectFork(url, 21880983); address curveFeed = address( new ChainlinkCurveFeed(dolaFeed, curvePool, k, targetIndex) ); diff --git a/test/marketForkTests/StYEthMarketForkTest.t.sol b/test/marketForkTests/StYEthMarketForkTest.t.sol index 28ec95b8..6ad472e4 100644 --- a/test/marketForkTests/StYEthMarketForkTest.t.sol +++ b/test/marketForkTests/StYEthMarketForkTest.t.sol @@ -6,7 +6,7 @@ import {Market} from "src/Market.sol"; import {SimpleERC20Escrow} from "src/escrows/SimpleERC20Escrow.sol"; import {StYEthPriceFeed} from "src/feeds/StYEthPriceFeed.sol"; import {ERC4626Helper, IERC4626} from "src/util/ERC4626Helper.sol"; -import {ERC4626Helper, IERC4626, IMultiMarketTransformHelper} from "src/util/ERC4626Helper.sol"; +import {ERC4626Helper, IERC4626, IMultiMarketConvertHelper} from "src/util/ERC4626Helper.sol"; contract StYEthMarketForkTest is MarketBaseForkTest { SimpleERC20Escrow escrow; @@ -58,7 +58,7 @@ contract StYEthMarketForkTest is MarketBaseForkTest { vm.startPrank(user); yEth.approve(address(helper), type(uint).max); - helper.transformToCollateralAndDeposit(initAmount, user, data); + helper.convertToCollateralAndDeposit(initAmount, user, data); assertEq(yEth.balanceOf(user), 0); } @@ -71,7 +71,7 @@ contract StYEthMarketForkTest is MarketBaseForkTest { vm.startPrank(userPk); yEth.approve(address(helper), type(uint).max); - helper.transformToCollateralAndDeposit(initAmount / 2, userPk, data); + helper.convertToCollateralAndDeposit(initAmount / 2, userPk, data); // Amount of SHARES to withdraw uint256 withdrawAmount = market.predictEscrow(userPk).balance(); @@ -95,14 +95,14 @@ contract StYEthMarketForkTest is MarketBaseForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - IMultiMarketTransformHelper.Permit - memory permit = IMultiMarketTransformHelper.Permit({ + IMultiMarketConvertHelper.Permit + memory permit = IMultiMarketConvertHelper.Permit({ deadline: block.timestamp, v: v, r: r, s: s }); - helper.withdrawAndTransformFromCollateral( + helper.withdrawAndConvertFromCollateral( withdrawAmount, userPk, permit, @@ -124,7 +124,7 @@ contract StYEthMarketForkTest is MarketBaseForkTest { address(0) ) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10 ether, address(0), abi.encode(address(0)) @@ -148,7 +148,7 @@ contract StYEthMarketForkTest is MarketBaseForkTest { abi.encodeWithSelector(ERC4626Helper.InsufficientShares.selector) ); - helper.transformToCollateralAndDeposit(10 ether, user, data); + helper.convertToCollateralAndDeposit(10 ether, user, data); vm.expectRevert( abi.encodeWithSelector( @@ -156,15 +156,15 @@ contract StYEthMarketForkTest is MarketBaseForkTest { address(0) ) ); - IMultiMarketTransformHelper.Permit - memory permit = IMultiMarketTransformHelper.Permit({ + IMultiMarketConvertHelper.Permit + memory permit = IMultiMarketConvertHelper.Permit({ deadline: block.timestamp, v: 0, r: bytes32(0), s: bytes32(0) }); data = abi.encode(address(0)); - helper.withdrawAndTransformFromCollateral(10 ether, user, permit, data); + helper.withdrawAndConvertFromCollateral(10 ether, user, permit, data); vm.clearMockedCalls(); @@ -174,7 +174,7 @@ contract StYEthMarketForkTest is MarketBaseForkTest { vm.startPrank(userPk); yEth.approve(address(helper), type(uint).max); data = abi.encode(address(market)); - helper.transformToCollateralAndDeposit(10 ether, userPk, data); + helper.convertToCollateralAndDeposit(10 ether, userPk, data); // Amount of SHARES to withdraw uint256 withdrawAmount = market.predictEscrow(userPk).balance(); @@ -206,14 +206,14 @@ contract StYEthMarketForkTest is MarketBaseForkTest { vm.expectRevert( abi.encodeWithSelector(ERC4626Helper.InsufficientShares.selector) ); - IMultiMarketTransformHelper.Permit - memory permit2 = IMultiMarketTransformHelper.Permit({ + IMultiMarketConvertHelper.Permit + memory permit2 = IMultiMarketConvertHelper.Permit({ deadline: block.timestamp, v: v, r: r, s: s }); - helper.withdrawAndTransformFromCollateral( + helper.withdrawAndConvertFromCollateral( withdrawAmount, userPk, permit2, diff --git a/test/mocks/MockFeedDescription.sol b/test/mocks/MockFeedDescription.sol new file mode 100644 index 00000000..3b8ec72f --- /dev/null +++ b/test/mocks/MockFeedDescription.sol @@ -0,0 +1,51 @@ +pragma solidity ^0.8.13; + +interface IChainlinkFeed { + function decimals() external view returns (uint8); + function latestAnswer() external view returns (uint); +} + +contract Aggregator { + function maxAnswer() external pure returns (int192) { + return type(int192).max; + } + function minAnswer() external pure returns (int192) { + return type(int192).min; + } +} + +contract MockFeedDescription is IChainlinkFeed { + uint8 public decimals; + int price; + uint updatedAt; + string public description; + Aggregator public aggregator; + + constructor(uint8 _decimals, int _price, string memory _description) { + updatedAt = block.timestamp; + decimals = _decimals; + price = _price; + description = _description; + aggregator = new Aggregator(); + } + + function latestAnswer() external view returns (uint) { + return uint(price); + } + + function latestRoundData() + external + view + returns (uint80, int256, uint256, uint256, uint80) + { + return (0, price, 0, updatedAt, 0); + } + + function changeAnswer(uint _price) external { + price = int(_price); + } + + function changeUpdatedAt(uint _updatedAt) external { + updatedAt = _updatedAt; + } +} diff --git a/test/mocks/MockPendleRouter.sol b/test/mocks/MockPendleRouter.sol new file mode 100644 index 00000000..04b9e13b --- /dev/null +++ b/test/mocks/MockPendleRouter.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +contract MockPendleRouter { + struct ApproxParams { + uint256 guessMin; + uint256 guessMax; + uint256 guessOffchain; + uint256 maxIteration; + uint256 eps; + } + + struct TokenInput { + address tokenIn; + uint256 netTokenIn; + address tokenMintSy; + address pendleSwap; + SwapData swapData; + } + struct TokenOutput { + address tokenOut; + uint256 minTokenOut; + address tokenRedeemSy; + address pendleSwap; + SwapData swapData; + } + + struct LimitOrderData { + address limitRouter; + uint256 epsSkipMarket; + FillOrderParams[] normalFills; + FillOrderParams[] flashFills; + bytes optData; + } + + struct Order { + uint256 salt; + uint256 expiry; + uint256 nonce; + OrderType orderType; + address token; + address YT; + address maker; + address receiver; + uint256 makingAmount; + uint256 lnImpliedRate; + uint256 failSafeRate; + bytes permit; + } + + enum OrderType { + SY_FOR_PT, + PT_FOR_SY, + SY_FOR_YT, + YT_FOR_SY + } + + struct FillOrderParams { + Order order; + bytes signature; + uint256 makingAmount; + } + + struct SwapData { + SwapType swapType; + address extRouter; + bytes extCalldata; + bool needScale; + } + + enum SwapType { + NONE, + KYBERSWAP, + ODOS, + // ETH_WETH not used in Aggregator + ETH_WETH, + OKX, + ONE_INCH, + RESERVE_1, + RESERVE_2, + RESERVE_3, + RESERVE_4, + RESERVE_5 + } + IERC20 public DOLA; + IERC20 public pendlePT; + IERC20 public pendleYT; + constructor(address _dola, address _pt, address _yt) { + DOLA = IERC20(_dola); + pendlePT = IERC20(_pt); + pendleYT = IERC20(_yt); + } + function swapExactTokenForPt( + address receiver, + address /*market*/, + uint256 minPtOut, + ApproxParams calldata /*guessPtOut*/, + TokenInput calldata /*input*/, + LimitOrderData calldata /*limit*/ + ) external { + DOLA.transferFrom(msg.sender, address(this), minPtOut); + pendlePT.transfer(receiver, minPtOut); + } + + function swapExactPtForToken( + address receiver, + address /*market*/, + uint256 exactPtIn, + TokenOutput calldata /*output*/, + LimitOrderData calldata /*limit*/ + ) external { + pendlePT.transferFrom(msg.sender, address(this), exactPtIn); + DOLA.transfer(receiver, exactPtIn); + } + + function mintPyFromToken( + address receiver, + address /*YT*/, + uint256 minPyOut, + TokenInput calldata /*input*/ + ) external { + DOLA.transferFrom(msg.sender, address(this), minPyOut); + pendlePT.transfer(receiver, minPyOut); + pendleYT.transfer(receiver, minPyOut); + } + + function redeemPyToToken( + address receiver, + address /*YT*/, + uint256 netPyIn, + TokenOutput calldata /*output*/ + ) external { + pendlePT.transferFrom(msg.sender, address(this), netPyIn); + pendleYT.transferFrom(msg.sender, address(this), netPyIn); + DOLA.transfer(receiver, netPyIn); + } +} diff --git a/test/mocks/YETHFeed.sol b/test/mocks/YETHFeed.sol index e9f363dc..7af83c0b 100644 --- a/test/mocks/YETHFeed.sol +++ b/test/mocks/YETHFeed.sol @@ -8,7 +8,7 @@ interface IChainlinkFeed { contract YETHFeed is IChainlinkFeed { uint8 decimals_ = 18; - uint price_ = 2626e18; + uint price_ = 1500e18; function decimals() external view returns (uint8) { return decimals_; diff --git a/test/util/BaseFeedSwitchNavFork.t.sol b/test/util/BaseFeedSwitchNavFork.t.sol new file mode 100644 index 00000000..d9c8c96d --- /dev/null +++ b/test/util/BaseFeedSwitchNavFork.t.sol @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import "forge-std/Test.sol"; +import {FeedSwitch, IPendlePT, IChainlinkFeed} from "src/util/FeedSwitch.sol"; +import {ConfigAddr} from "test/ConfigAddr.sol"; +import {console} from "forge-std/console.sol"; +import {MockFeed} from "test/mocks/MockFeed.sol"; + + +contract MockPendlePT { + function expiry() external pure returns (uint256) { + return 100; + } +} + + +interface INavFeed { + function getDiscount(uint256 timeLeft) external view returns (uint256) ; + function maturity() external view returns (uint256); + function decimals() external view returns (uint8); +} + +abstract contract BaseFeedSwitchNavForkTest is Test, ConfigAddr { + FeedSwitch feedSwitch; + IChainlinkFeed navFeed; + IChainlinkFeed beforeMaturityFeed; + IChainlinkFeed afterMaturityFeed; + address guardian = pauseGuardian; + address pendlePT = address(0xb7de5dFCb74d25c2f21841fbd6230355C50d9308); + uint256 baseDiscount; + uint256 timeLockPeriod = 18 hours; + + function initialize(address _beforeMaturityFeed, address _afterMaturityFeed, address _pendlePT, uint256 _baseDiscount, address _navFeed) public { + afterMaturityFeed = IChainlinkFeed(_afterMaturityFeed); + pendlePT = _pendlePT; + baseDiscount = _baseDiscount; + navFeed = IChainlinkFeed(_navFeed); + beforeMaturityFeed = IChainlinkFeed(_beforeMaturityFeed); + feedSwitch = new FeedSwitch( + address(navFeed), + address(beforeMaturityFeed), + address(afterMaturityFeed), + timeLockPeriod, + pendlePT, + guardian + ); + } + function test_Deployment() public view { + assertEq(address(feedSwitch.feed()), address(navFeed)); + assertEq( + address(feedSwitch.beforeMaturityFeed()), + address(beforeMaturityFeed) + ); + assertEq( + address(feedSwitch.afterMaturityFeed()), + address(afterMaturityFeed) + ); + assertEq(feedSwitch.timelockPeriod(), 18 hours); + assertEq(feedSwitch.maturity(), IPendlePT(pendlePT).expiry()); + assertEq(feedSwitch.guardian(), guardian); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + + assertEq(INavFeed(address(navFeed)).maturity(), IPendlePT(pendlePT).expiry()); + assertEq(INavFeed(address(navFeed)).decimals(), 18); + } + + function test_updateAt_NAVFeed() public { + INavFeed nav = INavFeed(address(navFeed)); + vm.warp(nav.maturity() - 365 days); + uint256 discount = nav.getDiscount(365 days); + (uint80 roundId, int256 price, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) = navFeed.latestRoundData(); + assertEq(uint(price), 1 ether - discount); + assertEq(startedAt, 0); + assertEq(updatedAt, block.timestamp); + assertEq(answeredInRound, 0); + } + function test_NavDiscount() public { + INavFeed nav = INavFeed(address(navFeed)); + + uint256 discount = nav.getDiscount(365 days); + assertEq(discount, baseDiscount); + // 1 year before maturity + vm.warp(nav.maturity() - 365 days); + assertEq(uint(feedSwitch.latestAnswer()), 1 ether - discount); + + discount = nav.getDiscount(365 days / 2); + // 6 months before maturity + vm.warp(block.timestamp + 365 days / 2); + assertEq(discount, baseDiscount / 2); + assertEq(uint(feedSwitch.latestAnswer()), 1 ether - discount); + + discount = nav.getDiscount(0); + // At maturity + vm.warp(nav.maturity()); + assertEq(discount, 0); + assertNotEq(uint(feedSwitch.latestAnswer()), 1 ether - discount); + // Already uses after maturity feed + assertEq(uint(feedSwitch.latestAnswer()), uint(afterMaturityFeed.latestAnswer())); + } + + function test_InitiateFeedSwitch() public { + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq( + feedSwitch.switchCompletedAt(), + block.timestamp + feedSwitch.timelockPeriod() + ); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + } + + function test_Fail_InitiateFeedSwitchNotGuardian() public { + vm.expectRevert(FeedSwitch.NotGuardian.selector); + feedSwitch.initiateFeedSwitch(); + } + + function test_SwitchFeed_before_maturity() public { + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + + vm.warp(block.timestamp + 0.5 days); + int256 price = feedSwitch.latestAnswer(); + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq(uint(price), uint(navFeedPrice), "initial feed"); + // Not yet switched + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod() - 0.5 days); + + vm.warp(block.timestamp + 1 days); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + + // After switch, not queued anymore + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_SwitchFeed_after_maturity_after_switch() public { + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + vm.warp(block.timestamp + 1 days); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + vm.warp(IPendlePT(pendlePT).expiry() + 1); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(afterMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_SwitchFeed_after_maturity() public { + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + + vm.warp(IPendlePT(pendlePT).expiry() + 1); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(afterMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_SwitchFeed_before_maturity_and_after_maturity() public { + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + + // Before Maturity + vm.warp(block.timestamp + 1 days); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + + vm.warp(IPendlePT(pendlePT).expiry() + 1); + // After Maturity + assertEq( + uint(feedSwitch.latestAnswer()), + uint(afterMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_Cancel_feed_switch() public { + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), block.timestamp + 18 hours); + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + vm.warp(block.timestamp + 0.5 days); + + (bool isQueued, uint timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod() - 0.5 days); + + // Cancel the feed switch + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), 0); + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice), + "before feed switch" + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + + vm.warp(block.timestamp + 1 days); + assertEq(feedSwitch.switchCompletedAt(), 0); + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_Cancel_feed_switch_and_reswitch() public { + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), block.timestamp + 18 hours); + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + vm.warp(block.timestamp + 0.5 days); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod() - 0.5 days); + + // Cancel the feed switch + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), 0); + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice), + "before feed switch" + ); + assertEq(feedSwitch.switchCompletedAt(), 0); + // Not queued anymore + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + + // After the feed is canceled, it keeps using the navFeed + vm.warp(block.timestamp + 1 days); + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + // Initiate a feed switch again + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), block.timestamp + 18 hours); + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + + vm.warp(block.timestamp + 1 days); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + // Feed switched so not queued anymore + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_Cancel_feed_switch_with_beforeMaturityFeed_and_reswitch() + public + { + // Switch feed to beforeMaturityFeed + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), block.timestamp + 18 hours); + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + + vm.warp(block.timestamp + 1 days); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + + // Initiate a feed switch again + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), block.timestamp + 18 hours); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + + // Cancel it when it is in the timelock period and keep using beforeMaturityFeed + vm.warp(block.timestamp + 0.5 days); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod() - 0.5 days); + + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), 0); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + // Initiate a feed switch again + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq(feedSwitch.switchCompletedAt(), block.timestamp + 18 hours); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + + vm.warp(block.timestamp + 1 days); + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_SwitchFeed_twice_before_maturity() public { + // Previous Feed is not initialized and current feed is navFeed + assertEq(address(feedSwitch.previousFeed()), address(0)); + assertEq(address(feedSwitch.feed()), address(navFeed)); + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + // Initiate a feed switch + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + // Feed switch initiated + assertEq(address(feedSwitch.previousFeed()), address(navFeed)); + assertEq(address(feedSwitch.feed()), address(beforeMaturityFeed)); + // Before timelock period, navFeed is still the one used + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (bool isQueued, uint timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + + // After timelock period, beforeMaturityFeed is used + vm.warp(block.timestamp + 1 days); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + + // After the switch is completed, the feed is switched back to navFeed + assertEq(address(feedSwitch.previousFeed()), address(navFeed)); + assertEq(address(feedSwitch.feed()), address(beforeMaturityFeed)); + + vm.prank(guardian); + feedSwitch.initiateFeedSwitch(); + assertEq( + address(feedSwitch.previousFeed()), + address(beforeMaturityFeed) + ); + assertEq(address(feedSwitch.feed()), address(navFeed)); + // Before timelock period, beforeMaturityFeed is still the one used + assertEq( + uint(feedSwitch.latestAnswer()), + uint(beforeMaturityFeed.latestAnswer()) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, true); + assertEq(timeLeft, feedSwitch.timelockPeriod()); + + vm.warp(block.timestamp + 1 days); + // After timelock period, navFeed is used + (,navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq( + uint(feedSwitch.latestAnswer()), + uint(navFeedPrice) + ); + (isQueued, timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_Fail_initiateFeedSwitch_after_maturity() public { + vm.warp(IPendlePT(pendlePT).expiry() + 1); + vm.prank(guardian); + vm.expectRevert(FeedSwitch.MaturityPassed.selector); + feedSwitch.initiateFeedSwitch(); + } + + function test_LatestRoundData() public view { + ( + uint80 roundId, + int256 price, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) = feedSwitch.latestRoundData(); + (,int256 navFeedPrice,, uint256 updatedAtNav,) = navFeed.latestRoundData(); + + assertEq(updatedAt, updatedAtNav); + assertEq(uint(price), uint(navFeedPrice)); + } + + function test_LatestAnswer() public view { + int256 price = feedSwitch.latestAnswer(); + (,int256 navFeedPrice,,,) = navFeed.latestRoundData(); + assertEq(uint(price), uint(navFeedPrice)); + } + + function test_Decimals() public view { + uint8 decimals = feedSwitch.decimals(); + assertEq(decimals, 18); + } + + function test_isFeedSwitchQueued() public view { + (bool isQueued, uint256 timeLeft) = feedSwitch.isFeedSwitchQueued(); + assertEq(isQueued, false); + assertEq(timeLeft, 0); + } + + function test_Deploy_Revert_Wrong_Decimals() public { + MockFeed wrongDecimalsFeed = new MockFeed(8, 1e18); + vm.expectRevert(FeedSwitch.FeedDecimalsMismatch.selector); + FeedSwitch feedSwitch2 = new FeedSwitch( + address(wrongDecimalsFeed), + address(beforeMaturityFeed), + address(afterMaturityFeed), + 18 hours, + pendlePT, + guardian + ); + + vm.expectRevert(FeedSwitch.FeedDecimalsMismatch.selector); + feedSwitch2 = new FeedSwitch( + address(navFeed), + address(wrongDecimalsFeed), + address(afterMaturityFeed), + 18 hours, + pendlePT, + guardian + ); + + vm.expectRevert(FeedSwitch.FeedDecimalsMismatch.selector); + feedSwitch2 = new FeedSwitch( + address(navFeed), + address(beforeMaturityFeed), + address(wrongDecimalsFeed), + 18 hours, + pendlePT, + guardian + ); + } + + function test_Deploy_maturity_in_past() public { + MockPendlePT MockPendlePT = new MockPendlePT(); + vm.expectRevert(FeedSwitch.MaturityInPast.selector); + FeedSwitch feedSwitch2 = new FeedSwitch( + address(navFeed), + address(beforeMaturityFeed), + address(afterMaturityFeed), + 18 hours, + address(MockPendlePT), + guardian + ); + } +} diff --git a/test/util/CurveDolaLPHelperDolaCrvUSD.t.sol b/test/util/CurveDolaLPHelperDolaCrvUSD.t.sol index 19de3dc7..7a6e6260 100644 --- a/test/util/CurveDolaLPHelperDolaCrvUSD.t.sol +++ b/test/util/CurveDolaLPHelperDolaCrvUSD.t.sol @@ -4,7 +4,7 @@ import {ICurvePool} from "src/interfaces/ICurvePool.sol"; import {CurveDolaLPHelper} from "src/util/CurveDolaLPHelper.sol"; import "test/marketForkTests/CrvUSDDolaConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {IMultiMarketConvertHelper} from "src/interfaces/IMultiMarketConvertHelper.sol"; contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { CurveDolaLPHelper helper; @@ -27,15 +27,16 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { vm.stopPrank(); } - function test_transformToCollateral() public { + function test_convertToCollateral() public { uint256 amount = 100 ether; // Estimate LP amount uint256[2] memory amounts = [amount, 0]; uint estLpAmount = dolaCrvUSD.calc_token_amount(amounts, true); DOLA.approve(address(helper), amount); - uint256 lpAmount = helper.transformToCollateral( + uint256 lpAmount = helper.convertToCollateral( amount, + address(this), abi.encode(address(market), uint(1)) ); assertEq( @@ -45,14 +46,14 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { assertEq(lpAmount, estLpAmount); } - function test_transformToCollateral_receiver() public { + function test_convertToCollateral_receiver() public { uint256 amount = 100 ether; // Estimate LP amount uint256[2] memory amounts = [amount, 0]; uint estLpAmount = dolaCrvUSD.calc_token_amount(amounts, true); DOLA.approve(address(helper), amount); - uint256 lpAmount = helper.transformToCollateral( + uint256 lpAmount = helper.convertToCollateral( amount, receiver, abi.encode(address(market), uint(1)) @@ -61,14 +62,14 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { assertEq(lpAmount, estLpAmount); } - function test_transformToCollateralAndDeposit() public { + function test_convertToCollateralAndDeposit() public { uint256 amount = 100 ether; // Estimate LP amount uint256[2] memory amounts = [amount, 0]; uint estLpAmount = dolaCrvUSD.calc_token_amount(amounts, true); DOLA.approve(address(helper), amount); - uint256 lpAmount = helper.transformToCollateralAndDeposit( + uint256 lpAmount = helper.convertToCollateralAndDeposit( amount, address(this), abi.encode(address(market), uint(1)) @@ -82,14 +83,14 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { assertEq(lpAmount, estLpAmount); } - function test_transformToCollateralAndDeposit_receiver() public { + function test_convertToCollateralAndDeposit_receiver() public { uint256 amount = 100 ether; // Estimate LP amount uint256[2] memory amounts = [amount, 0]; uint estLpAmount = dolaCrvUSD.calc_token_amount(amounts, true); DOLA.approve(address(helper), amount); - uint256 lpAmount = helper.transformToCollateralAndDeposit( + uint256 lpAmount = helper.convertToCollateralAndDeposit( amount, receiver, abi.encode(address(market), uint(1)) @@ -102,14 +103,15 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { assertEq(lpAmount, estLpAmount); } - function test_transformFromCollateral() public { - test_transformToCollateral(); + function test_convertFromCollateral() public { + test_convertToCollateral(); uint256 amount = IERC20(address(dolaCrvUSD)).balanceOf(address(this)); // Estimate DOLA amount uint estDolaAmount = dolaCrvUSD.calc_withdraw_one_coin(amount, 0); uint dolaBalBefore = DOLA.balanceOf(address(this)); IERC20(address(dolaCrvUSD)).approve(address(helper), amount); - uint256 dolaAmount = helper.transformFromCollateral( + uint256 dolaAmount = helper.convertFromCollateral( + address(0), amount, abi.encode(address(market), uint(1)) ); @@ -118,14 +120,14 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { assertEq(dolaAmount, estDolaAmount); } - function test_transformFromCollateral_receiver() public { - test_transformToCollateral(); + function test_convertFromCollateral_receiver() public { + test_convertToCollateral(); uint256 amount = IERC20(address(dolaCrvUSD)).balanceOf(address(this)); // Estimate DOLA amount uint estDolaAmount = dolaCrvUSD.calc_withdraw_one_coin(amount, 0); IERC20(address(dolaCrvUSD)).approve(address(helper), amount); - uint256 dolaAmount = helper.transformFromCollateral( + uint256 dolaAmount = helper.convertFromCollateral( amount, receiver, abi.encode(address(market), uint(1)) @@ -135,8 +137,8 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { assertEq(dolaAmount, estDolaAmount); } - function test_withdrawAndTransformFromCollateral() public { - test_transformToCollateralAndDeposit_receiver(); + function test_withdrawAndConvertFromCollateral() public { + test_convertToCollateralAndDeposit_receiver(); uint256 amount = ConvexEscrowV2(address(market.predictEscrow(receiver))) .balance(); // Estimate DOLA amount @@ -163,15 +165,15 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(23, hash); - IMultiMarketTransformHelper.Permit - memory permit = IMultiMarketTransformHelper.Permit( + IMultiMarketConvertHelper.Permit + memory permit = IMultiMarketConvertHelper.Permit( block.timestamp, v, r, s ); vm.prank(receiver); - uint256 dolaAmount = helper.withdrawAndTransformFromCollateral( + uint256 dolaAmount = helper.withdrawAndConvertFromCollateral( amount, receiver, permit, @@ -182,8 +184,8 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { assertEq(dolaAmount, estDolaAmount); } - function test_withdrawAndTransformFromCollateral_other_receiver() public { - test_transformToCollateralAndDeposit_receiver(); + function test_withdrawAndConvertFromCollateral_other_receiver() public { + test_convertToCollateralAndDeposit_receiver(); uint256 amount = ConvexEscrowV2(address(market.predictEscrow(receiver))) .balance(); // Estimate DOLA amount @@ -211,15 +213,15 @@ contract CurveDolaLPHelperTest is CrvUSDDolaConvexMarketForkTest { (uint8 v, bytes32 r, bytes32 s) = vm.sign(23, hash); - IMultiMarketTransformHelper.Permit - memory permit = IMultiMarketTransformHelper.Permit( + IMultiMarketConvertHelper.Permit + memory permit = IMultiMarketConvertHelper.Permit( block.timestamp, v, r, s ); vm.prank(receiver); - uint256 dolaAmount = helper.withdrawAndTransformFromCollateral( + uint256 dolaAmount = helper.withdrawAndConvertFromCollateral( amount, address(this), permit, diff --git a/test/util/FeedSwitchNavSUSDe29May25.t.sol b/test/util/FeedSwitchNavSUSDe29May25.t.sol new file mode 100644 index 00000000..dcbdc014 --- /dev/null +++ b/test/util/FeedSwitchNavSUSDe29May25.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +import {FeedSwitch, IChainlinkFeed} from "src/util/FeedSwitch.sol"; +import {BaseFeedSwitchNavForkTest} from "test/util/BaseFeedSwitchNavFork.t.sol"; +import {USDeNavBeforeMaturityFeed} from "src/feeds/USDeNavBeforeMaturityFeed.sol"; +import {PendleNAVFeed} from "src/feeds/PendleNAVFeed.sol"; + +contract FeedSwitchNavSUSDe29May25Test is BaseFeedSwitchNavForkTest { + address _beforeMaturityFeed; + address _afterMaturityFeed = address(0xB3C1D801A02d88adC96A294123c2Daa382345058); // USDe Chainlink Wrapper + address _pendlePT = address(0xb7de5dFCb74d25c2f21841fbd6230355C50d9308); // PT sUSDe 29 May 25 + uint256 _baseDiscount = 0.2 ether; // 20% + address sUSDeWrapper = address(0xD723a0910e261de49A90779d38A94aFaAA028F15); + address sUSDe = address(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497); + + function setUp() public { + string memory url = vm.rpcUrl("mainnet"); + vm.createSelectFork(url, 22018716); + + PendleNAVFeed _navFeed = new PendleNAVFeed(_pendlePT, _baseDiscount); + _beforeMaturityFeed = address(new USDeNavBeforeMaturityFeed(sUSDeWrapper,sUSDe,address(_navFeed))); // USDeBeforeMaturityFeed: USDe/USD Feed using sUSDe Chainlink feed and sUSDe/USDe rate and NAV + + initialize(address(_beforeMaturityFeed), address(_afterMaturityFeed), _pendlePT , _baseDiscount, address(_navFeed)); + } +} diff --git a/test/util/aleTests/ALEBaseDolaLPDyn.sol b/test/util/aleTests/ALEBaseDolaLPDyn.sol index 552455ca..4c4ef3cc 100644 --- a/test/util/aleTests/ALEBaseDolaLPDyn.sol +++ b/test/util/aleTests/ALEBaseDolaLPDyn.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/DolasUSDsConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {ConfigAddr} from "test/ConfigAddr.sol"; import {Test} from "forge-std/Test.sol"; import {MarketForkTest} from "test/marketForkTests/MarketForkTest.sol"; @@ -19,7 +19,7 @@ interface IFlashMinter { } abstract contract ALEBaseDolaLPDynTest is MarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelperDynamic helper; @@ -32,7 +32,7 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -66,11 +66,11 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[0] = maxBorrowAmount; @@ -100,7 +100,7 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -136,11 +136,11 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 97) / 100, 0 @@ -176,7 +176,7 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -209,11 +209,11 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[0] = maxBorrowAmount + initialDolaDeposit; @@ -246,11 +246,12 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 11000 ether); - uint256 initialLpAmount = helper.transformToCollateral( + uint256 initialLpAmount = helper.convertToCollateral( + address(0), 1000 ether, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -283,11 +284,11 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[0] = maxBorrowAmount; @@ -345,17 +346,17 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -402,11 +403,11 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -416,8 +417,8 @@ abstract contract ALEBaseDolaLPDynTest is MarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol b/test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol index cb75ee68..3add1970 100644 --- a/test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol +++ b/test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/MarketForkTest.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; interface IFlashMinter { @@ -23,7 +23,7 @@ interface ICurve { ) external returns (uint256); } abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelperDynamic helper; @@ -37,7 +37,7 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 1000000 ether, userPk, abi.encode(address(market), 0) @@ -70,11 +70,11 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[0] = maxBorrowAmount; @@ -104,7 +104,7 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -140,11 +140,11 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 98) / 100, 0 @@ -179,7 +179,7 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -212,11 +212,11 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[0] = maxBorrowAmount + initialDolaDeposit; @@ -249,11 +249,12 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 11000 ether); - uint256 initialSharesAmount = helper.transformToCollateral( + uint256 initialSharesAmount = helper.convertToCollateral( + address(0), 1000 ether, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -286,11 +287,11 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[0] = maxBorrowAmount; @@ -352,17 +353,17 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -387,7 +388,7 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000 ether); - uint256 yearnLeftover = helper.transformToCollateral( + uint256 yearnLeftover = helper.convertToCollateral( 1000 ether, address(helper), abi.encode(address(market), 0) @@ -426,17 +427,17 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -487,11 +488,11 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -501,8 +502,8 @@ abstract contract ALEBaseDolaLPDynYearnV2Test is MarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEBaseSDolaLPDyn.sol b/test/util/aleTests/ALEBaseSDolaLPDyn.sol index 65d0b76b..c445b872 100644 --- a/test/util/aleTests/ALEBaseSDolaLPDyn.sol +++ b/test/util/aleTests/ALEBaseSDolaLPDyn.sol @@ -5,7 +5,7 @@ import {CurveSDolaLPHelperDynamic} from "src/util/CurveSDolaLPHelperDynamic.sol" import "test/marketForkTests/DolasUSDsConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {ConfigAddr} from "test/ConfigAddr.sol"; import {Test} from "forge-std/Test.sol"; import {MarketForkTest} from "test/marketForkTests/MarketForkTest.sol"; @@ -21,7 +21,7 @@ interface IFlashMinter { } abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveSDolaLPHelperDynamic helper; @@ -34,7 +34,7 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -68,11 +68,11 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256 sDolaIn = sDOLA.convertToShares(maxBorrowAmount); uint256[] memory amounts = new uint256[](2); @@ -105,7 +105,7 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount, userPk, abi.encode(address(market), 0) @@ -118,7 +118,7 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { // Calculate the amount of DOLA needed to borrow to buy the DBR needed to cover for the borrowing period (uint256 dolaForDBR, uint256 dbrAmount) = ale - .approximateDolaAndDbrNeeded(maxBorrowAmount, 15 days, 8); + .approximateDolaAndDbrNeeded(maxBorrowAmount, 10 days, 8); // Sign Message for borrow on behalf bytes32 hash = keccak256( @@ -141,11 +141,11 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 90) / 100, 0 @@ -183,7 +183,7 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount - initialDolaDeposit); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaDeposit, userPk, abi.encode(address(market), 0) @@ -216,11 +216,11 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[1] = sDOLA.convertToShares( @@ -257,11 +257,12 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { uint256 initialDolaForLP = amount / 10; vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - uint256 initialLpAmount = helper.transformToCollateral( + uint256 initialLpAmount = helper.convertToCollateral( + address(0), initialDolaForLP, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaForLP, userPk, abi.encode(address(market), 0) @@ -294,11 +295,11 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[1] = sDOLA.convertToShares(maxBorrowAmount); @@ -356,17 +357,17 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -413,11 +414,11 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -427,8 +428,8 @@ abstract contract ALEBaseSDolaLPDynTest is MarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEBaseSDolaLPDynYearnV2.sol b/test/util/aleTests/ALEBaseSDolaLPDynYearnV2.sol index e6ae68cc..ad2c0ae2 100644 --- a/test/util/aleTests/ALEBaseSDolaLPDynYearnV2.sol +++ b/test/util/aleTests/ALEBaseSDolaLPDynYearnV2.sol @@ -4,13 +4,12 @@ import {ICurvePool} from "src/interfaces/ICurvePool.sol"; import {CurveSDolaLPHelperDynamic} from "src/util/CurveSDolaLPHelperDynamic.sol"; import "test/marketForkTests/MarketForkTest.sol"; import {console} from "forge-std/console.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { - ALE ale; + ALEV2 ale; address userPk = vm.addr(1); CurveSDolaLPHelperDynamic helper; address userPkEscrow; @@ -23,7 +22,7 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 1000000 ether, userPk, abi.encode(address(market), 0) @@ -56,11 +55,11 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[1] = sDOLA.convertToShares(maxBorrowAmount); @@ -92,7 +91,7 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount, userPk, abi.encode(address(market), 0) @@ -105,7 +104,7 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { // Calculate the amount of DOLA needed to borrow to buy the DBR needed to cover for the borrowing period (uint256 dolaForDBR, uint256 dbrAmount) = ale - .approximateDolaAndDbrNeeded(maxBorrowAmount, 15 days, 8); + .approximateDolaAndDbrNeeded(maxBorrowAmount, 10 days, 8); // Sign Message for borrow on behalf bytes32 hash = keccak256( @@ -128,11 +127,11 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 90) / 100, 0 @@ -169,7 +168,7 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount - initialDolaDeposit); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaDeposit, userPk, abi.encode(address(market), 0) @@ -202,11 +201,11 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[1] = sDOLA.convertToShares( @@ -243,11 +242,12 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { uint256 initialDolaForShares = amount / 10; vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - uint256 initialSharesAmount = helper.transformToCollateral( + uint256 initialSharesAmount = helper.convertToCollateral( + address(0), initialDolaForShares, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaForShares, userPk, abi.encode(address(market), 0) @@ -280,11 +280,11 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[] memory amounts = new uint256[](2); amounts[1] = sDOLA.convertToShares(maxBorrowAmount); @@ -347,17 +347,17 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -381,7 +381,7 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000 ether); - uint256 yearnLeftover = helper.transformToCollateral( + uint256 yearnLeftover = helper.convertToCollateral( 1000 ether, address(helper), abi.encode(address(market), 0) @@ -421,17 +421,17 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -483,11 +483,11 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -497,8 +497,8 @@ abstract contract ALEBaseSDolaLPDynYearnV2Test is MarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEDolaCrvUSD.t.sol b/test/util/aleTests/ALEDolaCrvUSD.t.sol index 3c49494c..ce413ae7 100644 --- a/test/util/aleTests/ALEDolaCrvUSD.t.sol +++ b/test/util/aleTests/ALEDolaCrvUSD.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelper} from "src/util/CurveDolaLPHelper.sol"; import "test/marketForkTests/CrvUSDDolaConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; interface IFlashMinter { function setMaxFlashLimit(uint256 limit) external; @@ -17,7 +17,7 @@ interface IFlashMinter { } contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelper helper; @@ -33,12 +33,9 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); - ale = new ALE(address(0), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); - flash = IFlashMinter(address(ale.flash())); - flash.setMaxFlashLimit(100000 ether); - DOLA.addMinter(address(flash)); borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); @@ -50,7 +47,7 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -84,11 +81,11 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -117,7 +114,7 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -153,11 +150,11 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 97) / 100, 0 @@ -192,7 +189,7 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -225,11 +222,11 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount + initialDolaDeposit, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -261,11 +258,12 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 11000 ether); - uint256 initialLpAmount = helper.transformToCollateral( + uint256 initialLpAmount = helper.convertToCollateral( + address(0), 1000 ether, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -298,11 +296,11 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -358,17 +356,17 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -415,11 +413,11 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -429,8 +427,8 @@ contract ALEDolaCrvUSDTest is CrvUSDDolaConvexMarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEDolaCrvUSDYearnV2.t.sol b/test/util/aleTests/ALEDolaCrvUSDYearnV2.t.sol index ad446d08..8f837404 100644 --- a/test/util/aleTests/ALEDolaCrvUSDYearnV2.t.sol +++ b/test/util/aleTests/ALEDolaCrvUSDYearnV2.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelper} from "src/util/CurveDolaLPHelper.sol"; import "test/marketForkTests/CrvUSDDolaYearnV2MarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; interface IFlashMinter { @@ -18,7 +18,7 @@ interface IFlashMinter { } contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelper helper; @@ -33,12 +33,9 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(dolaCrvUSD), 0, 2, yearn); - ale = new ALE(address(0), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); - flash = IFlashMinter(address(ale.flash())); - flash.setMaxFlashLimit(1000000 ether); - DOLA.addMinter(address(flash)); borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); @@ -50,7 +47,7 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 1000000 ether, userPk, abi.encode(address(market), 0) @@ -83,11 +80,11 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = dolaCrvUSD.calc_token_amount(amounts, true); @@ -116,7 +113,7 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -152,11 +149,11 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 98) / 100, 0 @@ -191,7 +188,7 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -224,11 +221,11 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount + initialDolaDeposit, 0]; uint256 lpAmountAdded = dolaCrvUSD.calc_token_amount(amounts, true); @@ -260,11 +257,12 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 11000 ether); - uint256 initialSharesAmount = helper.transformToCollateral( + uint256 initialSharesAmount = helper.convertToCollateral( + address(0), 1000 ether, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -297,11 +295,11 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = dolaCrvUSD.calc_token_amount(amounts, true); @@ -362,17 +360,17 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -397,7 +395,7 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000 ether); - uint256 yearnLeftover = helper.transformToCollateral( + uint256 yearnLeftover = helper.convertToCollateral( 1000 ether, address(helper), abi.encode(address(market), 0) @@ -436,17 +434,17 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -497,11 +495,11 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -511,8 +509,8 @@ contract ALEDolaCrvUSDYearnV2Test is CrvUSDDolaYearnV2MarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEDolaDeUSD.t.sol b/test/util/aleTests/ALEDolaDeUSD.t.sol new file mode 100644 index 00000000..8e9909d2 --- /dev/null +++ b/test/util/aleTests/ALEDolaDeUSD.t.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.8.13; + +import {ICurvePool} from "src/interfaces/ICurvePool.sol"; +import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; +import "test/marketForkTests/DolaDeUSDConvexMarketForkTest.t.sol"; +import {console} from "forge-std/console.sol"; +import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; +import {ALEBaseDolaLPDynTest, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDyn.sol"; + +contract ALEDolaDeUSDTest is + ALEBaseDolaLPDynTest, + DolaDeUSDConvexMarketForkTest +{ + function setUp() public override { + super.setUp(); + curvePool = dolaDeUSD; + + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); + + vm.startPrank(gov); + DOLA.mint(address(this), 100000 ether); + helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); + ale = ALEV2(payable(aleV2Addr)); + ale.setMarket(address(market), address(DOLA), address(helper), false); + + borrowController.allow(address(ale)); + vm.stopPrank(); + userPkEscrow = address(market.predictEscrow(userPk)); + } +} diff --git a/test/util/aleTests/ALEDolaDeUSDYearnV2.t.sol b/test/util/aleTests/ALEDolaDeUSDYearnV2.t.sol new file mode 100644 index 00000000..858405a8 --- /dev/null +++ b/test/util/aleTests/ALEDolaDeUSDYearnV2.t.sol @@ -0,0 +1,33 @@ +pragma solidity ^0.8.13; + +import {ICurvePool} from "src/interfaces/ICurvePool.sol"; +import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; +import "test/marketForkTests/DolaDeUSDYearnV2MarketForkTest.t.sol"; +import {console} from "forge-std/console.sol"; +import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; +import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; +import {ALEBaseDolaLPDynYearnV2Test, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol"; + +contract ALEDolaDeUSDeYearnV2Test is + ALEBaseDolaLPDynYearnV2Test, + DolaDeUSDYearnV2MarketForkTest +{ + function setUp() public override { + super.setUp(); + + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); + + curvePool = dolaDeUSD; + vault = IYearnVaultV2(yearn); + vm.startPrank(gov); + DOLA.mint(address(this), 100000 ether); + helper.setMarket(address(market), address(curvePool), 0, 2, yearn); + ale = ALEV2(payable(aleV2Addr)); + ale.setMarket(address(market), address(DOLA), address(helper), false); + + borrowController.allow(address(ale)); + vm.stopPrank(); + userPkEscrow = address(market.predictEscrow(userPk)); + } +} diff --git a/test/util/aleTests/ALEDolaFraxBP.t.sol b/test/util/aleTests/ALEDolaFraxBP.t.sol index 119881c9..b4ef593d 100644 --- a/test/util/aleTests/ALEDolaFraxBP.t.sol +++ b/test/util/aleTests/ALEDolaFraxBP.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelper} from "src/util/CurveDolaLPHelper.sol"; import "test/marketForkTests/DolaFraxBPConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; interface IFlashMinter { function setMaxFlashLimit(uint256 _maxFlashLimit) external; @@ -17,7 +17,7 @@ interface IFlashMinter { } contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelper helper; @@ -28,12 +28,12 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { super.setUp(); curvePool = dolaFraxBP; - helper = CurveDolaLPHelper(curveDolaLPHelperAddr); + helper = new CurveDolaLPHelper(gov, pauseGuardian, address(DOLA)); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); - ale = new ALE(address(0), triDBRAddr); + ale = new ALEV2(triDBRAddr); ale.setMarket(address(market), address(DOLA), address(helper), false); flash = IFlashMinter(address(ale.flash())); @@ -50,7 +50,7 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000000 ether, userPk, abi.encode(address(market), 0) @@ -83,11 +83,11 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -119,7 +119,7 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount, userPk, abi.encode(address(market), 0) @@ -155,11 +155,11 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 95) / 100, 0 @@ -197,7 +197,7 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount - initialDolaDeposit); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaDeposit, userPk, abi.encode(address(market), 0) @@ -230,11 +230,11 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount + initialDolaDeposit, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -270,11 +270,12 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - uint256 initialLpAmount = helper.transformToCollateral( + uint256 initialLpAmount = helper.convertToCollateral( + address(0), initialDolaDeposit, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaDeposit, userPk, abi.encode(address(market), 0) @@ -307,11 +308,11 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -370,17 +371,17 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -433,11 +434,11 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -447,8 +448,8 @@ contract ALEDolaFraxBPTest is DolaFraxBPConvexMarketForkTest { ale.deleveragePosition( amountToRepay, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEDolaFraxBPYearnV2.t.sol b/test/util/aleTests/ALEDolaFraxBPYearnV2.t.sol index 2e20ff53..21d2e20b 100644 --- a/test/util/aleTests/ALEDolaFraxBPYearnV2.t.sol +++ b/test/util/aleTests/ALEDolaFraxBPYearnV2.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelper} from "src/util/CurveDolaLPHelper.sol"; import "test/marketForkTests/DolaFraxBPYearnV2MarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; interface IFlashMinter { @@ -22,7 +22,7 @@ interface ISimpleERC20Escrow { } contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelper helper; @@ -35,7 +35,7 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { curvePool = dolaFraxBP; - helper = CurveDolaLPHelper(curveDolaLPHelperAddr); + helper = new CurveDolaLPHelper(gov, pauseGuardian, address(DOLA)); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); @@ -47,7 +47,7 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { address(yearn) ); - ale = new ALE(address(0), triDBRAddr); + ale = new ALEV2(triDBRAddr); ale.setMarket(address(market), address(DOLA), address(helper), false); flash = IFlashMinter(address(ale.flash())); @@ -64,7 +64,7 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000000 ether, userPk, abi.encode(address(market), 0) @@ -97,11 +97,11 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -142,7 +142,7 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount, userPk, abi.encode(address(market), 0) @@ -178,11 +178,11 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 95) / 100, 0 @@ -230,7 +230,7 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount - initialDolaDeposit); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaDeposit, userPk, abi.encode(address(market), 0) @@ -263,11 +263,11 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount + initialDolaDeposit, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -311,11 +311,12 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { uint256 initialDolaAmount = amount / 10; vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - uint256 initialSharesAmount = helper.transformToCollateral( + uint256 initialSharesAmount = helper.convertToCollateral( + address(0), initialDolaAmount, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaAmount, userPk, abi.encode(address(market), 0) @@ -348,11 +349,11 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -420,17 +421,17 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -462,7 +463,7 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000 ether); - uint256 yearnLeftover = helper.transformToCollateral( + uint256 yearnLeftover = helper.convertToCollateral( 1000 ether, address(helper), abi.encode(address(market), 0) @@ -501,17 +502,17 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -562,11 +563,11 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -576,8 +577,8 @@ contract ALEDolaFraxBPYearnV2Test is DolaFraxBPYearnV2MarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEDolaFraxPyUSD.t.sol b/test/util/aleTests/ALEDolaFraxPyUSD.t.sol index f1569630..e30638c9 100644 --- a/test/util/aleTests/ALEDolaFraxPyUSD.t.sol +++ b/test/util/aleTests/ALEDolaFraxPyUSD.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelper} from "src/util/CurveDolaLPHelper.sol"; import "test/marketForkTests/DolaFraxPyUSDConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; interface IFlashMinter { function setMaxFlashLimit(uint256 _maxFlashLimit) external; @@ -17,7 +17,7 @@ interface IFlashMinter { } contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelper helper; @@ -28,12 +28,12 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { super.setUp(); curvePool = dolaFraxPyUSD; - helper = CurveDolaLPHelper(curveDolaLPHelperAddr); + helper = new CurveDolaLPHelper(gov, pauseGuardian, address(DOLA)); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); - ale = new ALE(address(0), triDBRAddr); + ale = new ALEV2(triDBRAddr); ale.setMarket(address(market), address(DOLA), address(helper), false); flash = IFlashMinter(address(ale.flash())); @@ -50,7 +50,7 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -83,11 +83,11 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -116,7 +116,7 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -152,11 +152,11 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 97) / 100, 0 @@ -191,7 +191,7 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 10000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -224,11 +224,11 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount + initialDolaDeposit, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -260,11 +260,12 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 11000 ether); - uint256 initialLpAmount = helper.transformToCollateral( + uint256 initialLpAmount = helper.convertToCollateral( + address(0), 1000 ether, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 10000 ether, userPk, abi.encode(address(market), 0) @@ -297,11 +298,11 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = curvePool.calc_token_amount(amounts, true); @@ -357,17 +358,17 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -414,11 +415,11 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -428,8 +429,8 @@ contract ALEDolaFraxPyUSDTest is DolaFraxPyUSDConvexMarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEDolaFraxPyUSDYearnV2.t.sol b/test/util/aleTests/ALEDolaFraxPyUSDYearnV2.t.sol index 090199b7..dc1a251a 100644 --- a/test/util/aleTests/ALEDolaFraxPyUSDYearnV2.t.sol +++ b/test/util/aleTests/ALEDolaFraxPyUSDYearnV2.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelper} from "src/util/CurveDolaLPHelper.sol"; import "test/marketForkTests/DolaFraxPyUSDYearnV2MarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; interface IFlashMinter { @@ -18,7 +18,7 @@ interface IFlashMinter { } contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { - ALE ale; + ALEV2 ale; IFlashMinter flash; address userPk = vm.addr(1); CurveDolaLPHelper helper; @@ -28,7 +28,7 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { function setUp() public override { super.setUp(); - helper = CurveDolaLPHelper(curveDolaLPHelperAddr); + helper = new CurveDolaLPHelper(gov, pauseGuardian, address(DOLA)); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); @@ -39,7 +39,7 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { 2, address(yearn) ); - ale = new ALE(address(0), triDBRAddr); + ale = new ALEV2(triDBRAddr); ale.setMarket(address(market), address(DOLA), address(helper), false); flash = IFlashMinter(address(ale.flash())); @@ -56,7 +56,7 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000000 ether); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( 1000000 ether, userPk, abi.encode(address(market), 0) @@ -89,11 +89,11 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = dolaFraxPyUSD.calc_token_amount(amounts, true); @@ -124,7 +124,7 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount, userPk, abi.encode(address(market), 0) @@ -160,11 +160,11 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 95) / 100, 0 @@ -201,7 +201,7 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount - initialDolaDeposit); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaDeposit, userPk, abi.encode(address(market), 0) @@ -234,11 +234,11 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount + initialDolaDeposit, 0]; uint256 lpAmountAdded = dolaFraxPyUSD.calc_token_amount(amounts, true); @@ -272,11 +272,12 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { uint256 initialDolaAmount = amount / 10; vm.startPrank(userPk, userPk); DOLA.approve(address(helper), amount); - uint256 initialSharesAmount = helper.transformToCollateral( + uint256 initialSharesAmount = helper.convertToCollateral( + address(0), initialDolaAmount, abi.encode(address(market), 0) ); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( amount - initialDolaAmount, userPk, abi.encode(address(market), 0) @@ -309,11 +310,11 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData; - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; uint256[2] memory amounts = [maxBorrowAmount, 0]; uint256 lpAmountAdded = dolaFraxPyUSD.calc_token_amount(amounts, true); @@ -374,17 +375,17 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -416,7 +417,7 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { vm.startPrank(userPk, userPk); DOLA.approve(address(helper), 1000 ether); - uint256 yearnLeftover = helper.transformToCollateral( + uint256 yearnLeftover = helper.convertToCollateral( 1000 ether, address(helper), abi.encode(address(market), 0) @@ -455,17 +456,17 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; bytes memory swapData; vm.prank(userPk); ale.deleveragePosition( dolaRedeemed / 2, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), @@ -516,11 +517,11 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR bytes memory swapData; @@ -530,8 +531,8 @@ contract ALEDolaFraxPyUSDYearnV2Test is DolaFraxPyUSDYearnV2MarketForkTest { ale.deleveragePosition( debt, address(market), - amountToWithdraw, address(0), + amountToWithdraw, swapData, permit, abi.encode(address(market), uint(0)), diff --git a/test/util/aleTests/ALEDolaUSR.t.sol b/test/util/aleTests/ALEDolaUSR.t.sol new file mode 100644 index 00000000..fe2f548a --- /dev/null +++ b/test/util/aleTests/ALEDolaUSR.t.sol @@ -0,0 +1,31 @@ +pragma solidity ^0.8.13; + +import {ICurvePool} from "src/interfaces/ICurvePool.sol"; +import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; +import "test/marketForkTests/DolaUSRConvexMarketForkTest.t.sol"; +import {console} from "forge-std/console.sol"; +import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; +import {ALEBaseDolaLPDynTest, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDyn.sol"; + +contract ALEDolaUSRTest is + ALEBaseDolaLPDynTest, + DolaUSRConvexMarketForkTest +{ + function setUp() public override { + super.setUp(); + curvePool = dolaUSR; + + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); + + vm.startPrank(gov); + DOLA.mint(address(this), 100000 ether); + helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); + ale = ALEV2(payable(aleV2Addr)); + ale.setMarket(address(market), address(DOLA), address(helper), false); + + borrowController.allow(address(ale)); + vm.stopPrank(); + userPkEscrow = address(market.predictEscrow(userPk)); + } +} diff --git a/test/util/aleTests/ALEDolaUSRYearnV2.t.sol b/test/util/aleTests/ALEDolaUSRYearnV2.t.sol new file mode 100644 index 00000000..5c162d56 --- /dev/null +++ b/test/util/aleTests/ALEDolaUSRYearnV2.t.sol @@ -0,0 +1,32 @@ +pragma solidity ^0.8.13; + +import {ICurvePool} from "src/interfaces/ICurvePool.sol"; +import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; +import "test/marketForkTests/DolaUSRYearnV2MarketForkTest.t.sol"; +import {console} from "forge-std/console.sol"; +import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; +import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; +import {ALEBaseDolaLPDynYearnV2Test, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol"; + +contract ALEDolaUSRYearnV2Test is + ALEBaseDolaLPDynYearnV2Test, + DolaUSRYearnV2MarketForkTest +{ + function setUp() public override { + super.setUp(); + + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); + curvePool = dolaUSR; + vault = IYearnVaultV2(yearn); + vm.startPrank(gov); + DOLA.mint(address(this), 100000 ether); + helper.setMarket(address(market), address(curvePool), 0, 2, yearn); + ale = ALEV2(payable(aleV2Addr)); + ale.setMarket(address(market), address(DOLA), address(helper), false); + + borrowController.allow(address(ale)); + vm.stopPrank(); + userPkEscrow = address(market.predictEscrow(userPk)); + } +} diff --git a/test/util/aleTests/ALEDolasUSDe.t.sol b/test/util/aleTests/ALEDolasUSDe.t.sol index 16e250dc..f1a51ccc 100644 --- a/test/util/aleTests/ALEDolasUSDe.t.sol +++ b/test/util/aleTests/ALEDolasUSDe.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/DolasUSDeConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {ALEBaseDolaLPDynTest, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDyn.sol"; contract ALEDolasUSDeTest is @@ -16,21 +16,14 @@ contract ALEDolasUSDeTest is super.setUp(); curvePool = dolasUSDe; - helper = new CurveDolaLPHelperDynamic( - gov, - pauseGuardian, - address(DOLA) - ); + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); - ale = new ALE(address(0), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); - flash = IFlashMinter(address(ale.flash())); - flash.setMaxFlashLimit(100000 ether); - DOLA.addMinter(address(flash)); borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALEDolasUSDeYearnV2.t.sol b/test/util/aleTests/ALEDolasUSDeYearnV2.t.sol index bbae9fb3..f42ddaa5 100644 --- a/test/util/aleTests/ALEDolasUSDeYearnV2.t.sol +++ b/test/util/aleTests/ALEDolasUSDeYearnV2.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/DolasUSDeYearnV2MarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; import {ALEBaseDolaLPDynYearnV2Test, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol"; @@ -16,22 +16,16 @@ contract ALEDolasUSDeYearnV2Test is function setUp() public override { super.setUp(); - helper = new CurveDolaLPHelperDynamic( - gov, - pauseGuardian, - address(DOLA) - ); + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); + curvePool = dolasUSDe; vault = IYearnVaultV2(yearn); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, yearn); - ale = new ALE(address(0), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); - flash = IFlashMinter(address(ale.flash())); - flash.setMaxFlashLimit(1000000 ether); - DOLA.addMinter(address(flash)); borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALEDolasUSDs.t.sol b/test/util/aleTests/ALEDolasUSDs.t.sol index f394c456..a41b00d1 100644 --- a/test/util/aleTests/ALEDolasUSDs.t.sol +++ b/test/util/aleTests/ALEDolasUSDs.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/DolasUSDsConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {ALEBaseDolaLPDynTest, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDyn.sol"; contract ALEDolasUSDsTest is @@ -16,21 +16,14 @@ contract ALEDolasUSDsTest is super.setUp(); curvePool = dolasUSDs; - helper = new CurveDolaLPHelperDynamic( - gov, - pauseGuardian, - address(DOLA) - ); + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); - ale = new ALE(address(0), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); - flash = IFlashMinter(address(ale.flash())); - flash.setMaxFlashLimit(100000 ether); - DOLA.addMinter(address(flash)); borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALEDolasUSDsYearnV2.t.sol b/test/util/aleTests/ALEDolasUSDsYearnV2.t.sol index caf66569..9ce32966 100644 --- a/test/util/aleTests/ALEDolasUSDsYearnV2.t.sol +++ b/test/util/aleTests/ALEDolasUSDsYearnV2.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/DolasUSDsYearnV2MarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; import {ALEBaseDolaLPDynYearnV2Test, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol"; @@ -16,21 +16,14 @@ contract ALEDolasUSDsYearnV2Test is function setUp() public override { super.setUp(); curvePool = dolasUSDs; - helper = new CurveDolaLPHelperDynamic( - gov, - pauseGuardian, - address(DOLA) - ); + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); vault = IYearnVaultV2(yearn); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, yearn); - ale = new ALE(address(0), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); - flash = IFlashMinter(address(ale.flash())); - flash.setMaxFlashLimit(1000000 ether); - DOLA.addMinter(address(flash)); borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALEDolascrvUSD.t.sol b/test/util/aleTests/ALEDolascrvUSD.t.sol index 839f2bf6..0119f898 100644 --- a/test/util/aleTests/ALEDolascrvUSD.t.sol +++ b/test/util/aleTests/ALEDolascrvUSD.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/DolascrvUSDConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {ALEBaseDolaLPDynTest, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDyn.sol"; contract ALEDolascrvUSDTest is @@ -21,8 +21,9 @@ contract ALEDolascrvUSDTest is vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, address(0)); - ale = ALE(payable(aleAddr)); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); + borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALEDolascrvUSDYearnV2.t.sol b/test/util/aleTests/ALEDolascrvUSDYearnV2.t.sol index 1c30cc42..2f3b3ded 100644 --- a/test/util/aleTests/ALEDolascrvUSDYearnV2.t.sol +++ b/test/util/aleTests/ALEDolascrvUSDYearnV2.t.sol @@ -5,7 +5,7 @@ import {CurveDolaLPHelperDynamic} from "src/util/CurveDolaLPHelperDynamic.sol"; import "test/marketForkTests/DolascrvUSDYearnV2MarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; import {ALEBaseDolaLPDynYearnV2Test, IFlashMinter} from "test/util/aleTests/ALEBaseDolaLPDynYearnV2.sol"; @@ -16,14 +16,17 @@ contract ALEDolascrvUSDYearnV2Test is function setUp() public override { super.setUp(); curvePool = ICurvePool(dolascrvUSD); + helper = CurveDolaLPHelperDynamic(curveDolaLPHelperDynamicAddr); + vault = IYearnVaultV2(yearn); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 0, 2, yearn); - ale = ALE(payable(aleAddr)); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); + borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALEPendlePTUSDe.t.sol b/test/util/aleTests/ALEPendlePTUSDe.t.sol new file mode 100644 index 00000000..26cd95d7 --- /dev/null +++ b/test/util/aleTests/ALEPendlePTUSDe.t.sol @@ -0,0 +1,1043 @@ +pragma solidity ^0.8.13; + +import {ICurvePool} from "src/interfaces/ICurvePool.sol"; +import {PendlePTHelper, IPendleHelper} from "src/util/PendlePTHelper.sol"; +import "test/marketForkTests/PendlePTsUSDe27Mar25MarketForkTest.t.sol"; +import {console} from "forge-std/console.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; +import {SimpleERC20Escrow} from "src/escrows/SimpleERC20Escrow.sol"; + +/* + User 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf + ALE 0x9123ef9b7dB2e3D968B01C6FF99839aCb242dA13 + helper 0xcc39FC2eD370a4373FF4B8eBd507cd26906A80E8 + DOLA 0x865377367054516e17014CcdED1e7d814EDC9ce4 + address pendlePT = 0xE00bd3Df25fb187d6ABBB620b3dfd19839947b81 +*/ +contract ALEV2PTUSDeTest is PendlePTsUSDe27Mar25MarketForkTest { + ALEV2 ale; + address userPk = vm.addr(1); + PendlePTHelper helper; + address userPkEscrow; + + address pendleRouter = address(0x888888888889758F76e7103c6CbF23ABbF58F946); + address pendleYT = address(0x96512230bF0Fa4E20Cf02C3e8A7d983132cd2b9F); + address marketPT = address(0xcDd26Eb5EB2Ce0f203a84553853667aE69Ca29Ce); + + // Pendle Router Mock + address pendleYTHolder = + address(0x9844c3688dAaA98De18fBe52499A6B152236896b); + + // Get initial PT + // Swap 100K DOLA for PT (recipient helper) + bytes pendleDataInitial = + hex"c81f847a000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e8000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce0000000000000000000000000000000000000000000015bbb7d43d50adbe774d000000000000000000000000000000000000000000000af9f5689b16a553440300000000000000000000000000000000000000000000170ce9c2127c5b2edb9f0000000000000000000000000000000000000000000015f3ead1362d4aa68806000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000380000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000152d02c7e14af68000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cf5540fffcdc3d510b18bfca6d2b9987b07725590000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f083bd37f90001865377367054516e17014ccded1e7d814edc9ce400019d39a5de30e57443bff2a8307a4256c8797a34970a152d02c7e14af68000000a1258ca4b9af3b300000000c49b0001b28ca7e465c452ce4252598e0bc96aeba553cf820001744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0001888888888889758f76e7103c6cbf23abbf58f94635d39ebf03010203000a01010001020001ff00000000000000000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + // Swap maxBorrowAmount from 100k DOLA to PT deposit (recipient helper) + bytes pendleSwapData = + hex"c81f847a000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e8000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce00000000000000000000000000000000000000000000139a329b323cc73797320000000000000000000000000000000000000000000009e6717a576edb9070900000000000000000000000000000000000000000000014ca54b41e0266af52c90000000000000000000000000000000000000000000013cce2f4aeddb720e121000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000131953f892f952ddaea90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000131953f892f952ddaea9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000001159fb5d7cb8462000000000000108c343c74052cc41998000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000131953f892f952ddaea9000000000000000000000000000000000000000000001061d78dc4c7aee0dbe5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000131953f892f952ddaea900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002317b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238393334352e3131323431363431323732222c22416d6f756e744f7574555344223a2238393232312e32323435333932373334222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223738313434313731393037313837383130323435303136222c2254696d657374616d70223a313733363236303632312c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224d39474c677835524a3863396c7a6d4e44585571304159354a7157416769364234696269346c306f4f514f317a586248777952743945533748313250514b71734837566b66484f493850756e63647a6c535651426a654e745a2b752f62754e352f4775303038497a334141686b543950312b342f6b2b394678542f534d503350614f3642444c344678664b6273326c5171336c58664473716a7a4f326a426475784277415557624c5734756c777a4e654352587a304c494865676c6c50756a5359536b554e365745724e3056425043346c78565754492b3856574d70507971446e4e6a4666513043414c61504c6562437a413250576276756d5358465062627a734f534d78624f42756a7038756f6d7a594877585279574453472f3867466c6a7047524c756e5671736e474a34647861344f372b44544a3245437134457a57736a613837417a354576563868396779487a6976466b513d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + address aleAddress = 0x5233f4C2515ae21B540c438862Abb5603506dEBC; + address userPkAddr = 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf; + address helperAddr = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; + uint256 initialDolaAmount = 100000 ether; // 100K DOLA + + function setUp() public override { + super.setUp(); + + vm.startPrank(gov); + + ale = new ALEV2(triDBRAddr); + + helper = new PendlePTHelper( + gov, + pauseGuardian, + address(DOLA), + address(pendleRouter), + address(ale) + ); + ale.setMarket(address(market), address(DOLA), address(helper), false); + helper.setMarket(address(market), address(pendlePT), pendleYT); + borrowController.allow(address(ale)); + DOLA.mint(address(market), 1000000 ether); + vm.stopPrank(); + + userPkEscrow = address(market.predictEscrow(userPk)); + + vm.prank(userPk); + DOLA.approve(address(helper), initialDolaAmount); + } + + function _mintInitialDolaAmountToUser() internal { + vm.prank(gov); + DOLA.mint(userPk, initialDolaAmount); + } + function test_leveragePosition_Mint_PT_and_YT_with_DOLA_router() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + gibDBR(userPk, 20000 ether); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + // Mint PT and YT with maxBorrowAmount (swap recipient is the helper, then distribute PT to ALE and YT to userPk) + bytes + memory pendleMintData = hex"d0f42385000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e800000000000000000000000096512230bf0fa4e20cf02c3e8a7d983132cd2b9f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000131953f892f952ddaea90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000131953f892f952ddaea900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000115a0ce892f03fa000000000000108c44f77cc1a67bcd16000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000131953f892f952ddaea9000000000000000000000000000000000000000000000d3d03f9309aeb963dab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000131953f892f952ddaea900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002327b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238393333342e3633343132373134353838222c22416d6f756e744f7574555344223a2238393536302e3431363532383733343938222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223738313435333737343734313138363530393430363934222c2254696d657374616d70223a313733363236313338322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22464f70686550495230465553777469393367654f72525a74427a616f674f3649684368615a476f44654c4f6f6d6d536c774a65307342627555717a63612f78634e2f4b784374697a4a79386949316777325a2b34444469384c52746e71744e476a495230635468546f4539495469594d37336b5843446f38683566654d6147695656686c2f4156356e772f2f354363595046577552765668797538524d65587279413778774e34774949636376567757616555416245434d7941496d48457874577362747849437061486c35562f39624a616c326552486b414b5173654547744c4e764d6842576e7a654a3047656958426e76352b664974574d4f6750764e6d56464d4779665276343054714735544252714c43794a674d61463654537173566d6d5646674165567a43426c634c4b6e43476e56706831784d326c736464574c78536342495a39616764696a5745536e6654386235513d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.prank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), 0, pendleMintData), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + ((maxBorrowAmount * 1 ether) / 1.0081 ether), // 1.0081 ratio DOLA/mint PT and YT + 1e14 // 0.01% Delta + ); + assertApproxEqRel( + IERC20(pendleYT).balanceOf(userPk), + ((maxBorrowAmount * 1 ether) / 1.0081 ether), // 1.0081 ratio DOLA/mint PT and YT + 1e14 // 0.01% Delta + ); + } + + function test_leveragePosition_Swap_DOLA_for_PT_router() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), initialDolaAmount, pendleDataInitial) + ); + gibDBR(userPk, 20000 ether); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + vm.prank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), 0, pendleSwapData), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + ((maxBorrowAmount * 1.0365 ether) / 1 ether), // approx 1.0365 ratio PT/DOLA + 1e14 // 0.01% Delta + ); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_leveragePosition_buyDBR_Swap_DOLA_for_PT() public { + _mintInitialDolaAmountToUser(); + + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), initialDolaAmount); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + vm.stopPrank(); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Calculate the amount of DOLA needed to borrow to buy the DBR needed to cover for the borrowing period + (uint256 dolaForDBR, uint256 dbrAmount) = ale + .approximateDolaAndDbrNeeded(maxBorrowAmount, 15 days, 8); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount + dolaForDBR, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( + dolaForDBR, + (dbrAmount * 95) / 100, + 0 + ); + + vm.prank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), 0, pendleSwapData), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + ((maxBorrowAmount * 1.0365 ether) / 1 ether), // approx 1.0365 ratio PT/DOLA + 1e14 // 0.01% Delta + ); + assertEq(DOLA.balanceOf(address(ale)), 0); + assertGt(dbr.balanceOf(userPk), (dbrAmount * 95) / 100); + } + + function test_depositAndLeveragePosition_DOLA() public { + _mintInitialDolaAmountToUser(); + + uint256 initialDolaDeposit = initialDolaAmount / 10; + // Swap DOLA for PT (initialDolaAmount - initialDolaDeposit) (receiver is the helper) + bytes + memory pendleInitialDepositData = hex"c81f847a000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e8000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce00000000000000000000000000000000000000000000138eda3d96845d63d2380000000000000000000000000000000000000000000009e0b6a065e09699d95d0000000000000000000000000000000000000000000014be4c50d5f13c4315430000000000000000000000000000000000000000000013c16d40cbc12d33b2ba000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000130ee8e71790444000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000130ee8e7179044400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000001150943d0f8806600000000000010833c9f0b63d53eaf22000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000130ee8e7179044400000000000000000000000000000000000000000000000001058f6e52250ea27046b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000130ee8e717904440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002327b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238393433332e3230373638373333383937222c22416d6f756e744f7574555344223a2238383835362e3232373332303239303339222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223737393738373535343232323537323534363734323130222c2254696d657374616d70223a313733363236313937372c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224c4e79374f644e562b44456532484d376c7666596569413364384136326632363674506e55444263794e3549457542454941576a66326358533866573547704c767054336a6a74327552785039426e787939374b326a416c724b5438775338492b6f446f2f67347a4c58776441412b7355336d38684c383066475138726a303766324473565244627554363949575473626570546b4c736d696f666f792f513253415156762f6e685570347856364e62684d2b616e7731537a68326654492f737769344e527953303858636772444f50452b48713145776e757442673333743663716e3431446c7572684438597a7579754e7265433776314e7252395835377a4e6d76646c312b5450765a534c57616475523958426e78736a6e654a754f426f2b64344979646b4d4444317a75324c694b5278584158764836316a6d326d7353426a32786f432b72506c6859793831384364347156413d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), initialDolaAmount - initialDolaDeposit); + helper.convertToCollateralAndDeposit( + initialDolaAmount - initialDolaDeposit, + userPk, + abi.encode(address(market), 0, pendleInitialDepositData) + ); + assertEq(DOLA.balanceOf(address(helper)), 0); + vm.stopPrank(); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 20000 ether); + + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + // Swap DOLA for PT (maxBorrowAmount + initialDolaDeposit) (receiver is the helper) + bytes + memory pendleSwapDataDeposit = hex"c81f847a000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e8000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce0000000000000000000000000000000000000000000013d06077e362288d7229000000000000000000000000000000000000000000000a01ce75704117112262000000000000000000000000000000000000000000001503cb29d2224a0a61ce0000000000000000000000000000000000000000000014039ceae0822e2244c5000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000134ed0d8997aa99551a70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000134ed0d8997aa99551a700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000118aa0f8f492b7b00000000000010ba98ebec1e3bbabc78000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000134ed0d8997aa99551a700000000000000000000000000000000000000000000108fc578ec5121883a95000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000134ed0d8997aa99551a700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002327b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2239303630342e3634313736303439323534222c22416d6f756e744f7574555344223a2239303031392e3930323233393335373938222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223738393939393737323834313532313837333337383438222c2254696d657374616d70223a313733363236323035322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22514f58653035617433644e353470337558563842326c655638504a63754f7632626862536d612f47544e46376e362f71677273567a5a4f7a503532724c49786d3162535848744c6533527a6c2f4d48564d5776444a4172725a6c754b746c4379723162755535522b4766776a4b6d597365567a427739326a503566654f75443272694d78346f734c6154706d3669533446544e646441342b78302b474a692f6f4d737049576e344442734f526451452b613435737950395874674f6f6f364e79426a732b63484d636853744d79592b37374b374671625a425a6f4a2f726733524631486b6d2b73715468324e714d776c4944723039415531457a4f72546d6b4542647a4c4d7051692f694a304573686c4d5763773958353833516645436553416d63593850534d567039673946666f33346553534d582b413043442f6c3837442b4a75664b376d432b755a3548446266597243475a413d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + vm.startPrank(userPk); + DOLA.approve(address(ale), initialDolaDeposit); + ale.depositAndLeveragePosition( + initialDolaDeposit, + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapDataDeposit), + dbrData, + false + ); + + assertEq(DOLA.balanceOf(userPk), 0); + + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + + (((maxBorrowAmount + initialDolaDeposit) * 1.0365 ether) / + 1 ether), // approx 1.0365 ratio PT/DOLA + 1e14 // 0.01% Delta + ); + } + + function test_depositAndLeveragePosition_PT() public { + _mintInitialDolaAmountToUser(); + + uint256 initialDolaDeposit = initialDolaAmount / 10; + + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), initialDolaAmount); + // Swap DOLA for PT (initialDolaDeposit) (receiver is the helper, recipient is the user which will be depositing them) + bytes + memory pendleDataInitialPT = hex"c81f847a000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e8000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce00000000000000000000000000000000000000000000022c6c35d8ad8daca5320000000000000000000000000000000000000000000001190585372159a75b3100000000000000000000000000000000000000000000024e25315a2c6f790c4d0000000000000000000000000000000000000000000002320b0a6e42b34eb662000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000380000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cf5540fffcdc3d510b18bfca6d2b9987b07725590000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f083bd37f90001865377367054516e17014ccded1e7d814edc9ce400019d39a5de30e57443bff2a8307a4256c8797a34970a021e19e0c9bab24000000a01d5c122dc0d96c0000000c49b0001b28ca7e465c452ce4252598e0bc96aeba553cf820001744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0001888888888889758f76e7103c6cbf23abbf58f94635d39ebf03010203000a01010001020001ff00000000000000000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 initialLpAmount = helper.convertToCollateral( + initialDolaDeposit, + abi.encode(address(market), 0, pendleDataInitialPT) + ); + + // Swap DOLA to PT (initialDolaAmount - initialDolaDeposit) (receiver is the helper) + bytes + memory pendleDataInitialDeposit = hex"c81f847a000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e8000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce00000000000000000000000000000000000000000000138ed8e845253511ea5f0000000000000000000000000000000000000000000009e0b5f403e43f014a690000000000000000000000000000000000000000000014be4ae6d4f8eab5e90f0000000000000000000000000000000000000000000013c16be807c87e0294d2000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000130ee8e71790444000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000130ee8e7179044400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000001150901e57c62d6000000000000108338b12fed3b2a14cc000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000130ee8e7179044400000000000000000000000000000000000000000000000001058f30155d8f0552e30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000130ee8e717904440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002327b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238393338342e3831393337343337323537222c22416d6f756e744f7574555344223a2238383031322e3830343138353832313739222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223737393738343732323938363033323232373939353634222c2254696d657374616d70223a313733363236333233362c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2245474174712f717344474c6b6e337835425a576a7942647766772b6b4f34395a6874337370424578726b65526a333963727166745758737067374532494a575869316f616c5272633244416473314139485a2f73765a4233527952674f565a3363315a304c644c5231563146437a4948352f353150533233784e5a4e545368574d5047544d7854446d43464457686951676f304172306362676d4141486f4b4174472f565a49327276456a687634597661324c3843526d624e347448765a446e4f3557334e2b6f58732f754e39446a6b38776b3345443353546b4872414179447772436b4f7657756b37464a6474777345434b6c6b6b6363666867684242375470496a455331685a5666362f344447524f7856796f37514e4e6b475538706151324a6e356f596f6d675245496e696536324b7a687669653734616b47454d4b753353427a6d424b44774b5858466963766c794c7a6d773d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + helper.convertToCollateralAndDeposit( + initialDolaAmount - initialDolaDeposit, + userPk, + abi.encode(address(market), 0, pendleDataInitialDeposit) + ); + vm.stopPrank(); + + uint256 lpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 20000 ether); + + uint maxBorrowAmount = _getMaxBorrowAmount(lpAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + // Swap DOLA Max borrow amount to PT (receiver is the helper) + bytes + memory pendleDataLeverage = hex"c81f847a000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e8000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce0000000000000000000000000000000000000000000011a3e508cbe6b250fc9f0000000000000000000000000000000000000000000008e8c13feadbf29fda170000000000000000000000000000000000000000000012b595d306cde3e949fe0000000000000000000000000000000000000000000011d1827fd5b7e53fb42f000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000005af3107a400000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000b20000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000113087cb02f1ef7694960000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000113087cb02f1ef769496000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000f9e0216be43db00000000000000ee4cc35f88b6e5592b7000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000113087cb02f1ef769496000000000000000000000000000000000000000000000ebeab72df04eaabc1e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000113087cb02f1ef76949600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002327b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238303537312e3632323437313439373435222c22416d6f756e744f7574555344223a2237393536312e3330383336363338343934222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223730333333373033333439363136303438393638333735222c2254696d657374616d70223a313733363236333239392c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224774442f472b2f6f352b6c434a6279303836394736364d37624f2b42664670645542725a71724f5234754b564336446f396c6d704d46556e4d4857446b324c464e7355714a32414a47612f49485050416d74614833756942364c753144493161414877696a4a5146547777393563685a4d6658642b546f5057532b4e56777a754949556d657973765547716f337a47546e584e784f49616d4575323550554447787263374f373376626e693050795a674c47677564387a5746764f2f3867784f354c694b3061776b4b413145653243776262766e464746457354614347386c6e574978355879314e37445358697569537435544a445a6a3038367463354577684b6f3848627352486b4d3636534946434e6a45325535756f373974694f34636742774b4a783139306b7365544e62636b58454d426c7a7854545a494a4c5551666f38654d64464b4f2f7a61564a5349435073463154513d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + vm.startPrank(userPk); + IERC20(pendlePT).approve(address(ale), initialLpAmount); + ale.depositAndLeveragePosition( + initialLpAmount, + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), uint(100000), pendleDataLeverage), + dbrData, + true + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + lpAmount + + initialLpAmount + + ((maxBorrowAmount * 1.0365 ether) / 1 ether), + 1e14 // 0.01% Delta + ); + } + + function test_deleveragePosition_Redeem_with_PT_and_YT_router() public { + test_leveragePosition_Mint_PT_and_YT_with_DOLA_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer( + userPk, + amountToWithdraw - IERC20(pendleYT).balanceOf(userPk) + ); + vm.stopPrank(); + // Redeem PT and YT using all collateral (amountToWithdraw) (receiver is the ALE) + bytes + memory pendleRedeemData = hex"47f1de220000000000000000000000009123ef9b7db2e3d968b01c6ff99839acb242da1300000000000000000000000096512230bf0fa4e20cf02c3e8a7d983132cd2b9f0000000000000000000000000000000000000000000028e5a7091f3bc94d48c90000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000028bfaa0a0bcb95c851600000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023b5bfb83f6d82c58500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002b28e1ad68ad8fd000000000000292908d90428258485eb0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000023b5bfb83f6d82c585000000000000000000000000000000000000000000000028bfaa0a0bcb95c85160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000023b5bfb83f6d82c5850000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002347b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223139313833372e3933323739313839363633222c22416d6f756e744f7574555344223a223139313837332e38393134333935343531222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313934333733393739383530303730323639363538363033222c2254696d657374616d70223a313733363236343039382c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2261436846646739663758313472586e61736f364d4c42336874696e396765564d544c4946734543786b3130524932626c7a3341444c4a2f704754764a596a59664b432f36384b59565974706d32576a69724d4a7a70546f64526a394169356a55357552454e5a4c5a7a4d4a46494b662b554f7a714b653168515938544d5243417969574e5931543076505761307475766477334664396949376b426f5333706c796278334330754a2b324947692f304c4b784b644862365275486a66686e372b5742536a52326b43674531735666506d34415a784463585571373663696c654563734e6f677963757a7170774858475555754a79657753793470552b676751334250455a72344f324c743058754675304d797350746d336155764d663654375232364971726c48536e4f6a4f6d616766433639445a58354d466f466249324e793956577361436e625a676b6b66617679694b734639513d3d227d7d00000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(userPk); + vm.startPrank(userPk); + IERC20(pendleYT).approve(address(helper), amountToWithdraw); + + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleRedeemData), + dbrData + ); + + assertEq(SimpleERC20Escrow(userPkEscrow).balance(), 0); + assertApproxEqRel( + DOLA.balanceOf(userPk), + 194373979850070269658603 - debt, + 0.01 ether + ); + assertEq(IERC20(pendleYT).balanceOf(userPk), 0); + assertEq(market.debts(userPk), 0); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_AFTER_DEADLINE_deleveragePosition_Redeem_with_PT_and_YT_router() + public + { + test_leveragePosition_Mint_PT_and_YT_with_DOLA_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 1000000 ether); + vm.warp(block.timestamp + 180 days); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer( + userPk, + amountToWithdraw - IERC20(pendleYT).balanceOf(userPk) + ); + vm.stopPrank(); + // Redeem PT and YT using all collateral (amountToWithdraw) (receiver is the ALE) + bytes + memory pendleRedeemData = hex"47f1de220000000000000000000000009123ef9b7db2e3d968b01c6ff99839acb242da1300000000000000000000000096512230bf0fa4e20cf02c3e8a7d983132cd2b9f0000000000000000000000000000000000000000000028e5a7091f3bc94d48c90000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000028bfaa0a0bcb95c851600000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023b5bfb83f6d82c58500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002b28e1ad68ad8fd000000000000292908d90428258485eb0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000023b5bfb83f6d82c585000000000000000000000000000000000000000000000028bfaa0a0bcb95c85160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000023b5bfb83f6d82c5850000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002347b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223139313833372e3933323739313839363633222c22416d6f756e744f7574555344223a223139313837332e38393134333935343531222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313934333733393739383530303730323639363538363033222c2254696d657374616d70223a313733363236343039382c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2261436846646739663758313472586e61736f364d4c42336874696e396765564d544c4946734543786b3130524932626c7a3341444c4a2f704754764a596a59664b432f36384b59565974706d32576a69724d4a7a70546f64526a394169356a55357552454e5a4c5a7a4d4a46494b662b554f7a714b653168515938544d5243417969574e5931543076505761307475766477334664396949376b426f5333706c796278334330754a2b324947692f304c4b784b644862365275486a66686e372b5742536a52326b43674531735666506d34415a784463585571373663696c654563734e6f677963757a7170774858475555754a79657753793470552b676751334250455a72344f324c743058754675304d797350746d336155764d663654375232364971726c48536e4f6a4f6d616766433639445a58354d466f466249324e793956577361436e625a676b6b66617679694b734639513d3d227d7d00000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(userPk); + vm.startPrank(userPk); + uint256 ytBalBefore = IERC20(pendleYT).balanceOf(userPk); + + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleRedeemData), + dbrData + ); + assertEq(IERC20(pendleYT).balanceOf(userPk), ytBalBefore); + } + + function test_deleveragePosition_Swap_PT_to_DOLA_router() public { + test_leveragePosition_Swap_DOLA_for_PT_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000009123ef9b7db2e3d968b01c6ff99839acb242da13000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce0000000000000000000000000000000000000000000029bed86f3d5430f5df9b00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000a80000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000027c9826c343c05e8faee0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022ddf92a7faedbd903b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002a242992bba1475000000000000283064b5b3781aa819e90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000022ddf92a7faedbd903b00000000000000000000000000000000000000000000027c9826c343c05e8faee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000022ddf92a7faedbd903b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002357b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223138373631352e3330393836373435393831222c22416d6f756e744f7574555344223a223138373834302e3931353135393932343432222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313839373837333539393334303236383639313139343635222c2254696d657374616d70223a313733363236333933322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224b61594b3637504f6544415066746e36514148516857526a6f4b796d6c34484f586c564a78473967323532695a2f2b702b674b4f30556853306d67466b476e5a326d4c4b554558783536362f4c2b4c6f58572b544a434d46785067336c7874624f6b4771504434786f73704c336e65337a56446d46372b4b7a2f766235663630437366746a557832366875743930585434496c6e2f324155714d50745a4f6e4a3034534a6571656f33384b4c44736e38544436314f6e346637733343716348336b3555776d666a316576346777714256656c484665417771796b6436626146706d6e6e676e5452376435394248516e6256653952767843454e7359574d537a2b524948326573754c71725333584a727058546b4f726a466637796f56793636556e746f6e6a2f4646744b456b4e5936326e504e3954464d6c714c58587762726c67757544644d79413843494435472b666b72625738773d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(address(userPk)); + vm.startPrank(userPk); + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged (repay debt) + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapToDolaData), + dbrData + ); + + assertEq(SimpleERC20Escrow(userPkEscrow).balance(), 0); + // Dola balance is equal to collateral sold for DOLA minus debt + assertEq(DOLA.balanceOf(userPk), 189795305864399402361976 - debt); + assertEq(market.debts(address(userPk)), 0); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_fail_AFTER_DEADLINE_deleveragePosition_Swap_PT_to_DOLA_router() + public + { + test_leveragePosition_Swap_DOLA_for_PT_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 1000000 ether); + vm.warp(block.timestamp + 180 days); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000009123ef9b7db2e3d968b01c6ff99839acb242da13000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce0000000000000000000000000000000000000000000029bed86f3d5430f5df9b00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000a80000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000027c9826c343c05e8faee0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022ddf92a7faedbd903b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002a242992bba1475000000000000283064b5b3781aa819e90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000022ddf92a7faedbd903b00000000000000000000000000000000000000000000027c9826c343c05e8faee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000022ddf92a7faedbd903b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002357b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223138373631352e3330393836373435393831222c22416d6f756e744f7574555344223a223138373834302e3931353135393932343432222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313839373837333539393334303236383639313139343635222c2254696d657374616d70223a313733363236333933322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224b61594b3637504f6544415066746e36514148516857526a6f4b796d6c34484f586c564a78473967323532695a2f2b702b674b4f30556853306d67466b476e5a326d4c4b554558783536362f4c2b4c6f58572b544a434d46785067336c7874624f6b4771504434786f73704c336e65337a56446d46372b4b7a2f766235663630437366746a557832366875743930585434496c6e2f324155714d50745a4f6e4a3034534a6571656f33384b4c44736e38544436314f6e346637733343716348336b3555776d666a316576346777714256656c484665417771796b6436626146706d6e6e676e5452376435394248516e6256653952767843454e7359574d537a2b524948326573754c71725333584a727058546b4f726a466637796f56793636556e746f6e6a2f4646744b456b4e5936326e504e3954464d6c714c58587762726c67757544644d79413843494435472b666b72625738773d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(address(userPk)); + + vm.startPrank(userPk); + vm.expectRevert(PendlePTHelper.PendleSwapFailed.selector); + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged (repay debt) + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapToDolaData), + dbrData + ); + } + + function test_deleveragePosition_sellDBR() public { + test_leveragePosition_Swap_DOLA_for_PT_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + uint256 debt = market.debts(address(userPk)); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( + dbr.balanceOf(userPk), + 10000, + 0 + ); // sell all DBR + bytes memory swapData; + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000009123ef9b7db2e3d968b01c6ff99839acb242da13000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce0000000000000000000000000000000000000000000029bed86f3d5430f5df9b00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000a80000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000027c9826c343c05e8faee0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022ddf92a7faedbd903b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002a242992bba1475000000000000283064b5b3781aa819e90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000022ddf92a7faedbd903b00000000000000000000000000000000000000000000027c9826c343c05e8faee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000022ddf92a7faedbd903b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002357b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223138373631352e3330393836373435393831222c22416d6f756e744f7574555344223a223138373834302e3931353135393932343432222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313839373837333539393334303236383639313139343635222c2254696d657374616d70223a313733363236333933322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224b61594b3637504f6544415066746e36514148516857526a6f4b796d6c34484f586c564a78473967323532695a2f2b702b674b4f30556853306d67466b476e5a326d4c4b554558783536362f4c2b4c6f58572b544a434d46785067336c7874624f6b4771504434786f73704c336e65337a56446d46372b4b7a2f766235663630437366746a557832366875743930585434496c6e2f324155714d50745a4f6e4a3034534a6571656f33384b4c44736e38544436314f6e346637733343716348336b3555776d666a316576346777714256656c484665417771796b6436626146706d6e6e676e5452376435394248516e6256653952767843454e7359574d537a2b524948326573754c71725333584a727058546b4f726a466637796f56793636556e746f6e6a2f4646744b456b4e5936326e504e3954464d6c714c58587762726c67757544644d79413843494435472b666b72625738773d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + vm.startPrank(userPk); + dbr.approve(address(ale), dbr.balanceOf(userPk)); + ale.deleveragePosition( + debt, + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapToDolaData), + dbrData + ); + + assertEq(SimpleERC20Escrow(userPkEscrow).balance(), 0); + + assertEq(dbr.balanceOf(userPk), 0); + // Dola balance is greater than collateral sold for DOLA minus debt because we also sold DBR + assertGt(DOLA.balanceOf(userPk), 189795305864399402361976 - debt); + assertEq(market.debts(address(userPk)), 0); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_fail_InsufficientPT_after_swap() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + vm.expectRevert(PendlePTHelper.InsufficientPT.selector); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode( + address(market), + (initialDolaAmount * 1.1 ether) / 1 ether, + pendleDataInitial + ) + ); + } + + function test_fail_InsufficientPT_after_mint() public { + _mintInitialDolaAmountToUser(); + bytes + memory pendleMintData = hex"d0f42385000000000000000000000000cc39fc2ed370a4373ff4b8ebd507cd26906a80e800000000000000000000000096512230bf0fa4e20cf02c3e8a7d983132cd2b9f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000131953f892f952ddaea90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000131953f892f952ddaea900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000115a0ce892f03fa000000000000108c44f77cc1a67bcd16000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000131953f892f952ddaea9000000000000000000000000000000000000000000000d3d03f9309aeb963dab000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000131953f892f952ddaea900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002327b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238393333342e3633343132373134353838222c22416d6f756e744f7574555344223a2238393536302e3431363532383733343938222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223738313435333737343734313138363530393430363934222c2254696d657374616d70223a313733363236313338322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22464f70686550495230465553777469393367654f72525a74427a616f674f3649684368615a476f44654c4f6f6d6d536c774a65307342627555717a63612f78634e2f4b784374697a4a79386949316777325a2b34444469384c52746e71744e476a495230635468546f4539495469594d37336b5843446f38683566654d6147695656686c2f4156356e772f2f354363595046577552765668797538524d65587279413778774e34774949636376567757616555416245434d7941496d48457874577362747849437061486c35562f39624a616c326552486b414b5173654547744c4e764d6842576e7a654a3047656958426e76352b664974574d4f6750764e6d56464d4779665276343054714735544252714c43794a674d61463654537173566d6d5646674165567a43426c634c4b6e43476e56706831784d326c736464574c78536342495a39616764696a5745536e6654386235513d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.prank(userPk); + vm.expectRevert(PendlePTHelper.InsufficientPT.selector); + helper.convertToCollateral( + initialDolaAmount, + abi.encode(address(market), initialDolaAmount, pendleMintData) + ); + } + + function test_withdrawAndConvertFromCollateral_SWAP() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(helper), + userPk, + amountToWithdraw, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + IPendleHelper.Permit memory permit = IPendleHelper.Permit( + block.timestamp, + v, + r, + s + ); + + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf000000000000000000000000cdd26eb5eb2ce0f203a84553853667ae69ca29ce0000000000000000000000000000000000000000000015f3ead1362d4aa6880600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000dc0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000001507b990cd0c30827dd00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000bc4e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000005e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000057064f49ad7123c92560882a45518374ad982e850000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000043bc8c40ec139fd0c7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000008272e1a3dbef607c04aa6e5bd3a1a134c8ac063b000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d2def47b1f5943d27800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e2c523752c9abdfdbe70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000016464471999e099000000000000153e1b1de6643b57d9970000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000012681afb618ae5dce864000000000000000000000000000000000000000000001507b990cd0c30827dd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd6700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000012681afb618ae5dce86400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002327b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2239393633332e3033333633363636393938222c22416d6f756e744f7574555344223a2239393839372e30383937363034373237222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313030333135333438323433393634303537303832323633222c2254696d657374616d70223a313733363435373037312c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22576b5455384c5a3837586861774575536f573755736a59784f69756848336e386d4753337359505758517239324f302b374b414e4464774667366e43533535785a76453264304474494a346841652b6c706c3365576f6e33726464614f7a4a3748324c53375561464d654a79306b614269564a366c334c66452b4548463930644a326f5761622b4f706b47456667527369434b574e476d775a6673614f756f6b4e5764566a4939644e64796e74464656654e66684235575a6c6631697737666a2f304555507552785239524a557850346e2f7a3136624467666c536f4f466562634d5544464c2b5569597775675a6737747456582f68777a6d6d77733745504d437a717131737a473536585730747277726d6a5456763144674565666534674a7a702f3971783062787249484a4b35306f74683752594b54483478656b6e68624f6e6f655a614f676a6341755153697a5536384978513d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + assertEq(DOLA.balanceOf(userPk), 0); + vm.startPrank(userPk); + uint256 dolaAmount = helper.withdrawAndConvertFromCollateral( + amountToWithdraw, + userPk, + permit, + abi.encode(address(market), 98000 ether, pendleSwapToDolaData) + ); + assertEq(DOLA.balanceOf(userPk), dolaAmount); + } + + function test_withdrawAndConvertFromCollateral_REDEEM() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + gibDBR(userPk, 20000 ether); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(helper), + userPk, + amountToWithdraw, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + IPendleHelper.Permit memory permit = IPendleHelper.Permit( + block.timestamp, + v, + r, + s + ); + + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleRedeemToDolaData = hex"47f1de220000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf00000000000000000000000096512230bf0fa4e20cf02c3e8a7d983132cd2b9f0000000000000000000000000000000000000000000015f3ead1362d4aa688060000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000015e2465534fd239f4bc90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000132853f8cb988597b6f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000172dbf91cd0e9da000000000000161add05e7f2c44e285a0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000132853f8cb988597b6f80000000000000000000000000000000000000000000015e2465534fd239f4bc9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000132853f8cb988597b6f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002347b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223130333638382e36343736393437303934222c22416d6f756e744f7574555344223a223130333832312e3634333939303035313037222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313034333837363034333539373335373730323238383236222c2254696d657374616d70223a313733363435373335322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22536b5a6751506656577232654e484278436c51507555506b63363843356c6753557166464d36704736373176547937316b425730794e5271666f70574d6b4d627a3343334b555164672f61756a6f62684e64553131694164484c6558685131433578436c586b73704162456f566a4c62306c38423439744a6645416b7778705635785039464e735a47532f78426a78414153364c6948763232574758494841312b4f50324b7a6778533656646b776a45714b4a7269427a4d6c3864373538796947793049312f4f2b522b6d5471766349492b75324374643879495a476d376d50523779445051514856536246357944665852515934654e6755724f3549652b76362f2b747234497869736e345961567a477a4977546a4a793073687434372b6f734e35397259725a49446f4758554330364d4d70714f6479302b6133445849394835722b39723734506e4b57626d6b6647422b5773513d3d227d7d00000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer(userPk, amountToWithdraw); + vm.stopPrank(); + + assertEq(DOLA.balanceOf(userPk), 0); + vm.startPrank(userPk); + IERC20(pendleYT).approve(address(helper), amountToWithdraw); + uint256 dolaAmount = helper.withdrawAndConvertFromCollateral( + amountToWithdraw, + userPk, + permit, + abi.encode( + address(market), + (initialDolaAmount * 98) / 100, + pendleRedeemToDolaData + ) + ); + assertEq(DOLA.balanceOf(userPk), dolaAmount); + assertEq(IERC20(pendleYT).balanceOf(userPk), 0); + } + + function test_withdrawAndConvertFromCollateral_REDEEM_AFTER_DEADLINE() + public + { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + gibDBR(userPk, 1000000 ether); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + vm.warp(block.timestamp + 180 days); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(helper), + userPk, + amountToWithdraw, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + IPendleHelper.Permit memory permit = IPendleHelper.Permit( + block.timestamp, + v, + r, + s + ); + + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleRedeemToDolaData = hex"47f1de220000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf00000000000000000000000096512230bf0fa4e20cf02c3e8a7d983132cd2b9f0000000000000000000000000000000000000000000015f3ead1362d4aa688060000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000015e2465534fd239f4bc90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000007ae5c4cf5cddfbda870a80d021997d8a04f4ff1100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000884e21fd0e90000000000000000000000000000000000000000000000000000000000000020000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000132853f8cb988597b6f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000172dbf91cd0e9da000000000000161add05e7f2c44e285a0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000132853f8cb988597b6f80000000000000000000000000000000000000000000015e2465534fd239f4bc9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000f081470f5c6fbccf48cc4e5b82dd926409dcdd67000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000132853f8cb988597b6f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002347b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223130333638382e36343736393437303934222c22416d6f756e744f7574555344223a223130333832312e3634333939303035313037222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313034333837363034333539373335373730323238383236222c2254696d657374616d70223a313733363435373335322c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22536b5a6751506656577232654e484278436c51507555506b63363843356c6753557166464d36704736373176547937316b425730794e5271666f70574d6b4d627a3343334b555164672f61756a6f62684e64553131694164484c6558685131433578436c586b73704162456f566a4c62306c38423439744a6645416b7778705635785039464e735a47532f78426a78414153364c6948763232574758494841312b4f50324b7a6778533656646b776a45714b4a7269427a4d6c3864373538796947793049312f4f2b522b6d5471766349492b75324374643879495a476d376d50523779445051514856536246357944665852515934654e6755724f3549652b76362f2b747234497869736e345961567a477a4977546a4a793073687434372b6f734e35397259725a49446f4758554330364d4d70714f6479302b6133445849394835722b39723734506e4b57626d6b6647422b5773513d3d227d7d00000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer(userPk, amountToWithdraw); + vm.stopPrank(); + + assertEq(DOLA.balanceOf(userPk), 0); + vm.startPrank(userPk); + IERC20(pendleYT).approve(address(helper), amountToWithdraw); + + helper.withdrawAndConvertFromCollateral( + amountToWithdraw, + userPk, + permit, + abi.encode( + address(market), + (initialDolaAmount * 98) / 100, + pendleRedeemToDolaData + ) + ); + assertGt(DOLA.balanceOf(userPk), (initialDolaAmount * 98) / 100); + // Didn't transfer token even if approval granted because after maturity + assertEq(IERC20(pendleYT).balanceOf(userPk), amountToWithdraw); + } + function test_Access_Control_convertToCollateral_ALE() public { + vm.expectRevert(PendlePTHelper.NotALE.selector); + helper.convertToCollateral( + userPk, + initialDolaAmount, + abi.encode(address(market), initialDolaAmount, pendleDataInitial) + ); + } + + function test_Access_Control_convertFromCollateral_ALE() public { + vm.expectRevert(PendlePTHelper.NotALE.selector); + helper.convertFromCollateral( + userPk, + initialDolaAmount, + abi.encode(address(market), initialDolaAmount, pendleDataInitial) + ); + } + + function test_updateMarketHelper_if_helper_not_address_zero() public { + vm.prank(gov); + ale.updateMarketHelper(address(market), address(1)); + (, , IPendleHelper helper, ) = ale.markets(address(market)); + assertEq(address(helper), address(1)); + } + + function test_updateMarketHelper_if_market_not_set() public { + vm.expectRevert( + abi.encodeWithSelector(ALEV2.MarketNotSet.selector, address(2)) + ); + vm.prank(gov); + ale.updateMarketHelper(address(2), address(1)); + } + + function test_updateMarketHelper_fails_if_helper_not_set() public { + vm.expectRevert(ALEV2.InvalidHelperAddress.selector); + vm.prank(gov); + ale.updateMarketHelper(address(market), address(0)); + } + + function _getMaxBorrowAmount( + uint amountCollat + ) internal view returns (uint) { + return + (amountCollat * + oracle.viewPrice(address(pendlePT), 0) * + market.collateralFactorBps()) / + 10_000 / + 1e18; + } +} diff --git a/test/util/aleTests/ALEPendlePTUSDe29May25.t.sol b/test/util/aleTests/ALEPendlePTUSDe29May25.t.sol new file mode 100644 index 00000000..46d8ee3b --- /dev/null +++ b/test/util/aleTests/ALEPendlePTUSDe29May25.t.sol @@ -0,0 +1,1035 @@ +pragma solidity ^0.8.13; + +import {ICurvePool} from "src/interfaces/ICurvePool.sol"; +import {PendlePTHelper, IPendleHelper} from "src/util/PendlePTHelper.sol"; +import "test/marketForkTests/PendlePTsUSDe29May25MarketForkTest.t.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; +import {SimpleERC20Escrow} from "src/escrows/SimpleERC20Escrow.sol"; + +/* + User 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf + ALE 0x4dF2EaA1658a220FDB415B9966a9ae7c3d16e240 + helper 0x4809fE7d314c2AE5b2Eb7fa19C1B166434D29141 + DOLA 0x865377367054516e17014CcdED1e7d814EDC9ce4 + address pendlePT = 0xb7de5dFCb74d25c2f21841fbd6230355C50d9308 +*/ +contract ALEV2PTUSDe29May25Test is PendlePTsUSDe29May25MarketForkTest { + ALEV2 ale; + address userPk = vm.addr(1); + PendlePTHelper helper; + address userPkEscrow; + + address pendleRouter = address(0x888888888889758F76e7103c6CbF23ABbF58F946); + address pendleYT = address(0x1de6Ff19FDA7496DdC12f2161f6ad6427c52aBBe); + address marketPT = address(0xB162B764044697cf03617C2EFbcB1f42e31E4766); + + // Pendle Router Mock + address pendleYTHolder = + address(0x8607a7D180de23645Db594D90621d837749408d5); + + // Get initial PT + // Swap 100K DOLA for PT (recipient helper) + bytes pendleDataInitial = + hex"c81f847a0000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d29141000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e4766000000000000000000000000000000000000000000001460b0f643c093ac7dcf000000000000000000000000000000000000000000000ab9a0819cec17d40c52000000000000000000000000000000000000000000001ad01144084e3b921ece000000000000000000000000000000000000000000001573410339d82fa818a5000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000001820000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000152d02c7e14af68000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001584e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000108000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000fc0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000f600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000006691dbb44154a9f23f8357c56fc9ff5548a8bdc4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002544faa778090e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007c4e143b23d72e6938e06291f705b5ae3d5c7c7c00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002540906888aeac0ca4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000028f74f359000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e57180685e3348589e9521aa53af0bcd497e884d000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000d8d726b7177a800000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040301a40330000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000031373595f40ea48a7aab6cbcb0d377c6066e2dca00000000000000000000000000000000000000000000000000000000ee36ad82000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b00000000000000000000000000000000000000000000000000000000ee5af26c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a349700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000e9c7f5bd65501200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000008272e1a3dbef607c04aa6e5bd3a1a134c8ac063b000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf4000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003626711d3d231b859c500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b00000000000000000000000000000000000000000000000000000003b9385ef2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000000000000000000000000000000000000000000200000000000000000013115d793a6549a000000000000122f3b88b0b4c077c590000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000152d02c7e14af6800000000000000000000000000000000000000000000000001146788ea7deea0b62150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000152d02c7e14af680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002657b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223130303334352e3634393330303136353831222c22416d6f756e744f7574555344223a223130303032392e3938343630343537333234222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223835383733383833353336303533343032383436363038222c2254696d657374616d70223a313734343332393836372c22526f7574654944223a2231303832323436302d353063612d343066352d623330612d393136363964663463376564222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2255344a333730695546363465756d4d46524c442f57526e714e31464c6c4b6d76396c513635482f4f4d4636346e6b4d4365474e46715753617173612b6176527051615a474531656246643977337250646b67547277597154555864565979596e4456756f43484d4d614756437138304a566b4744346a623842323362364430704f313064575762746a6f74626e4a546c5a676a6a32642f4c4c3555477942776e4841744f3657703936733053555268507a633677644754792f3736446256564656526831422f31304f5044596f682b4a74446844775532682f31486e51335968326d64516d4f637a745a56653531743743434342335a47383648524a4d477566352b454e7677674d58686a4c2b6750746946714378375a654848704442754a546e74486741384e64722b734779534b74705a5a3461312f515561752b4c6c744d434d3434536971454d624f44363463476734356c71673d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + // Swap maxBorrowAmount from 100k DOLA to PT deposit (recipient helper) + bytes pendleSwapData = + hex"c81f847a0000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d29141000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e476600000000000000000000000000000000000000000000125ba211ba86559a51330000000000000000000000000000000000000000000009a97db87d1e480060a2000000000000000000000000000000000000000000001827ba4d38cbb400f195000000000000000000000000000000000000000000001352fb70fa3c9000c144000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000001820000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000001313a08445915c4095e30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001584e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000108000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000fc0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000f600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000006691dbb44154a9f23f8357c56fc9ff5548a8bdc4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002dc8b470a6f67701685000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007c4e143b23d72e6938e06291f705b5ae3d5c7c7c00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002dc33fc842d828cc58700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000032529bd37000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e57180685e3348589e9521aa53af0bcd497e884d000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000c3585735fb930cd2cb000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040301a40330000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000031373595f40ea48a7aab6cbcb0d377c6066e2dca00000000000000000000000000000000000000000000000000000000d69b5341000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b00000000000000000000000000000000000000000000000000000000d6bbddd1000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a349700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000c665b892d3815906166000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000008272e1a3dbef607c04aa6e5bd3a1a134c8ac063b000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000030d615cd7ee4c334b2d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf4000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030c859185c6f60a4f0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000035abc01ae000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000002000000000000000000112d844c64848140000000000001061cc7f6a8c6dd3c1f3000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000001313a08445915c4095e3000000000000000000000000000000000000000000000f901bdf72056855f8400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000001313a08445915c4095e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002637b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2239303239352e3938343134303330343232222c22416d6f756e744f7574555344223a2238393936342e3733393730363237343235222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223737333631393333353134373231333030303338313331222c2254696d657374616d70223a313734343333303039352c22526f7574654944223a2237383931383839352d616562312d343939302d613234642d346335336534323433343136222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224678524e7353517552586261792b4c68506e2b375a484f69677a4b2b4637766c3936334b7a6b5a6a335753526b6d694458466a314578463946667556336e566a35752f46544649576e3441514278625166355879656e346f7248704a4b486c7163334f57424f4f7774724144664e77307a4735493144757062367259334476497645617332786a5a3757687061342f62536445534866734f7036697464644d416b2f346f4b7645564b707854704f44584c7531596c4f42756357645a4d654c356c7938633668344a6452634b586639393870422b424d536f634b752b65515971534a674c6f544c684e30325530723449454150786b543479476e644a3451735472677252354c42665264494938416e66583043685738434e38536735705a673375556f635671592f5451664c526f4f594e3254335332546c364f346666424566472b5267484157634943416b475872486642687855513d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 initialDolaAmount = 100000 ether; // 100K DOLA + + function setUp() public override { + super.setUp(); + + vm.startPrank(gov); + + ale = ALEV2(payable(aleV2Addr)); + + helper = PendlePTHelper( + pendlePTHelperAddr + ); + ale.setMarket(address(market), address(DOLA), address(helper), false); + helper.setMarket(address(market), address(pendlePT), pendleYT); + borrowController.allow(address(ale)); + DOLA.mint(address(market), 1000000 ether); + vm.stopPrank(); + + userPkEscrow = address(market.predictEscrow(userPk)); + + vm.prank(userPk); + DOLA.approve(address(helper), initialDolaAmount); + } + + function _mintInitialDolaAmountToUser() internal { + vm.prank(gov); + DOLA.mint(userPk, initialDolaAmount); + } + function test_leveragePosition_Mint_PT_and_YT_with_DOLA_router() public { + _mintInitialDolaAmountToUser(); + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + gibDBR(userPk, 20000 ether); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + // Mint PT and YT with maxBorrowAmount (swap recipient is the helper, then distribute PT to ALE and YT to userPk) + bytes + memory pendleMintData = hex"d0f423850000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d291410000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe00000000000000000000000000000000000000000000122904f889feccff18fb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000001313a08445915c4095e30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001584e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000108000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000fc0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000f600000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000008a00000000000000000000000000000000000000000000000000000000000000a400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000006691dbb44154a9f23f8357c56fc9ff5548a8bdc4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002dc8b470a6f67701685000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007c4e143b23d72e6938e06291f705b5ae3d5c7c7c00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000002dc33fc842d828cc58700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000032529bd37000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e57180685e3348589e9521aa53af0bcd497e884d000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000c3585735fb930cd2cb000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040301a40330000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000031373595f40ea48a7aab6cbcb0d377c6066e2dca00000000000000000000000000000000000000000000000000000000d69b5341000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b00000000000000000000000000000000000000000000000000000000d6bbfea4000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a349700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000c665b892d3815906166000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000008272e1a3dbef607c04aa6e5bd3a1a134c8ac063b000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000030d615cd7ee4c334b2d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000390f3595bca2df7d23783dfd126427cceb997bf4000000000000000000000000f939e0a03fb07f59a73314e73794be0e57ac1b4e000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030c859185c6f60a4f0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000035abc01ae000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000002000000000000000000112d847edb9bddb0000000000001061ccaf8b27713fa896000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000001313a08445915c4095e3000000000000000000000000000000000000000000000f901c0d2a98ab96135b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000001313a08445915c4095e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002637b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2239303234352e3738393933393232323333222c22416d6f756e744f7574555344223a2238393930352e3432333638323832333333222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223737333631393437303631333730333331353732333734222c2254696d657374616d70223a313734343333303031322c22526f7574654944223a2262366461626362652d656238632d343430322d613636322d386566653139393063373563222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a224b7156356c50556d357a4a374d6e38455731314636477938527a77336d446a4d5763507a707978376d65786a367138614a51546f323361794a437046506d596c6161393330436b4165366d73516758775064674152647864494c782b61634a65416832364430705969726531764c684f452f706f6158514d6a754d614362396f556b695578764150684846532b72544d5952334d504d6a5037782f496e7655506b2f326e4266704277564a5863556a327169545958626a56574c72595736394b5a567a50434a6d7771416f30684c5a32413970773379437a524b4e4c645562564c66553745566533676b5155694c4e43484d75593572726c6449682b6642522b392b6d332f66343858596b56735976654f706852734669517434734e744e47552b34564b7973504a616462585261373157384d477732757434497445362b4c33384445796a424342336232556f7061314c4a586165673d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.prank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), 0, pendleMintData), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + ((maxBorrowAmount * 1.0016512 ether) / 1 ether), //1.0016512 ratio minted PT and YT to DOLA + 1e14 // 0.01% Delta + ); + + assertApproxEqRel( + IERC20(pendleYT).balanceOf(userPk), + ((maxBorrowAmount * 1.0016512 ether) / 1 ether), // 1.0016512 ratio minted PT and YT to DOLA + 1e14 // 0.01% Delta + ); + } + + function test_leveragePosition_Swap_DOLA_for_PT_router() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), initialDolaAmount, pendleDataInitial) + ); + gibDBR(userPk, 20000 ether); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + vm.prank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), 0, pendleSwapData), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + ((maxBorrowAmount * 1.010968098 ether) / 1 ether), // approx 1.010968098 ratio PT/DOLA + 1e14 // 0.01% Delta + ); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_leveragePosition_buyDBR_Swap_DOLA_for_PT() public { + _mintInitialDolaAmountToUser(); + + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), initialDolaAmount); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + vm.stopPrank(); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Calculate the amount of DOLA needed to borrow to buy the DBR needed to cover for the borrowing period + (uint256 dolaForDBR, uint256 dbrAmount) = ale + .approximateDolaAndDbrNeeded(maxBorrowAmount, 15 days, 8); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount + dolaForDBR, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( + dolaForDBR, + (dbrAmount * 90) / 100, + 0 + ); + + vm.prank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), 0, pendleSwapData), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + ((maxBorrowAmount * 1.010968098 ether) / 1 ether), // approx 1.010968098 ratio PT/DOLA + 1e14 // 0.01% Delta + ); + assertEq(DOLA.balanceOf(address(ale)), 0); + assertGt(dbr.balanceOf(userPk), (dbrAmount * 90) / 100); + } + + function test_depositAndLeveragePosition_DOLA() public { + _mintInitialDolaAmountToUser(); + + uint256 initialDolaDeposit = initialDolaAmount / 10; + // Swap DOLA for PT (initialDolaAmount - initialDolaDeposit) (receiver is the helper) + bytes + memory pendleInitialDepositData = hex"c81f847a0000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d29141000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e4766000000000000000000000000000000000000000000001256c2ee948f0d275def0000000000000000000000000000000000000000000009a6221be0118c55103f0000000000000000000000000000000000000000000018214500c0c371f7530500000000000000000000000000000000000000000000134c4437c02318aa207f000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000009184e72a000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000012a0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000130ee8e71790444000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001004e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000a40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000007600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000001e7e4171bf4d3a0000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b00000000000000000000000000000000000000000000000000000002184cc397000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000006691dbb44154a9f23f8357c56fc9ff5548a8bdc4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000001e7e4171bf4d3a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007c4e143b23d72e6938e06291f705b5ae3d5c7c7c00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000001e7a435a54a7d35a9a600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000021846b9ff000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a349700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000f3f20b8dfa69d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000011291b191d98772000000000000105d979bbfbd7b093fd5000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000130ee8e7179044400000000000000000000000000000000000000000000000000f8c1cd3f62734e2630a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000130ee8e717904440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002637b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2239303239302e3530393037373736393334222c22416d6f756e744f7574555344223a2238393934372e3838353339333836393936222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223737323834333335343630393130393632393137333333222c2254696d657374616d70223a313734343333313632392c22526f7574654944223a2266326638316532322d366537382d343238622d393331362d663837373061356564303434222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2245396c495461524145714f422b38307a567946656536325a79506d2f327469776b4349496f4665666a4763734e69436278585255487854546441546d374c6c79727231774a524d3970414e657a57364d4e624d4b64594678304857754767766e4a5030716656534d2f66647578484932523139507656646856696a365a4f76414f6c325446344c6a435a325661577875587748456e455a39444848664847377644524169546270376d4e7466537a4c795a646d4d68424b504c7a6f3737466d664443534f4e6a706e3267446a504c362b4e7a2f586b4b72624f66672b6a6573352b38513554704c743443563442324b7077556e317864344f524152674b4d705858786c38304e6d6c565556756c46534754686373323571555263773734446d667037374c5634336a3975726e586a7738456c6e6a653052497a4c37466d7a595261727768557a5a4e33686b592f7a2b6a6532694431413d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b3e2c3ec88b1b4c0cd853f4321000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000414c455c6987b5c1a194fa1867dc570ce60a3f1a07414e05f937a93b7e5b843608780eb47561b50000000000000000000000000000000000000000000000000000000006812ba77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004c9edd5852cd905f086c759e8383e09bff1e68b30000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000ae7519fc3ca24195aa6dbb3af77aea2d98afde9f000000000000000000000000ae7519fc3ca24195aa6dbb3af77aea2d98afde9f00000000000000000000000000000000000000000000001043561a88293000000000000000000000000000000000000000000000000000000110c337f720fa110000000000000000000000000000000000000000000000000ab423d28f346a54000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041c6fd5465d2bda6c6d88281e4a369e65767d1d580697ec60c87c179732a27af8664330b011f7cc353a9101033e9ecf7978f45154650ab2c52df378dfd93b5b5231c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), initialDolaAmount - initialDolaDeposit); + helper.convertToCollateralAndDeposit( + initialDolaAmount - initialDolaDeposit, + userPk, + abi.encode(address(market), 0, pendleInitialDepositData) + ); + assertEq(DOLA.balanceOf(address(helper)), 0); + vm.stopPrank(); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 20000 ether); + + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + // Swap DOLA for PT (maxBorrowAmount + initialDolaDeposit) (receiver is the helper) + bytes + memory pendleSwapDataDeposit = hex"c81f847a0000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d29141000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e476600000000000000000000000000000000000000000000128fa162480494566d030000000000000000000000000000000000000000000009c4dbacfd7bac6361cc00000000000000000000000000000000000000000000186c253079b52ef8747e000000000000000000000000000000000000000000001389b759faf758c6c398000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000001480000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000001349ba770daa32d334b70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b50000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011e4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000ce00000000000000000000000000000000000000000000000000000000000000f200000000000000000000000000000000000000000000000000000000000000c20000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000bc000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000009400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000001edc5d8b49105151ee9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af000000000000000000000000000000000000000000000000000000021e6d990f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000021ec4e040000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000006691dbb44154a9f23f8357c56fc9ff5548a8bdc4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000001edc5d8b49105151ede000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007c4e143b23d72e6938e06291f705b5ae3d5c7c7c00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000001ed852e44df94d2ab6200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000021ebd8561000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a349700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000f6e2ec5a48828a8f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000115e111b245aaaa000000000000109019884677876b785e000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000001349ba770daa32d334b7000000000000000000000000000000000000000000000fbc18417624c0a618bf0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000001349ba770daa32d334b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002627b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2239313335332e30323639343533373433222c22416d6f756e744f7574555344223a2239313036332e3434373332393238323536222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223738323136303334363730343535343636373139333236222c2254696d657374616d70223a313734343333313735372c22526f7574654944223a2232643936383536652d386566392d343337332d626532302d386436316664323930623330222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22477a73762b4a71756c335670776433535a4168752b47776948466a6d3139315a79554f6251386d503675516c65484a35584e32365447496b58563756716b676a717a546f65374633415338475039554c57414964447764376b36765944344d394c76326350766d6979343644327156794a73366f6838554b36627641474169376f31394764336a367650465835644a79772f595857394a636d2b67635137584130672f6d324f652f44756f534e425a4241574544486978764a5856706d39686e38726b353244706e4d53585831435a5236666e75756f597045437a446471426546364a6c36597859716d375a75752b4e6d4f396b4a594f546868537769456c2b4259446b4d473755784259427a4b67584d634f5563574435346e764f31542f644368325a6d394c554f6c48465571627639446456714a4859527a5a367a32666c73414a5857342f713744762b4d6531322b54415847773d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + vm.startPrank(userPk); + DOLA.approve(address(ale), initialDolaDeposit); + ale.depositAndLeveragePosition( + initialDolaDeposit, + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapDataDeposit), + dbrData, + false + ); + + assertEq(DOLA.balanceOf(userPk), 0); + + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + + (((maxBorrowAmount + initialDolaDeposit) * 1.01082 ether) / + 1 ether), // approx 1.01082 ratio PT/DOLA + 1e14 // 0.01% Delta + ); + } + + function test_depositAndLeveragePosition_PT() public { + _mintInitialDolaAmountToUser(); + + uint256 initialDolaDeposit = initialDolaAmount / 10; + + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), initialDolaAmount); + // Swap DOLA for PT (initialDolaDeposit) (receiver is the helper, recipient is the user which will be depositing them) + bytes + memory pendleDataInitialPT = hex"c81f847a0000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d29141000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e4766000000000000000000000000000000000000000000000209bfa72d7dd4139fc100000000000000000000000000000000000000000000011101c3cd3b271e93970000000000000000000000000000000000000000000002ae82db4ebb9d0b63fe00000000000000000000000000000000000000000000022203879a764e3d272f000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000009184e72a00000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000e60000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000021e19e0c9bab2400000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af000000000000000000000000000000000000000000000000000000025381a5d9000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b0000000000000000000000000000000000000000000000000000000253e155b0000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000000000000000000000000000000000000000000200000000000000000001e82a7ce46c8ad00000000000001d18d2503479392f726000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000021e19e0c9bab24000000000000000000000000000000000000000000000000001ba46165cb732986aca0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000021e19e0c9bab240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002617b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a22393938352e3436353732353237373934222c22416d6f756e744f7574555344223a22393934372e323032323830313534343234222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a2238353837393036353333323134333831303738333130222c2254696d657374616d70223a313734343333313832392c22526f7574654944223a2232303166366637392d623438352d346561642d613732392d646536646530303461363266222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2255337944366b4c3537426e43555574724d774830394d5a744b3036684a4c554458435a4a687250517a786375516759694653322b664b61324c6a4655305675554347467369464b3575626566314572586d353751726b534d6b39344d636b5169696f6c436e6468345a4274644c536e522f443677547a364255577951796241696f33364b68696646387935497a7133734d4e38717758306b32767733736b6b50392b566838656e497a43644e6a447447306169413030724e68666135476f71394649494147356b5574377250315a2f4d4e677253642b485864354e61683238446e70704f5861596b49574231337a37456c7243714c6e64346e61466b5362325a38474958716861785254734c503151464f6e67776e6e58674a4949774772372b794635306243306b6e58522b39314f41766b6536784164373251776f69635046302b643471462f6451377951792f58393177316475673d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b3e2c3ec88b1b4c0cd853f4321000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000006cb79bf0598ef8013b793ff4d3e5b235cf6f84bdd08d5a0c31c8fd836b4cec5ad9821ca4a4dbba60000000000000000000000000000000000000000000000000000000067fdf664000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004c9edd5852cd905f086c759e8383e09bff1e68b30000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000731bfadaf268f6a388558fad07cfcc5ec9e9323f000000000000000000000000731bfadaf268f6a388558fad07cfcc5ec9e9323f000000000000000000000000000000000000000000000003849d4d6850881a6b00000000000000000000000000000000000000000000000000e17735d896e7ea0000000000000000000000000000000000000000000000000ab45f0c207219f80000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000418c032bfd8f33de1db5250a7d3ed86679ca55986075b4fcd6840a169156b3382168296f1ef69b54e304c309227d5c5e339bfa18eb99d08a206d491de0f0dcdad61b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 initialLpAmount = helper.convertToCollateral( + initialDolaDeposit, + abi.encode(address(market), 0, pendleDataInitialPT) + ); + + // Swap DOLA to PT (initialDolaAmount - initialDolaDeposit) (receiver is the helper) + bytes + memory pendleDataInitialDeposit = hex"c81f847a0000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d29141000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e4766000000000000000000000000000000000000000000001256c2491fc7a406b21d0000000000000000000000000000000000000000000009a6220ec6a51d317d5400000000000000000000000000000000000000000000182144dffe935c97f08700000000000000000000000000000000000000000000134c441d8d4a3a62faa8000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000009184e72a000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000012a0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000130ee8e71790444000000000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001004e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000a40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000007600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000001e7e4171bf4d3a0000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b00000000000000000000000000000000000000000000000000000002184cc397000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000006691dbb44154a9f23f8357c56fc9ff5548a8bdc4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000001e7e4171bf4d3a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007c4e143b23d72e6938e06291f705b5ae3d5c7c7c00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000001e7a435a54a7d35a9a600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000021846b9ff000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a349700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000f3f20b8dfa69d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000011291ac671b1034000000000000105d974ce844b40071e2000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000130ee8e7179044400000000000000000000000000000000000000000000000000f8c1c890fdade339f630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000130ee8e717904440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002637b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238393836392e3139313532373530313436222c22416d6f756e744f7574555344223a2238393531362e3839313437393430383934222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223737323834333133323638393439303434313935383130222c2254696d657374616d70223a313734343333313836322c22526f7574654944223a2231363230383834612d663430372d343565392d396264362d623231333665353862366462222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a225938716d6d624832794f66704544656a73587a5631415a4c6f2b7065392b536d4f7776765261782f516b794334765a656b596942645536794b704b6a476b304b50326c6d546b33506b4e7a4471373933506178704669483951472b4b513236595270674e624e48576f4c36776750583251446b576d4d427950376c7748306a72575a7a7a733663716a6c4334664e4934582b53334559434b673446525039324746776a634e6635354b72376a2b656c597350484b6c49525544764156587232364664305848665944646c473335374a692b5958436834596b52325259527776496f4253672b35342f436634464e4a504e786147375054524b336d5061564b3333477a6275797776714667326b5a3239466647537975616b6a41306d506f474e353232546c2f796c4e6c312f327033796c6c67536d53727447385a32594e456274793866425670433664644a3144786b467157714c77513d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b3e2c3ec88b1b4c0cd853f4321000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000041338ab7d8f3d5f1a194fa1867dc570ce60a3f1a07414e05f937a93b7e5b843608780eb47561b50000000000000000000000000000000000000000000000000000000006812ba77000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004c9edd5852cd905f086c759e8383e09bff1e68b30000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000ae7519fc3ca24195aa6dbb3af77aea2d98afde9f000000000000000000000000ae7519fc3ca24195aa6dbb3af77aea2d98afde9f00000000000000000000000000000000000000000000001043561a88293000000000000000000000000000000000000000000000000000000110c337f720fa110000000000000000000000000000000000000000000000000ab423d28f346a54000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041c6fd5465d2bda6c6d88281e4a369e65767d1d580697ec60c87c179732a27af8664330b011f7cc353a9101033e9ecf7978f45154650ab2c52df378dfd93b5b5231c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + helper.convertToCollateralAndDeposit( + initialDolaAmount - initialDolaDeposit, + userPk, + abi.encode(address(market), 0, pendleDataInitialDeposit) + ); + vm.stopPrank(); + + uint256 lpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 20000 ether); + + uint maxBorrowAmount = _getMaxBorrowAmount(lpAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + maxBorrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + // Swap DOLA Max borrow amount to PT (receiver is the helper) + bytes + memory pendleDataLeverage = hex"c81f847a0000000000000000000000004809fe7d314c2ae5b2eb7fa19c1b166434d29141000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e4766000000000000000000000000000000000000000000001085aab849fb41ffe1070000000000000000000000000000000000000000000008b223f534694b285b7d0000000000000000000000000000000000000000000015bd59e503073be4e4b800000000000000000000000000000000000000000000116447ea68d29650b6fa000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000009184e72a000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000012a0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000112b5f81ea34fa3888b90000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001004e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000a40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000009e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000007600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000001e37db4b0cd81ec9e7500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b000000000000000000000000000000000000000000000000000000021376dd00000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000006691dbb44154a9f23f8357c56fc9ff5548a8bdc4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000015700b564ca08d9439c58ca5053166e8317aa1380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000018b95654ad6b01eb024000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007c4e143b23d72e6938e06291f705b5ae3d5c7c7c00000000000000000000000015700b564ca08d9439c58ca5053166e8317aa138000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000018b61c9de47873bfa7a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000001dd125c32e4b5086c63cc13b3ca02c4a2a61fa9b00000000000000000000000000000000000000000000000000000001b2d42ee3000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a349700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000dbc4c67ee90c82d3a20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000f75bed6a1069410000000000000ebe667367a8ea0e383e000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000112b5f81ea34fa3888b9000000000000000000000000000000000000000000000e01ae20d5ad44c0b56e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000112b5f81ea34fa3888b900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002627b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a2238313237392e36333533373138383836222c22416d6f756e744f7574555344223a2238303934392e3338353939383037363633222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223639363235333934343932333639323137313430373938222c2254696d657374616d70223a313734343333323033372c22526f7574654944223a2266366163356636322d363731362d346635352d393339372d623866633462663766336465222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2242764b6954762b4a34424a6a74324e7a4f7a4a5a546739434a53624c2b326544486143567a3537352f6b7155524b536e31687162677941457a6342386d2b4f563658394b32423831634f7632484132794563314671395772534f30396b6a724557426b4f36586748745070707a66774a4948366d7449557762643652506e6777634e7064514943636c6c63324c35706f636b624e6d733358707937342f556c31546b396a73706e766666744a7375764f305a6c2f3052446d386774637a67517337754b71525a5a46494436374147696e48386c6d51554a6d7a4d635a39726d43715a4c573244435a2f62636f765a4a36626c7765783842623655796c644f6f47694b51426a4e613767435053513630634d6d616b56746a52417766324c615750586c6c754b4c62737674535048376149727471526f6d35704450533366772b324e625445437178784e3133583264556a5976683135773d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + vm.startPrank(userPk); + IERC20(pendlePT).approve(address(ale), initialLpAmount); + ale.depositAndLeveragePosition( + initialLpAmount, + maxBorrowAmount, + address(market), + address(0), + swapData, + permit, + abi.encode(address(market), uint(100000), pendleDataLeverage), + dbrData, + true + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + lpAmount + + initialLpAmount + + ((maxBorrowAmount * 1.01072 ether) / 1 ether), + 1e14 // 0.01% Delta + ); + } + + function test_deleveragePosition_Redeem_with_PT_and_YT_router() public { + test_leveragePosition_Mint_PT_and_YT_with_DOLA_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer( + userPk, + amountToWithdraw - IERC20(pendleYT).balanceOf(userPk) + ); + vm.stopPrank(); + // Redeem PT and YT using all collateral (amountToWithdraw) (receiver is the ALE) + bytes + memory pendleRedeemData = hex"47f1de220000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e2400000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe00000000000000000000000000000000000000000000288531720083e475df5e0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000002661106360027ef6534d0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001244e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000f800000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000c2000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e1b4827368ce9d15ebb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000001c55e98e517590c50dd000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040301a40330000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000031373595f40ea48a7aab6cbcb0d377c6066e2dca0000000000000000000000000000000000000000000000000000000243d552b7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002442dd2d30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af00000000000000000000000000000000000000000000038abd31ca2eb218a1ba000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c6fdfc08401113d02280e27f53f4f38a19fbccc90000000000000000000000000000000000000000000000000000000487a9b512000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e1100000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000038de22a3175708d45e7c7c64cd78479c8b56f76e00000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e110000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ecf7e8f9cf4c98d12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002b3571f297a85100000000000002935041f3ea9c0e8238c0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000236b63f1e5d2f4f6515200000000000000000000000000000000000000000000272590b748547742ee910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000236b63f1e5d2f4f6515200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002647b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223139343330372e36353134353434323132222c22416d6f756e744f7574555344223a223139343833382e31393235323131343132222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313934353935303030323538353631323936353733333234222c2254696d657374616d70223a313734343333303337342c22526f7574654944223a2265306366333537362d653266312d343237642d383139302d396134303138396333613435222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22454b35577838614c4f34394f3675716a47736332505875564a653161424f466c56702f656d4261543843584a365a4c4a465a795037334b547143383732354739697548445168537035666c6565714a5652666c574949517a767631517641534a4f4d4733696d5447476c4b466f7267726c672f436d6c4c62313252626b52516f7a2b697267706748522f6237544b504f6c6459615141446579544d46316a6b6c4c667551356878346d45786d332f6342484d70597a38496c4859546e5a2f787a5945646a696e384139622b555a4d39445757743444454b515437642b687567426c356d4c2b5a6b47586b4e3732344b65625854667a2b6950382f52776c477637367941496779615136396c456d3439652f597150766e39346a2b63757155372f5347535239392b425431383436414346566f685951686a3743737662324347427863503145697a7676424d543453477073616a646d513d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(userPk); + vm.startPrank(userPk); + IERC20(pendleYT).approve(address(helper), amountToWithdraw); + + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleRedeemData), + dbrData + ); + + assertEq(SimpleERC20Escrow(userPkEscrow).balance(), 0); + assertApproxEqRel( + DOLA.balanceOf(userPk), + 190637534582110192477893 - debt, + 0.01 ether + ); + assertEq(IERC20(pendleYT).balanceOf(userPk), 0); + assertEq(market.debts(userPk), 0); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_AFTER_DEADLINE_deleveragePosition_Redeem_with_PT_and_YT_router() + public + { + test_leveragePosition_Mint_PT_and_YT_with_DOLA_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 1000000 ether); + vm.warp(block.timestamp + 180 days); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer( + userPk, + amountToWithdraw - IERC20(pendleYT).balanceOf(userPk) + ); + vm.stopPrank(); + // Redeem PT and YT using all collateral (amountToWithdraw) (receiver is the ALE) + bytes + memory pendleRedeemData = hex"47f1de220000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e2400000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe00000000000000000000000000000000000000000000288531720083e475df5e0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000002661106360027ef6534d0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001244e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000f800000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000c2000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e1b4827368ce9d15ebb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000001c55e98e517590c50dd000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040301a40330000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000031373595f40ea48a7aab6cbcb0d377c6066e2dca0000000000000000000000000000000000000000000000000000000243d552b7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002442dd2d30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af00000000000000000000000000000000000000000000038abd31ca2eb218a1ba000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c6fdfc08401113d02280e27f53f4f38a19fbccc90000000000000000000000000000000000000000000000000000000487a9b512000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e1100000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000038de22a3175708d45e7c7c64cd78479c8b56f76e00000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e110000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041ecf7e8f9cf4c98d12000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002b3571f297a85100000000000002935041f3ea9c0e8238c0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000236b63f1e5d2f4f6515200000000000000000000000000000000000000000000272590b748547742ee910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000236b63f1e5d2f4f6515200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002647b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223139343330372e36353134353434323132222c22416d6f756e744f7574555344223a223139343833382e31393235323131343132222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313934353935303030323538353631323936353733333234222c2254696d657374616d70223a313734343333303337342c22526f7574654944223a2265306366333537362d653266312d343237642d383139302d396134303138396333613435222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22454b35577838614c4f34394f3675716a47736332505875564a653161424f466c56702f656d4261543843584a365a4c4a465a795037334b547143383732354739697548445168537035666c6565714a5652666c574949517a767631517641534a4f4d4733696d5447476c4b466f7267726c672f436d6c4c62313252626b52516f7a2b697267706748522f6237544b504f6c6459615141446579544d46316a6b6c4c667551356878346d45786d332f6342484d70597a38496c4859546e5a2f787a5945646a696e384139622b555a4d39445757743444454b515437642b687567426c356d4c2b5a6b47586b4e3732344b65625854667a2b6950382f52776c477637367941496779615136396c456d3439652f597150766e39346a2b63757155372f5347535239392b425431383436414346566f685951686a3743737662324347427863503145697a7676424d543453477073616a646d513d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(userPk); + vm.startPrank(userPk); + uint256 ytBalBefore = IERC20(pendleYT).balanceOf(userPk); + + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleRedeemData), + dbrData + ); + assertEq(IERC20(pendleYT).balanceOf(userPk), ytBalBefore); + } + + function test_deleveragePosition_Swap_PT_to_DOLA_router() public { + test_leveragePosition_Swap_DOLA_for_PT_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e240000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e47660000000000000000000000000000000000000000000028b21966e97b0e57517a00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001500000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000261d5b9887bee02dd28b0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001304e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010400000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000ce00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000760000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001de6312b0ccd57caee1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000001c23f20a6666e93776b000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af000000000000000000000000000000000000000000000000000000023fd6d94e000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002402fc75a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000003847e414cccdd26eed6000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c6fdfc08401113d02280e27f53f4f38a19fbccc9000000000000000000000000000000000000000000000000000000047facc58c000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e1100000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000038de22a3175708d45e7c7c64cd78479c8b56f76e00000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e110000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004178b778d0424a735f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002ae9380163eb9e200000000000028ec522d336e9fdaf79d0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000232cee8d0000a385545d0000000000000000000000000000000000000000000026e081448a75e4a99e6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000232cee8d0000a385545d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002637b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223139333037372e373230303833393633222c22416d6f756e744f7574555344223a223139333739322e34393634323437383137222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313933323534303132333631383136353436313436323035222c2254696d657374616d70223a313734343333303734322c22526f7574654944223a2230656233346131612d636537382d343533302d613037312d393962616636313261633538222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22566a3369456546436d6b45784b6c35676a43337562353078576836724c647961733856755a3263507771772f31594f334b4662776c5355364d56314e794e4a5a74594e5a5147304a49657a6f494d2f7a4775345a466e74783739354e2b74646d6836315875536f77626c79704e754b3333796b4f6455376248436f4d2f62614b55467852753770417434672f3842332f46416d4f396f5731676c51676a6846504f476431324e5359727658343736666831526f6b467342732b2b55625455494c583255327154694679456b62664d7a77587a30386439787153427a71543777573638305053796336624332327268616f45762b6f50435a3253384d4c757152694e32796a4a5a3361487062437370757462525347742b6b504d62654c385155624d656f344165442f344759424c43592b47565a7354317165466f4e77576150324c71703556683350615650425a2b5663614b466157673d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b3e2c3ec88b1b4c0cd853f4321000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000002849eb11672558700b8a242ea5f877a3a2756f8a6879c5bb885874668ff252fdea822bea50e6af214c630000000000000000000000000000000000000000000000000000000067f8e6cd000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000030000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000afbb30322d80b572a7ff5aee89896d555732051d000000000000000000000000afbb30322d80b572a7ff5aee89896d555732051d00000000000000000000000000000000000000000002ee152257ecad790d4cbc000000000000000000000000000000000000000000000000012a2337a255a4f30000000000000000000000000000000000000000000000000c7d713b49da000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004187d8e83f8d3cf54b5be96da92d7f9be2eb61098ac118100edf2aabc8a3c9137a5b73e1bc74d2936c7210483899d1045fff5446de36a7d8a1a08b8cdfaa4128341c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(address(userPk)); + vm.startPrank(userPk); + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged (repay debt) + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapToDolaData), + dbrData + ); + + assertEq(SimpleERC20Escrow(userPkEscrow).balance(), 0); + // Dola balance is equal to collateral sold for DOLA minus debt + assertEq(DOLA.balanceOf(userPk), 189467723788496711906807 - debt); + assertEq(market.debts(address(userPk)), 0); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_fail_AFTER_DEADLINE_deleveragePosition_Swap_PT_to_DOLA_router() + public + { + test_leveragePosition_Swap_DOLA_for_PT_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 1000000 ether); + vm.warp(block.timestamp + 180 days); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e240000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e47660000000000000000000000000000000000000000000028b21966e97b0e57517a00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001500000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000261d5b9887bee02dd28b0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001304e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010400000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000ce00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000760000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001de6312b0ccd57caee1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000001c23f20a6666e93776b000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af000000000000000000000000000000000000000000000000000000023fd6d94e000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002402fc75a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000003847e414cccdd26eed6000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c6fdfc08401113d02280e27f53f4f38a19fbccc9000000000000000000000000000000000000000000000000000000047facc58c000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e1100000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000038de22a3175708d45e7c7c64cd78479c8b56f76e00000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e110000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004178b778d0424a735f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002ae9380163eb9e200000000000028ec522d336e9fdaf79d0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000232cee8d0000a385545d0000000000000000000000000000000000000000000026e081448a75e4a99e6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000232cee8d0000a385545d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002637b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223139333037372e373230303833393633222c22416d6f756e744f7574555344223a223139333739322e34393634323437383137222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313933323534303132333631383136353436313436323035222c2254696d657374616d70223a313734343333303734322c22526f7574654944223a2230656233346131612d636537382d343533302d613037312d393962616636313261633538222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22566a3369456546436d6b45784b6c35676a43337562353078576836724c647961733856755a3263507771772f31594f334b4662776c5355364d56314e794e4a5a74594e5a5147304a49657a6f494d2f7a4775345a466e74783739354e2b74646d6836315875536f77626c79704e754b3333796b4f6455376248436f4d2f62614b55467852753770417434672f3842332f46416d4f396f5731676c51676a6846504f476431324e5359727658343736666831526f6b467342732b2b55625455494c583255327154694679456b62664d7a77587a30386439787153427a71543777573638305053796336624332327268616f45762b6f50435a3253384d4c757152694e32796a4a5a3361487062437370757462525347742b6b504d62654c385155624d656f344165442f344759424c43592b47565a7354317165466f4e77576150324c71703556683350615650425a2b5663614b466157673d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b3e2c3ec88b1b4c0cd853f4321000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000002849eb11672558700b8a242ea5f877a3a2756f8a6879c5bb885874668ff252fdea822bea50e6af214c630000000000000000000000000000000000000000000000000000000067f8e6cd000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000030000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000afbb30322d80b572a7ff5aee89896d555732051d000000000000000000000000afbb30322d80b572a7ff5aee89896d555732051d00000000000000000000000000000000000000000002ee152257ecad790d4cbc000000000000000000000000000000000000000000000000012a2337a255a4f30000000000000000000000000000000000000000000000000c7d713b49da000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004187d8e83f8d3cf54b5be96da92d7f9be2eb61098ac118100edf2aabc8a3c9137a5b73e1bc74d2936c7210483899d1045fff5446de36a7d8a1a08b8cdfaa4128341c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + uint256 debt = market.debts(address(userPk)); + + vm.startPrank(userPk); + vm.expectRevert(PendlePTHelper.PendleSwapFailed.selector); + ale.deleveragePosition( + debt, // dola redeemed to be deleveraged (repay debt) + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapToDolaData), + dbrData + ); + } + + function test_deleveragePosition_sellDBR() public { + test_leveragePosition_Swap_DOLA_for_PT_router(); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + uint256 debt = market.debts(address(userPk)); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit( + block.timestamp, + v, + r, + s + ); + + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( + dbr.balanceOf(userPk), + 10000, + 0 + ); // sell all DBR + bytes memory swapData; + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the ALE) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e240000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e47660000000000000000000000000000000000000000000028b21966e97b0e57517a00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000001500000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000261d5b9887bee02dd28b0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000001304e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000010400000000000000000000000000000000000000000000000000000000000000d40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000ce00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000760000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001de6312b0ccd57caee1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000001c23f20a6666e93776b000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af000000000000000000000000000000000000000000000000000000023fd6d94e000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040d90ce4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000007d10a8734d985dbb3ad91fce9c48ccc78b9f8b94000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002402fc75a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040f59b1df7000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000066a9893cc07d91d95644aedd05d03f95e1dba8af0000000000000000000000000000000000000000000003847e414cccdd26eed6000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba30000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000000012c0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c6fdfc08401113d02280e27f53f4f38a19fbccc9000000000000000000000000000000000000000000000000000000047facc58c000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e1100000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000038de22a3175708d45e7c7c64cd78479c8b56f76e00000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e110000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004178b778d0424a735f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000002ae9380163eb9e200000000000028ec522d336e9fdaf79d0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000232cee8d0000a385545d0000000000000000000000000000000000000000000026e081448a75e4a99e6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000232cee8d0000a385545d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002637b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223139333037372e373230303833393633222c22416d6f756e744f7574555344223a223139333739322e34393634323437383137222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313933323534303132333631383136353436313436323035222c2254696d657374616d70223a313734343333303734322c22526f7574654944223a2230656233346131612d636537382d343533302d613037312d393962616636313261633538222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22566a3369456546436d6b45784b6c35676a43337562353078576836724c647961733856755a3263507771772f31594f334b4662776c5355364d56314e794e4a5a74594e5a5147304a49657a6f494d2f7a4775345a466e74783739354e2b74646d6836315875536f77626c79704e754b3333796b4f6455376248436f4d2f62614b55467852753770417434672f3842332f46416d4f396f5731676c51676a6846504f476431324e5359727658343736666831526f6b467342732b2b55625455494c583255327154694679456b62664d7a77587a30386439787153427a71543777573638305053796336624332327268616f45762b6f50435a3253384d4c757152694e32796a4a5a3361487062437370757462525347742b6b504d62654c385155624d656f344165442f344759424c43592b47565a7354317165466f4e77576150324c71703556683350615650425a2b5663614b466157673d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9b3e2c3ec88b1b4c0cd853f4321000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000002849eb11672558700b8a242ea5f877a3a2756f8a6879c5bb885874668ff252fdea822bea50e6af214c630000000000000000000000000000000000000000000000000000000067f8e6cd000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000030000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a34970000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000afbb30322d80b572a7ff5aee89896d555732051d000000000000000000000000afbb30322d80b572a7ff5aee89896d555732051d00000000000000000000000000000000000000000002ee152257ecad790d4cbc000000000000000000000000000000000000000000000000012a2337a255a4f30000000000000000000000000000000000000000000000000c7d713b49da000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004187d8e83f8d3cf54b5be96da92d7f9be2eb61098ac118100edf2aabc8a3c9137a5b73e1bc74d2936c7210483899d1045fff5446de36a7d8a1a08b8cdfaa4128341c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + + vm.startPrank(userPk); + dbr.approve(address(ale), dbr.balanceOf(userPk)); + ale.deleveragePosition( + debt, + address(market), + address(0), + amountToWithdraw, + swapData, + permit, + abi.encode(address(market), uint(100000), pendleSwapToDolaData), + dbrData + ); + + assertEq(SimpleERC20Escrow(userPkEscrow).balance(), 0); + + assertEq(dbr.balanceOf(userPk), 0); + // Dola balance is greater than collateral sold for DOLA minus debt because we also sold DBR + assertGt(DOLA.balanceOf(userPk), 189384718117561736955569 - debt); + assertEq(market.debts(address(userPk)), 0); + assertEq(DOLA.balanceOf(address(ale)), 0); + } + + function test_fail_InsufficientPT_after_swap() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + vm.expectRevert(PendlePTHelper.InsufficientPT.selector); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode( + address(market), + (initialDolaAmount * 1.1 ether) / 1 ether, + pendleDataInitial + ) + ); + } + + function test_fail_InsufficientPT_after_mint() public { + _mintInitialDolaAmountToUser(); + bytes + memory pendleMintData = hex"d0f423850000000000000000000000006218d3d6c01f8077f06519d8f5d31f935f3c3c530000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000000000000000000000000a960112b065b45d415c0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000152d02c7e14af68000000000000000000000000000004c9edd5852cd905f086c759e8383e09bff1e68b3000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b64e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000066000000000000000000000000000000000000000000000000000000000000008a000000000000000000000000000000000000000000000000000000000000005a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000004c9edd5852cd905f086c759e8383e09bff1e68b3000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff0000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000038de22a3175708d45e7c7c64cd78479c8b56f76e000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e1100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000152d02c7e14af680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004088e563110000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c6fdfc08401113d02280e27f53f4f38a19fbccc90000000000000000000000000000000000000000000015280b7c36b1c460d75b00000000000000000000000066a1e37c9b0eaddca17d3662d6c05f4decf3e110000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca30000000000000000000000000000000000000000000000000000000000000040d90ce49100000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000002950460e2b9529d0e00284a5fa2d7bdf3fa4d72000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000004c9edd5852cd905f086c759e8383e09bff1e68b3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001744060f9f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000016334a784e6e8ef000000000000152c023123ac67d66fc5000000000000000000000000865377367054516e17014ccded1e7d814edc9ce40000000000000000000000004c9edd5852cd905f086c759e8383e09bff1e68b3000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94600000000000000000000000000000000000000000000152d02c7e14af68000000000000000000000000000000000000000000000000010f001c0e956b978596a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000152d02c7e14af680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002647b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223130303034392e3539363630353032353434222c22416d6f756e744f7574555344223a2239393937322e3732313331353438373937222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a223939393831353130383236313931303837383930333733222c2254696d657374616d70223a313734333730323332352c22526f7574654944223a2266313138373235332d356131632d343865352d393933342d326566393932653734393463222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2259524b6a7130684373542f625a774b4d72595267694f416d4d7039716e653470306335496776614a4e4b45625259565475574452594c57344d65386274572f58436167584637524249626b7430775868555039386c6c666f32644945714362544868456737656d4336754c7a69587334766b38656450717365586d6533465132696b7471564851536d576e6355564e2f5a56696550652f6230653539695263374a4c55752f6575312f626c71554d53694a684f62526d644b57716f3663362f642f72576e55474b447469595039547938344e4d674c5855554234634f6d324a4a66377759697956396231314c497875644d34367333742f6638776635516c67626c64544d4949755056334954396647626d794c36344d6754415550672b72765374635a49622f7732696a783678724c652f546f79355230795743686c59483068796a6154636a304838794a4175704d5867614d5672673d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.prank(userPk); + vm.expectRevert(PendlePTHelper.InsufficientPT.selector); + helper.convertToCollateral( + initialDolaAmount, + abi.encode(address(market), initialDolaAmount, pendleMintData) + ); + } + + function test_withdrawAndConvertFromCollateral_SWAP() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(helper), + userPk, + amountToWithdraw, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + IPendleHelper.Permit memory permit = IPendleHelper.Permit( + block.timestamp, + v, + r, + s + ); + + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the User) + bytes + memory pendleSwapToDolaData = hex"594a88cc0000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf000000000000000000000000b162b764044697cf03617c2efbcb1f42e31e4766000000000000000000000000000000000000000000001568777a51f893b0d35300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000ac0000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000001306bd7c7fac6e94e5eb0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b50000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008c4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012803ce5f8fd8089d15500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000169c51f16ceb20b00000000000015902c15a1c36c4259e80000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000012803ce5f8fd8089d15500000000000000000000000000000000000000000000136827ad119647d550ea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000012803ce5f8fd8089d15500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002667b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223130313838332e3236373639303730313531222c22416d6f756e744f7574555344223a223130313930382e3731333137303936343237222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313031383239323033393039383439363131363535363536222c2254696d657374616d70223a313734333730323730362c22526f7574654944223a2231383566623136362d633136622d343238322d626362302d306163623338626639643331222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a2258747579587a36366c49574e69624f4c68436f4c446f336d614e5a4f384853726b4e6f7173627732495a6b536169393037384b547a2f4f6b776553664672615a4236754c43717934324b463442682f4c4454556a367a4a6533744b4a2f4a576b504248764846747644694b4b6a6332337373305561307a79474933687544626330547552386f516645624c2b614a4575776765334d30654444616c30526561537a3546586b4c3265594a4d6b6151717546712f6947775a2f6d6c394e62393453765a704d6a5a6b5244725643426850334a4a66563130787450667a306f4c52737a37575a3333324c73726e3735684d4e756f6b6443354233507a4c6855684b446b6876754d6a4657696a5a4572386d66784c565a6f425a67467676744b344650355443497671315952527170567934715075626d4b644b676a2f6331695a414d53433533312b662b307a6664506f3732617648516d413d3d227d7d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + assertEq(DOLA.balanceOf(userPk), 0); + + vm.startPrank(userPk); + uint256 dolaAmount = helper.withdrawAndConvertFromCollateral( + amountToWithdraw, + userPk, + permit, + abi.encode(address(market), 99000 ether, pendleSwapToDolaData) + ); + assertEq(DOLA.balanceOf(userPk), dolaAmount); + } + + function test_withdrawAndConvertFromCollateral_REDEEM() public { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + gibDBR(userPk, 20000 ether); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(helper), + userPk, + amountToWithdraw, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + IPendleHelper.Permit memory permit = IPendleHelper.Permit( + block.timestamp, + v, + r, + s + ); + + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the User) + bytes + memory pendleRedeemToDolaData = hex"47f1de220000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf0000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000000000000000000000001568777a51f893b0d3530000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000ab32e707b061de22ac10000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b50000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008c4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012ba5fb7e19fc3b2689b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000016e35e8e0553a6700000000000015d3ee18a90c7a66fb0f0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000012ba5fb7e19fc3b2689b0000000000000000000000000000000000000000000011765813ba7061ebfc0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000012ba5fb7e19fc3b2689b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002667b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223130333037332e3730303636333839323535222c22416d6f756e744f7574555344223a223130333037382e3034393837323436383733222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313033303739313135373838343636373931303534303935222c2254696d657374616d70223a313734333730323930302c22526f7574654944223a2233353936393166362d616466392d343435632d383166632d323336363432353264636133222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22594e6159306479445a574176524466743951662b424a49676b31747258372b524d59586a6f6a7577386550593158694470684a30626262756e7043745262447144534935377a7541384f63544f306f67356f38665744664d477535653959484c486450434668502f3050352b4a357a594931656b36747449615153695332673034716b51447451717834454166672b57483876305a5936306c315a3952662f6c326d555577506657584b4c496435616a313665447a5274306b6b7173493776344e7a397641533245367662446268613455502f356f386848413167615067532f7a377477686a7a6a35774c5a594b62705a454c5a5a31524175632f374a4a3133664b37593439737744424e6e656d474a637a784e7a786b7570495941646c7950626d724e783962623731797a2b77686c68744446617435427651456e434a674b6b573258726f514b4576764a4a53556b6973587648773d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer(userPk, amountToWithdraw); + vm.stopPrank(); + + assertEq(DOLA.balanceOf(userPk), 0); + vm.startPrank(userPk); + IERC20(pendleYT).approve(address(helper), amountToWithdraw); + uint256 dolaAmount = helper.withdrawAndConvertFromCollateral( + amountToWithdraw, + userPk, + permit, + abi.encode( + address(market), + (initialDolaAmount * 98) / 100, + pendleRedeemToDolaData + ) + ); + assertEq(DOLA.balanceOf(userPk), dolaAmount); + assertEq(IERC20(pendleYT).balanceOf(userPk), 0); + } + + function test_withdrawAndConvertFromCollateral_REDEEM_AFTER_DEADLINE() + public + { + _mintInitialDolaAmountToUser(); + + vm.prank(userPk); + helper.convertToCollateralAndDeposit( + initialDolaAmount, + userPk, + abi.encode(address(market), 0, pendleDataInitial) + ); + + gibDBR(userPk, 1000000 ether); + + uint256 amountToWithdraw = SimpleERC20Escrow(userPkEscrow).balance(); + vm.warp(block.timestamp + 180 days); + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(helper), + userPk, + amountToWithdraw, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + IPendleHelper.Permit memory permit = IPendleHelper.Permit( + block.timestamp, + v, + r, + s + ); + + // Swap all collateral (amountToWithdraw) to DOLA (receiver is the User) + bytes + memory pendleRedeemToDolaData = hex"47f1de220000000000000000000000007e5f4552091a69125d5dfcb7b8c2659029395bdf0000000000000000000000001de6ff19fda7496ddc12f2161f6ad6427c52abbe000000000000000000000000000000000000000000001568777a51f893b0d3530000000000000000000000000000000000000000000000000000000000000080000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000ab32e707b061de22ac10000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000313e7ef7d52f5c10ac04ebaa4d33cdc68634c21200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131b5fae19ea4f9d964eac0408e4408b66337b50000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008c4e21fd0e900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946000000000000000000000000000000000000000000000000000000007fffffff00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040d90ce491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012ba5fb7e19fc3b2689b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000016e35e8e0553a6700000000000015d3ee18a90c7a66fb0f0000000000000000000000009d39a5de30e57443bff2a8307a4256c8797a3497000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000000000000000000000000012ba5fb7e19fc3b2689b0000000000000000000000000000000000000000000011765813ba7061ebfc0c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000f4a1d7fdf4890be35e71f3e0bbc4a0ec377eca300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000012ba5fb7e19fc3b2689b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002667b22536f75726365223a2250656e646c65222c22416d6f756e74496e555344223a223130333037332e3730303636333839323535222c22416d6f756e744f7574555344223a223130333037382e3034393837323436383733222c22526566657272616c223a22222c22466c616773223a302c22416d6f756e744f7574223a22313033303739313135373838343636373931303534303935222c2254696d657374616d70223a313734333730323930302c22526f7574654944223a2233353936393166362d616466392d343435632d383166632d323336363432353264636133222c22496e74656772697479496e666f223a7b224b65794944223a2231222c225369676e6174757265223a22594e6159306479445a574176524466743951662b424a49676b31747258372b524d59586a6f6a7577386550593158694470684a30626262756e7043745262447144534935377a7541384f63544f306f67356f38665744664d477535653959484c486450434668502f3050352b4a357a594931656b36747449615153695332673034716b51447451717834454166672b57483876305a5936306c315a3952662f6c326d555577506657584b4c496435616a313665447a5274306b6b7173493776344e7a397641533245367662446268613455502f356f386848413167615067532f7a377477686a7a6a35774c5a594b62705a454c5a5a31524175632f374a4a3133664b37593439737744424e6e656d474a637a784e7a786b7570495941646c7950626d724e783962623731797a2b77686c68744446617435427651456e434a674b6b573258726f514b4576764a4a53556b6973587648773d3d227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + vm.startPrank(pendleYTHolder); + IERC20(pendleYT).transfer(userPk, amountToWithdraw); + vm.stopPrank(); + + assertEq(DOLA.balanceOf(userPk), 0); + vm.startPrank(userPk); + IERC20(pendleYT).approve(address(helper), amountToWithdraw); + + helper.withdrawAndConvertFromCollateral( + amountToWithdraw, + userPk, + permit, + abi.encode( + address(market), + (initialDolaAmount * 98) / 100, + pendleRedeemToDolaData + ) + ); + assertGt(DOLA.balanceOf(userPk), (initialDolaAmount * 98) / 100); + // Didn't transfer token even if approval granted because after maturity + assertEq(IERC20(pendleYT).balanceOf(userPk), amountToWithdraw); + } + function test_Access_Control_convertToCollateral_ALE() public { + vm.expectRevert(PendlePTHelper.NotALE.selector); + helper.convertToCollateral( + userPk, + initialDolaAmount, + abi.encode(address(market), initialDolaAmount, pendleDataInitial) + ); + } + + function test_Access_Control_convertFromCollateral_ALE() public { + vm.expectRevert(PendlePTHelper.NotALE.selector); + helper.convertFromCollateral( + userPk, + initialDolaAmount, + abi.encode(address(market), initialDolaAmount, pendleDataInitial) + ); + } + + function test_updateMarketHelper_if_helper_not_address_zero() public { + vm.prank(gov); + ale.updateMarketHelper(address(market), address(1)); + (, , IPendleHelper helper, ) = ale.markets(address(market)); + assertEq(address(helper), address(1)); + } + + function test_updateMarketHelper_if_market_not_set() public { + vm.expectRevert( + abi.encodeWithSelector(ALEV2.MarketNotSet.selector, address(2)) + ); + vm.prank(gov); + ale.updateMarketHelper(address(2), address(1)); + } + + function test_updateMarketHelper_fails_if_helper_not_set() public { + vm.expectRevert(ALEV2.InvalidHelperAddress.selector); + vm.prank(gov); + ale.updateMarketHelper(address(market), address(0)); + } + + function _getMaxBorrowAmount( + uint amountCollat + ) internal view returns (uint) { + return + (amountCollat * + oracle.viewPrice(address(pendlePT), 0) * + market.collateralFactorBps()) / + 10_000 / + 1e18; + } +} diff --git a/test/util/aleTests/ALEPendlePTUSDeMock.t.sol b/test/util/aleTests/ALEPendlePTUSDeMock.t.sol new file mode 100644 index 00000000..06d2c0f3 --- /dev/null +++ b/test/util/aleTests/ALEPendlePTUSDeMock.t.sol @@ -0,0 +1,568 @@ +pragma solidity ^0.8.13; + +import {PendlePTHelper} from "src/util/PendlePTHelper.sol"; +import "test/marketForkTests/PendlePTsUSDe27Mar25MarketForkTest.t.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; +import {SimpleERC20Escrow} from "src/escrows/SimpleERC20Escrow.sol"; +import {MockPendleRouter} from "test/mocks/MockPendleRouter.sol"; + +interface IFlashMinter { + function setMaxFlashLimit(uint256 _maxFlashLimit) external; +} + +contract ALEV2PTUSDeMockTest is PendlePTsUSDe27Mar25MarketForkTest { + MockPendleRouter.ApproxParams emptyApprox; + MockPendleRouter.TokenInput emptyTokenInput; + MockPendleRouter.TokenOutput emptyTokenOutput; + MockPendleRouter.LimitOrderData emptyLimit; + ALEV2 ale; + IFlashMinter flash; + address userPk = vm.addr(1); + PendlePTHelper helper; + address userPkEscrow; + MockPendleRouter mockRouter; + + address pendleRouter = address(0x888888888889758F76e7103c6CbF23ABbF58F946); + address pendleYT = address(0x96512230bF0Fa4E20Cf02C3e8A7d983132cd2b9F); + address marketPT = address(0xcDd26Eb5EB2Ce0f203a84553853667aE69Ca29Ce); + + // Pendle Router Mock + address pendleYTHolder = + address(0x9844c3688dAaA98De18fBe52499A6B152236896b); + + function setUp() public override { + super.setUp(); + + mockRouter = new MockPendleRouter( + address(DOLA), + address(pendlePT), + address(pendleYT) + ); + + vm.startPrank(gov); + DOLA.mint(address(this), 100000 ether); + + ale = new ALEV2(triDBRAddr); + + helper = new PendlePTHelper( + gov, + pauseGuardian, + address(DOLA), + address(mockRouter), + address(ale) + ); + ale.setMarket(address(market), address(DOLA), address(helper), false); + helper.setMarket(address(market), address(pendlePT), pendleYT); + + flash = IFlashMinter(address(ale.flash())); + flash.setMaxFlashLimit(10000000 ether); + DOLA.addMinter(address(flash)); + borrowController.allow(address(ale)); + vm.stopPrank(); + userPkEscrow = address(market.predictEscrow(userPk)); + + vm.label(address(DOLA), "DOLA"); + vm.label(address(pendlePT), "pendlePT"); + vm.label(address(pendleYT), "pendleYT"); + // Fill Pendle Router Mock + vm.prank(gov); + DOLA.mint(address(mockRouter), 10000000 ether); + vm.prank(address(pendlePTHolder)); + IERC20(pendlePT).transfer(address(mockRouter), 10000000 ether); + vm.prank(address(pendleYTHolder)); + IERC20(pendleYT).transfer(address(mockRouter), 10000000 ether); + } + function test_leveragePosition_Mint_PT_and_YT_with_DOLA() public { + vm.prank(gov); + DOLA.mint(userPk, 1000000 ether); + + _transformAndDeposit(1000000 ether); + + gibDBR(userPk, 20000 ether); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + vm.startPrank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + _getPermitForBorrow(maxBorrowAmount), + _encodeMintPt(maxBorrowAmount), + dbrData + ); + vm.stopPrank(); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + maxBorrowAmount, // 1:1 ratio DOLA/PT + 1e14 // 0.01% Delta + ); + assertEq(IERC20(pendleYT).balanceOf(userPk), maxBorrowAmount); + } + + function test_leveragePosition_Swap_DOLA_for_PT() public { + vm.prank(gov); + DOLA.mint(userPk, 1000000 ether); + + _transformAndDeposit(1000000 ether); + + gibDBR(userPk, 20000 ether); + + uint256 ptAmount = SimpleERC20Escrow(userPkEscrow).balance(); + uint maxBorrowAmount = _getMaxBorrowAmount(ptAmount); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + vm.startPrank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + _getPermitForBorrow(maxBorrowAmount), + _encodeSwapForPT(maxBorrowAmount), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertApproxEqRel( + SimpleERC20Escrow(userPkEscrow).balance(), + ptAmount + maxBorrowAmount, // 1:1 ratio DOLA/PT + 1e14 // 0.01% Delta + ); + } + + function test_leveragePosition_buyDBR(uint amount) public { + vm.assume(amount < 5000000 ether); + vm.assume(amount > 0.001 ether); + vm.prank(gov); + DOLA.mint(userPk, amount); + + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), amount); + helper.convertToCollateralAndDeposit( + amount, + userPk, + abi.encode( + address(market), + 0, + abi.encodeWithSelector( + MockPendleRouter.swapExactTokenForPt.selector, + address(helper), + address(0), + amount, + emptyApprox, + emptyTokenInput, + emptyLimit + ) + ) + ); + vm.stopPrank(); + + uint256 lpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + + uint maxBorrowAmount = _getMaxBorrowAmount(lpAmount); + + // Calculate the amount of DOLA needed to borrow to buy the DBR needed to cover for the borrowing period + (uint256 dolaForDBR, uint256 dbrAmount) = ale + .approximateDolaAndDbrNeeded(maxBorrowAmount, 15 days, 8); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( + dolaForDBR, + (dbrAmount * 90) / 100, + 0 + ); + + vm.startPrank(userPk); + ale.leveragePosition( + maxBorrowAmount, + address(market), + address(0), + swapData, + _getPermitForBorrow(maxBorrowAmount + dolaForDBR), + _encodeSwapForPT(maxBorrowAmount), + dbrData + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertEq( + SimpleERC20Escrow(userPkEscrow).balance(), + lpAmount + maxBorrowAmount + ); + assertGt(dbr.balanceOf(userPk), (dbrAmount * 95) / 100); + } + + function test_depositAndLeveragePosition_DOLA(uint amount) public { + vm.assume(amount < 5000000 ether); + vm.assume(amount > 0.001 ether); + vm.prank(gov); + DOLA.mint(userPk, amount); + uint256 initialDolaDeposit = amount / 10; + + _transformAndDeposit(amount - initialDolaDeposit); + + uint256 lpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 20000 ether); + + uint maxBorrowAmount = _getMaxBorrowAmount(lpAmount); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + vm.startPrank(userPk); + DOLA.approve(address(ale), initialDolaDeposit); + ale.depositAndLeveragePosition( + initialDolaDeposit, + maxBorrowAmount, + address(market), + address(0), + swapData, + _getPermitForBorrow(maxBorrowAmount), + _encodeSwapForPT(maxBorrowAmount + initialDolaDeposit), + dbrData, + false + ); + + assertEq(DOLA.balanceOf(userPk), 0, "DOLA BALANCE"); + assertEq( + SimpleERC20Escrow(userPkEscrow).balance(), + lpAmount + maxBorrowAmount + initialDolaDeposit + ); + } + + function test_depositAndLeveragePosition_PT(uint256 amount) public { + vm.assume(amount < 5000000 ether); + vm.assume(amount > 0.001 ether); + vm.prank(gov); + DOLA.mint(userPk, amount); + uint256 initialDolaDeposit = amount / 10; + + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), amount); + uint256 initialLpAmount = helper.convertToCollateral( + initialDolaDeposit, + _encodeSwapForPT(initialDolaDeposit) + ); + helper.convertToCollateralAndDeposit( + amount - initialDolaDeposit, + userPk, + _encodeSwapForPT(amount - initialDolaDeposit) + ); + vm.stopPrank(); + + uint256 lpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + gibDBR(userPk, 20000 ether); + + uint maxBorrowAmount = _getMaxBorrowAmount(lpAmount); + + bytes memory swapData; + + ALEV2.DBRHelper memory dbrData; + + vm.startPrank(userPk); + IERC20(address(pendlePT)).approve(address(ale), initialLpAmount); + ale.depositAndLeveragePosition( + initialLpAmount, + maxBorrowAmount, + address(market), + address(0), + swapData, + _getPermitForBorrow(maxBorrowAmount), + _encodeSwapForPT(maxBorrowAmount), + dbrData, + true + ); + + assertEq(DOLA.balanceOf(userPk), 0); + assertEq( + SimpleERC20Escrow(userPkEscrow).balance(), + lpAmount + maxBorrowAmount + initialLpAmount + ); + } + + function test_deleveragePosition_Redeem_with_PT_and_YT( + uint256 lpAmount + ) public { + test_leveragePosition_Mint_PT_and_YT_with_DOLA(); + + uint256 totalLpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + vm.assume(lpAmount > 0.0001 ether); + vm.assume(lpAmount <= totalLpAmount); + uint256 amountToWithdraw = lpAmount / 2; + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + + uint256 ytBalBefore = IERC20(pendleYT).balanceOf(userPk); + if (amountToWithdraw > ytBalBefore) { + vm.prank(pendleYTHolder); + IERC20(pendleYT).transfer(userPk, amountToWithdraw - ytBalBefore); + } + + uint256 ytBalAfter = IERC20(pendleYT).balanceOf(userPk); + vm.startPrank(userPk); + IERC20(pendleYT).approve(address(helper), type(uint256).max); + + ale.deleveragePosition( + amountToWithdraw / 2, // dola redeemed to be deleveraged + address(market), + address(0), + amountToWithdraw, + swapData, + _getPermitWithdraw(amountToWithdraw), + _encodeRedeem(amountToWithdraw), + dbrData + ); + + assertEq( + SimpleERC20Escrow(userPkEscrow).balance(), + totalLpAmount - amountToWithdraw + ); + assertApproxEqAbs(DOLA.balanceOf(userPk), amountToWithdraw / 2, 1); + assertEq( + IERC20(pendleYT).balanceOf(userPk), + ytBalAfter - amountToWithdraw + ); + } + + function test_deleveragePosition_Swap_PT_to_DOLA(uint256 lpAmount) public { + test_leveragePosition_Swap_DOLA_for_PT(); + + uint256 totalLpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + vm.assume(lpAmount > 0.0001 ether); + vm.assume(lpAmount <= totalLpAmount); + uint256 amountToWithdraw = lpAmount / 2; + + ALEV2.DBRHelper memory dbrData; + bytes memory swapData; + + vm.startPrank(userPk); + ale.deleveragePosition( + amountToWithdraw / 2, // dola redeemed to be deleveraged + address(market), + address(0), + amountToWithdraw, + swapData, + _getPermitWithdraw(amountToWithdraw), + _encodeSwapForDola(amountToWithdraw), + dbrData + ); + + assertEq( + SimpleERC20Escrow(userPkEscrow).balance(), + totalLpAmount - amountToWithdraw + ); + assertApproxEqAbs(DOLA.balanceOf(userPk), amountToWithdraw / 2, 1); + } + + function test_deleveragePosition_sellDBR(uint256 lpAmount) public { + test_leveragePosition_Swap_DOLA_for_PT(); + uint256 totalLpAmount = SimpleERC20Escrow(userPkEscrow).balance(); + vm.assume(lpAmount > 0.0001 ether); + vm.assume(lpAmount <= totalLpAmount); + uint256 amountToWithdraw = lpAmount; + + uint256 dolaRedeemed = amountToWithdraw; + + uint256 debt = market.debts(address(userPk)); + uint256 amountToRepay; + if (debt < dolaRedeemed) { + amountToRepay = debt; + } else { + amountToRepay = dolaRedeemed; + } + + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( + dbr.balanceOf(userPk), + 10000, + 0 + ); // sell all DBR + bytes memory swapData; + + vm.startPrank(userPk); + dbr.approve(address(ale), dbr.balanceOf(userPk)); + ale.deleveragePosition( + amountToRepay, + address(market), + address(0), + amountToWithdraw, + swapData, + _getPermitWithdraw(amountToWithdraw), + _encodeSwapForDola(amountToWithdraw), + dbrData + ); + + assertEq( + SimpleERC20Escrow(userPkEscrow).balance(), + totalLpAmount - amountToWithdraw + ); + // Dbrs have also been sold + if (debt < dolaRedeemed) { + // Dola left are more than the debt (plus the DBR sold) + assertGt(DOLA.balanceOf(userPk), dolaRedeemed - amountToRepay); + } else { + // only DBR sold + assertGt(DOLA.balanceOf(userPk), 0); + } + + assertEq(dbr.balanceOf(userPk), 0); + } + + function _transformAndDeposit(uint amount) internal { + vm.startPrank(userPk, userPk); + DOLA.approve(address(helper), amount); + helper.convertToCollateralAndDeposit( + amount, + userPk, + _encodeSwapForPT(amount) + ); + vm.stopPrank(); + } + + function _getPermitForBorrow( + uint amount + ) internal view returns (ALEV2.Permit memory permit) { + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + permit = ALEV2.Permit(block.timestamp, v, r, s); + } + + function _getPermitWithdraw( + uint256 amountToWithdraw + ) internal view returns (ALEV2.Permit memory permit) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + 1, + keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ) + ); + return ALEV2.Permit(block.timestamp, v, r, s); + } + + function _encodeSwapForPT( + uint amount + ) internal view returns (bytes memory) { + return + abi.encode( + address(market), + 0, + abi.encodeWithSelector( + MockPendleRouter.swapExactTokenForPt.selector, + address(helper), + address(0), + amount, + emptyApprox, + emptyTokenInput, + emptyLimit + ) + ); + } + + function _encodeSwapForDola( + uint amount + ) internal view returns (bytes memory) { + return + abi.encode( + address(market), + uint(0), + abi.encodeWithSelector( + MockPendleRouter.swapExactPtForToken.selector, + address(ale), + address(0), + amount, + emptyTokenOutput, + emptyLimit + ) + ); + } + + function _encodeMintPt(uint amount) internal view returns (bytes memory) { + return + abi.encode( + address(market), + 0, + abi.encodeWithSelector( + MockPendleRouter.mintPyFromToken.selector, + address(helper), + address(0), + amount, + emptyTokenInput + ) + ); + } + + function _encodeRedeem( + uint256 amount + ) internal view returns (bytes memory) { + return + abi.encode( + address(market), + uint(0), + abi.encodeWithSelector( + MockPendleRouter.redeemPyToToken.selector, + address(ale), + address(0), + amount, + emptyTokenOutput + ) + ); + } + + function _getMaxBorrowAmount( + uint amountCollat + ) internal view returns (uint) { + return + (amountCollat * + oracle.viewPrice(address(pendlePT), 0) * + market.collateralFactorBps()) / + 10_000 / + 1e18; + } +} diff --git a/test/util/aleTests/ALESDolascrvUSD.t.sol b/test/util/aleTests/ALESDolascrvUSD.t.sol index ef554f70..b099e555 100644 --- a/test/util/aleTests/ALESDolascrvUSD.t.sol +++ b/test/util/aleTests/ALESDolascrvUSD.t.sol @@ -5,7 +5,7 @@ import {CurveSDolaLPHelperDynamic} from "src/util/CurveSDolaLPHelperDynamic.sol" import "test/marketForkTests/SDolascrvUSDConvexMarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {ALEBaseSDolaLPDynTest, IFlashMinter} from "test/util/aleTests/ALEBaseSDolaLPDyn.sol"; contract ALESDolascrvUSDTest is @@ -16,18 +16,16 @@ contract ALESDolascrvUSDTest is super.setUp(); curvePool = ICurvePool(sDolascrvUSD); - helper = new CurveSDolaLPHelperDynamic( - gov, - pauseGuardian, - address(DOLA), - sDolaAddr + helper = CurveSDolaLPHelperDynamic( + curveSDolaLPHelperDynamicAddr ); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 1, 2, address(0)); - ale = ALE(payable(aleAddr)); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); + borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALESDolascrvUSDYearnV2.t.sol b/test/util/aleTests/ALESDolascrvUSDYearnV2.t.sol index 89581b49..2a92f446 100644 --- a/test/util/aleTests/ALESDolascrvUSDYearnV2.t.sol +++ b/test/util/aleTests/ALESDolascrvUSDYearnV2.t.sol @@ -5,7 +5,7 @@ import {CurveSDolaLPHelperDynamic} from "src/util/CurveSDolaLPHelperDynamic.sol" import "test/marketForkTests/SDolascrvUSDYearnV2MarketForkTest.t.sol"; import {console} from "forge-std/console.sol"; import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YearnVaultV2Helper, IYearnVaultV2} from "src/util/YearnVaultV2Helper.sol"; import {ALEBaseSDolaLPDynYearnV2Test} from "test/util/aleTests/ALEBaseSDolaLPDynYearnV2.sol"; @@ -16,19 +16,17 @@ contract ALESDolascrvUSDYearnV2Test is function setUp() public override { super.setUp(); curvePool = ICurvePool(sDolascrvUSD); - helper = new CurveSDolaLPHelperDynamic( - gov, - pauseGuardian, - address(DOLA), - sDolaAddr + helper = CurveSDolaLPHelperDynamic( + curveSDolaLPHelperDynamicAddr ); vault = IYearnVaultV2(yearn); vm.startPrank(gov); DOLA.mint(address(this), 100000 ether); helper.setMarket(address(market), address(curvePool), 1, 2, yearn); - ale = ALE(payable(aleAddr)); + ale = ALEV2(payable(aleV2Addr)); ale.setMarket(address(market), address(DOLA), address(helper), false); + borrowController.allow(address(ale)); vm.stopPrank(); userPkEscrow = address(market.predictEscrow(userPk)); diff --git a/test/util/aleTests/ALEsFrax4626HelperForkTest.t.sol b/test/util/aleTests/ALEsFrax4626HelperForkTest.t.sol index 4ab0113c..79939695 100644 --- a/test/util/aleTests/ALEsFrax4626HelperForkTest.t.sol +++ b/test/util/aleTests/ALEsFrax4626HelperForkTest.t.sol @@ -7,9 +7,9 @@ import "src/DBR.sol"; import {Market, IBorrowController} from "src/Market.sol"; import {Oracle, IChainlinkFeed} from "src/Oracle.sol"; import {Fed, IMarket} from "src/Fed.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {ERC4626Helper, IERC4626} from "src/util/ERC4626Helper.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {IMultiMarketConvertHelper} from "src/interfaces/IMultiMarketConvertHelper.sol"; import {console} from "forge-std/console.sol"; import {BaseHelperForkTest, IERC4626, MockExchangeProxy} from "test/util/aleTests/BaseHelperForkTest.t.sol"; import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; @@ -43,7 +43,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { IChainlinkFeed feed; BorrowController borrowController; - address sFraxHolder = 0x440888714A6afeD60ff44e9975A96E6a36f7Fac4; + address sFraxHolder = 0xBc2F0Ebc412647C7d4EC8FFD88Ca84Bc5b32C8cC; address fraxHolder = 0x5E583B6a1686f7Bc09A6bBa66E852A7C80d36F00; //ERC-20s @@ -56,14 +56,14 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { Fed fed; MockExchangeProxy exchangeProxy; - ALE ale; + ALEV2 ale; IFlashMinter flash; ERC4626Helper helper; //Variables uint collateralFactorBps; function getBlockNumber() public view override returns (uint256) { - return 20590050; + return 22241605; } function setUp() public override { @@ -74,7 +74,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { feed = IChainlinkFeed(sFraxFeedAddr); borrowController = BorrowController(borrowControllerAddr); dbr = DolaBorrowingRights(dbrAddr); - helper = new ERC4626Helper(gov, pauseGuardian); + helper = ERC4626Helper(erc4626HelperAddr); initBase(address(helper)); exchangeProxy = new MockExchangeProxy( @@ -87,7 +87,8 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { dbr.addMarket(address(market)); DOLA.mint(address(market), 1000000e18); - ale = new ALE(address(exchangeProxy), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); + ale.allowProxy(address(exchangeProxy)); ale.setMarket(address(market), fraxAddr, address(helper), true); vm.stopPrank(); //FiRM @@ -186,7 +187,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -194,7 +195,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; ale.leveragePosition( maxBorrowAmount, @@ -268,7 +269,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -276,7 +277,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 98) / 100, 0 @@ -360,11 +361,11 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR @@ -380,8 +381,8 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ale.deleveragePosition( _convertCollatToDola(amountToWithdraw), address(market), - amountToWithdraw, address(exchangeProxy), + amountToWithdraw, swapData, permit, abi.encode(address(market)), @@ -391,11 +392,11 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { // Some collateral has been withdrawn assertEq( IERC20(sFraxAddr).balanceOf(address(market.predictEscrow(userPk))), - sFraxAmount - amountToWithdraw + sFraxAmount - amountToWithdraw, 'COLLATERAL' ); // User still has dola and actually he has more bc he sold his DBRs - assertGt(DOLA.balanceOf(userPk), borrowAmount); + assertGt(DOLA.balanceOf(userPk), borrowAmount, 'DOLA'); assertEq(dbr.balanceOf(userPk), 0); } @@ -455,9 +456,9 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper(0, 0, borrowAmount / 2); // repay partially debt with DOLA in the wallet + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper(0, 0, borrowAmount / 2); // repay partially debt with DOLA in the wallet bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaOut.selector, @@ -471,8 +472,8 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ale.deleveragePosition( _convertCollatToDola(amountToWithdraw), address(market), - amountToWithdraw, address(exchangeProxy), + amountToWithdraw, swapData, permit, abi.encode(address(market)), @@ -488,7 +489,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { assertApproxEqAbs(DOLA.balanceOf(userPk), borrowAmount / 2, 1); } - function test_transformToCollateralAndDeposit(uint256 fraxAmount) public { + function test_convertToCollateralAndDeposit(uint256 fraxAmount) public { //vm.assume(fraxAmount < 1 ether); uint256 fraxAmount = 1 ether; @@ -498,7 +499,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { vm.startPrank(userPk, userPk); IERC20(fraxAddr).approve(address(helper), fraxAmount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( fraxAmount, userPk, abi.encode(address(market)) @@ -512,7 +513,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); } - function test_withdrawAndTransformFromCollateral( + function test_withdrawAndConvertFromCollateral( uint256 fraxAmount ) public { // vm.assume(fraxAmount < IsFrax(sFrax).availableDepositLimit()); @@ -524,7 +525,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { vm.startPrank(userPk, userPk); IERC20(fraxAddr).approve(address(helper), fraxAmount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( fraxAmount, userPk, abi.encode(address(market)) @@ -555,8 +556,8 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - IMultiMarketTransformHelper.Permit - memory permit = IMultiMarketTransformHelper.Permit( + IMultiMarketConvertHelper.Permit + memory permit = IMultiMarketConvertHelper.Permit( block.timestamp, v, r, @@ -565,7 +566,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { assertEq(IERC20(fraxAddr).balanceOf(userPk), 0); - helper.withdrawAndTransformFromCollateral( + helper.withdrawAndConvertFromCollateral( amountToWithdraw, userPk, permit, @@ -583,7 +584,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { address fakeMarket = address(0x69); vm.expectRevert( - abi.encodeWithSelector(ALE.NoMarket.selector, fakeMarket) + abi.encodeWithSelector(ALEV2.NoMarket.selector, fakeMarket) ); vm.prank(gov); ale.setMarket(fakeMarket, address(0), address(0), true); @@ -594,7 +595,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { vm.expectRevert( abi.encodeWithSelector( - ALE.MarketSetupFailed.selector, + ALEV2.MarketSetupFailed.selector, address(market), fakeBuySellToken, address(collateral), @@ -606,7 +607,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { vm.expectRevert( abi.encodeWithSelector( - ALE.MarketSetupFailed.selector, + ALEV2.MarketSetupFailed.selector, address(market), address(0), address(collateral), @@ -626,7 +627,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { address newHelper = address(0x70); vm.expectRevert( - abi.encodeWithSelector(ALE.MarketNotSet.selector, wrongMarket) + abi.encodeWithSelector(ALEV2.MarketNotSet.selector, wrongMarket) ); vm.prank(gov); ale.updateMarketHelper(wrongMarket, newHelper); @@ -686,7 +687,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -694,7 +695,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; // Mock call to return 0 buySellToken balance for the ALE vm.mockCall( @@ -703,7 +704,7 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { abi.encode(uint256(0)) ); - vm.expectRevert(ALE.CollateralIsZero.selector); + vm.expectRevert(ALEV2.CollateralIsZero.selector); ale.leveragePosition( maxBorrowAmount, address(market), @@ -715,6 +716,141 @@ contract ALEsFrax4626HelperForkTest is BaseHelperForkTest { ); } + function test_Frax_Odos_leverage_and_deleverage() public { + address odos = address(0xCf5540fFFCdC3d510B18bFcA6d2b9987b0772559); + vm.makePersistent(address(helper)); + vm.makePersistent(address(oracle)); + vm.rollFork(22074631); + sFraxHolder = 0x56398b89d53e8731bca8C1B06886CFB14BD6b654; + + vm.startPrank(gov); + ale = new ALEV2(triDBRAddr); + ale.allowProxy(odos); + helper.setMarket(address(market), fraxAddr, sFraxAddr); + ale.setMarket(address(market), fraxAddr, address(helper), true); + borrowController.allow(address(ale)); + vm.stopPrank(); + console.log("ALE address", address(ale)); + uint sFraxAmount = 100000 ether; + address userPk = vm.addr(1); + vm.prank(sFraxHolder); + IERC20(sFraxAddr).transfer(userPk, sFraxAmount); + + gibDBR(userPk, 20000 ether); + + uint borrowAmount = 100000 ether; + vm.startPrank(userPk, userPk); + // Initial sFrax deposit + IERC20(sFraxAddr).approve(address(market), sFraxAmount); + market.deposit(sFraxAmount); + + // Sign Message for borrow on behalf + bytes32 hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + borrowAmount, + 0, + block.timestamp + ) + ) + ) + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); + + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); + + bytes memory swapData = hex"3b635ce4000000000000000000000000865377367054516e17014ccded1e7d814edc9ce400000000000000000000000000000000000000000000152d02c7e14af6800000000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef000000000000000000000000853d955acef822db058eb8505911ed77f175b99e00000000000000000000000000000000000000000000152075f09d69360000000000000000000000000000000000000000000000000014ea6047cf09710000000000000000000000000000009123ef9b7db2e3d968b01c6ff99839acb242da130000000000000000000000000000000000000000000000000000000000000140000000000000000000000000d768d1fe6ef1449a54f9409400fe9d0e4954ea3f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013c03030c006701000001020001020e00000304010000c800000400016cd2f9d06701000105040100000d0207070400046700000006080100025600090a0b010000020bff0000000000000000000000000000000000000000000000000000000000744793b5110f6ca9cc7cdfe1ce16677c3eb192ef865377367054516e17014ccded1e7d814edc9ce49d39a5de30e57443bff2a8307a4256c8797a3497dac17f958d2ee523a2206206994597c13d831ec74f493b7de8aac7d55f71853688b1f7c8f0243c855dc1bf6f1e983c0b21efb003c105133736fa0743435664008f38b0650fbc1c9fc971d0a3bc2f1e474c9edd5852cd905f086c759e8383e09bff1e68b3dcef968d416a41cdac0ed8702fac8128a64241a2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48853d955acef822db058eb8505911ed77f175b99e00000000"; + + ALEV2.DBRHelper memory dbrData; + + ale.leveragePosition( + borrowAmount, + address(market), + odos, + swapData, + permit, + abi.encode(address(market)), + dbrData + ); + assertApproxEqAbs( + IERC20(sFraxAddr).balanceOf(address(market.predictEscrow(userPk))), + sFraxAmount + + IERC4626(sFraxAddr).convertToShares( + 99768490416193306361856 // expected FRAX out from ODOS + ), + 1 + ); + + assertEq(DOLA.balanceOf(userPk), 0); + + uint256 amountToWithdraw = IERC20(sFraxAddr).balanceOf( + address(market.predictEscrow(userPk))); + console.log("fraxAmount", IERC4626(sFraxAddr).convertToAssets(amountToWithdraw)); + hash = keccak256( + abi.encodePacked( + "\x19\x01", + market.DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" + ), + address(ale), + userPk, + amountToWithdraw, + 1, + block.timestamp + ) + ) + ) + ); + (v, r, s) = vm.sign(1, hash); + + permit = ALEV2.Permit(block.timestamp, v, r, s); + + dbrData = ALEV2.DBRHelper( + dbr.balanceOf(userPk), + 1, + 0 + ); // sell all DBR + + swapData = hex"83bd37f90001853d955acef822db058eb8505911ed77f175b99e0001865377367054516e17014ccded1e7d814edc9ce40a2cd6b5a59e02b5bb76440a2ce28d9c48d54a000000028f5c0001d768d1Fe6Ef1449A54F9409400fe9d0E4954ea3F000000019123ef9b7dB2e3D968B01C6FF99839aCb242dA13000000001d070723010879335a4601020203040101283834826702060106030001012020c36067030001070300010067040001080300010913c3130046000a0a0b0c0109239cc2dd67050e010e0b000108670600010f0b000106560410111200010467031400051401000267020000010401000c000104460616161718010667021a00131a00010067061600090c00010a670000000d1800010856051b12170001004604011c1d1e000467040100191f01000c67040100151801000a4a040120170100026704010121220100ff00000000000000000000000000000000000000000000000073a0cba58c19ed5f27c6590bd792ec38de4815eaa663b02cf0a4b149d2ad41910cb81e23e1c41c32853d955acef822db058eb8505911ed77f175b99ea663b02cf0a4b149d2ad41910cb81e23e1c41c32670a72e6d22b0956c0d2573288f82dcc5d6e3a615dc1bf6f1e983c0b21efb003c105133736fa0743ce6431d21e3fb1036ce9973a3312368ed96f5ce7bbaf8b2837cbbc7146f5bc978d6f84db0be1cacc15e4ff94b70a8f146b4e4afd069014d126035752cf62f905562626cfcdd2261162a51fd02fc9c5b6cacd6fd266af91b8aed52accc382b4e165586e29cf62f905562626cfcdd2261162a51fd02fc9c5b676a962ba6770068bcf454d34dde17175611e66374d968a5db8da0822a2f08840f553de4129aae5f381a2612f6dea269a6dd1f6deab45c5424ee2c4b7425bfb93370f14ff525adb6eaeacfe1f4e3b580283f20f44975d03b1b09e64809b757c47f942beea59d9356e565ab3a36dd77763fc0d87feaf85508c4628f13651ead6793f8d838b34b8f8522fb0cc524c9edd5852cd905f086c759e8383e09bff1e68b3ff17dab22f1e61078aba2623c89ce6110e878b3c0655977feb2f289a4ab78af67bab0d17aab84367f939e0a03fb07f59a73314e73794be0e57ac1b4e0655977feb2f289a4ab78af67bab0d17aab8436738de22a3175708d45e7c7c64cd78479c8b56f76e40d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f30ce6e5a75586f0e83bcac77c9135e980e6bc7a8b45ad160634c528cc3d2926d9807104fa3157305b45ad160634c528cc3d2926d9807104fa3157305865377367054516e17014ccded1e7d814edc9ce466a1e37c9b0eaddca17d3662d6c05f4decf3e1108272e1a3dbef607c04aa6e5bd3a1a134c8ac063b8b83c4aa949254895507d09365229bc3a8c7f710a3931d71877c0e7a3148cb7eb4463524fec27fbd000000000000000000000000000000000000000000000000"; + + console.log("amountToWithdraw", amountToWithdraw); + dbr.approve(address(ale), type(uint).max); + ale.deleveragePosition( + market.debts(userPk), + address(market), + odos, + amountToWithdraw, + swapData, + permit, + abi.encode(address(market)), + dbrData + ); + + // All collateral withdrawn and zero debt + assertEq( + IERC20(sFraxAddr).balanceOf(address(market.predictEscrow(userPk))), + 0, 'COLLATERAL' + ); + assertEq(market.debts(userPk), 0); + // User receives Dola from selling his collateral and actually he has more bc he sold all his DBR + uint256 amountDolaOut = 211963293517859369517056; // Odos output swap + assertGt(DOLA.balanceOf(userPk), 211963293517859369517056 - borrowAmount, 'DOLA'); + assertEq(IERC20(fraxAddr).balanceOf(userPk), 0, 'FRAX'); + assertEq(dbr.balanceOf(userPk), 0); + } + + function _convertCollatToDola(uint amount) internal view returns (uint) { uint256 underlying = IERC4626(sFraxAddr).convertToAssets(amount); return _convertUnderlyingToDola(underlying); diff --git a/test/util/aleTests/ALEst-yCRVHelperForkTest.t.sol b/test/util/aleTests/ALEst-yCRVHelperForkTest.t.sol deleted file mode 100644 index 52146e67..00000000 --- a/test/util/aleTests/ALEst-yCRVHelperForkTest.t.sol +++ /dev/null @@ -1,977 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import {BorrowController} from "src/BorrowController.sol"; -import "src/DBR.sol"; -import {Market, IBorrowController} from "src/Market.sol"; -import {Oracle, IChainlinkFeed} from "src/Oracle.sol"; -import {ALE} from "src/util/ALE.sol"; -import {YVYCRVHelper, YearnVaultV2Helper, IYearnVaultV2} from "src/util/YVYCRVHelper.sol"; -import {YCRVFeed} from "test/mocks/YCRVFeed.sol"; -import {Fed, IMarket} from "src/Fed.sol"; -import {console} from "forge-std/console.sol"; -import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; -import {BaseHelperForkTest, MockExchangeProxy} from "test/util/aleTests/BaseHelperForkTest.t.sol"; -import {Fed, IMarket} from "src/Fed.sol"; - -interface IMintable is IERC20 { - function mint(address receiver, uint amount) external; - - function addMinter(address minter) external; -} - -interface IFlashMinter { - function setMaxFlashLimit(uint256 limit) external; -} - -interface IBC { - function setMinDebt(address market, uint256 minDebt) external; - - function setStalenessThreshold(address market, uint256 threshold) external; -} - -interface ISimpleERC20Escrow { - function balance() external view returns (uint256); -} - -interface IYCRV { - function mint(uint256 amount) external returns (uint256); -} - -contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { - using stdStorage for StdStorage; - - //Market deployment: - Market market; - IChainlinkFeed feed; - BorrowController borrowController; - - address yCRV = address(0xFCc5c47bE19d06BF83eB04298b026F81069ff65b); - address CRV = address(0xD533a949740bb3306d119CC777fa900bA034cd52); - - // User - address userPk; - - //ERC-20s - IMintable DOLA; - IERC20 collateral; - - //FiRM - address escrow; - Oracle oracle; - DolaBorrowingRights dbr; - - // ALE - ALE ale; - YVYCRVHelper helper; - IFlashMinter flash; - - // Mocks - YCRVFeed feedYCRV; - MockExchangeProxy exchangeProxy; - - //Variables - uint collateralFactorBps; - - function setUp() public override { - super.setUp(); - - DOLA = IMintable(dolaAddr); - market = Market(yvyCRVMarketAddr); // yvyCRV Market - feed = IChainlinkFeed(yvyCRVFeedAddr); - borrowController = BorrowController(borrowControllerAddr); - dbr = DolaBorrowingRights(dbrAddr); - - helper = new YVYCRVHelper(gov, pauseGuardian); - initBase(address(helper)); - feedYCRV = new YCRVFeed(); - - exchangeProxy = new MockExchangeProxy( - address(market.oracle()), - address(DOLA) - ); - - Fed fed = Fed(market.lender()); - vm.startPrank(gov); - market.pauseBorrows(false); - borrowController.setDailyLimit(address(market), 5000000 ether); - fed.changeMarketCeiling(IMarket(address(market)), 100000000 ether); - vm.stopPrank(); - - vm.prank(chair); - fed.expansion(IMarket(address(market)), 5000000 ether); - - ale = new ALE(address(exchangeProxy), triDBRAddr); - ale.setMarket(address(market), yCRV, address(helper), true); - - //FiRM - oracle = Oracle(address(market.oracle())); - collateral = IERC20(address(market.collateral())); - - vm.startPrank(gov, gov); - - IBC(address(borrowController)).setMinDebt(address(market), 0); - oracle.setFeed(address(collateral), feed, 18); - oracle.setFeed(yCRV, IChainlinkFeed(address(feedYCRV)), 18); - borrowController.allow(address(ale)); - - flash = IFlashMinter(address(ale.flash())); - DOLA.addMinter(address(flash)); - flash.setMaxFlashLimit(5000000e18); // 5M DOLA - vm.stopPrank(); - - collateralFactorBps = market.collateralFactorBps(); - userPk = vm.addr(1); - - escrow = address(market.predictEscrow(userPk)); - // For Recharging proxy - deal(CRV, address(this), 15_000_000 ether); - IERC20(CRV).approve(address(yCRV), 15_000_000 ether); - IYCRV(yCRV).mint(15_000_000 ether); - - // Fill userPk with some yvyCRV - deal(CRV, userPk, 30_000_000 ether); // 30M CRV => roughly 7.5M USD - vm.startPrank(userPk); - IERC20(CRV).approve(address(yCRV), 30_000_000 ether); - uint256 yCRVAmount = IYCRV(yCRV).mint(30_000_000 ether); - assertEq(yCRVAmount, IERC20(yCRV).balanceOf(userPk)); - IERC20(yCRV).approve(yvyCRVAddr, yCRVAmount); - uint256 yvyCRVAmount = IYearnVaultV2(yvyCRVAddr).deposit( - yCRVAmount, - userPk - ); // 16.94M yvyCRV - assertEq(yvyCRVAmount, IERC20(yvyCRVAddr).balanceOf(userPk)); - assertEq(0, IERC20(yCRV).balanceOf(userPk)); - vm.stopPrank(); - } - - function getBlockNumber() public view override returns (uint256) { - return 20590050; - } - - function checkEq( - uint stYCRVDeposit, - uint collateralToSwap, - address userPk - ) internal { - assertApproxEqAbs( - IERC20(yvyCRVAddr).balanceOf(address(market.predictEscrow(userPk))), - stYCRVDeposit + collateralToSwap, - 1 - ); - } - - function test_leveragePosition(uint256 styCRVAmount) public { - vm.assume(styCRVAmount < IERC20(yvyCRVAddr).balanceOf(userPk)); // 16.94M yvyCRV - vm.assume(styCRVAmount > 0.00000001 ether); - // We are going to deposit some yvyCRV, then leverage the position - - gibDBR(userPk, styCRVAmount); - - uint maxBorrowAmount = _getMaxBorrowAmount(styCRVAmount); - - uint256 yCRVAmount = YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - _convertDolaToCollat(maxBorrowAmount) - ); - // recharge mocked proxy for swap, we need to swap DOLA to unwrapped collateral - IERC20(yCRV).transfer(address(exchangeProxy), yCRVAmount + 2); - - vm.startPrank(userPk, userPk); - // Initial yvyCRV deposit - IERC20(yvyCRVAddr).approve(address(market), styCRVAmount); - market.deposit(styCRVAmount); - - // Sign Message for borrow on behalf - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(ale), - userPk, - maxBorrowAmount, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); - - bytes memory swapData = abi.encodeWithSelector( - MockExchangeProxy.swapDolaIn.selector, - yCRV, - maxBorrowAmount - ); - - ALE.DBRHelper memory dbrData; - - ale.leveragePosition( - maxBorrowAmount, - address(market), - address(exchangeProxy), - swapData, - permit, - bytes(""), - dbrData - ); - - // Balance in escrow is equal to the collateral deposited + the extra collateral swapped from the leverage - uint256 expectedCollateral = styCRVAmount + - YearnVaultV2Helper.assetToCollateral( - IYearnVaultV2(yvyCRVAddr), - _convertDolaToUnderlying(maxBorrowAmount) - ); - assertApproxEqAbs( - IERC20(yvyCRVAddr).balanceOf(address(market.predictEscrow(userPk))), - expectedCollateral, - 1 - ); - assertApproxEqAbs( - ISimpleERC20Escrow(escrow).balance(), - expectedCollateral, - 1 - ); - assertEq(DOLA.balanceOf(userPk), 0); - } - - function test_depositAndLeveragePosition(uint256 styCRVAmount) public { - vm.assume(styCRVAmount < IERC20(yvyCRVAddr).balanceOf(userPk)); - vm.assume(styCRVAmount > 0.00000001 ether); - // We are going to deposit some CRV, then leverage the position - - gibDBR(userPk, styCRVAmount); - - uint maxBorrowAmount = _getMaxBorrowAmount(styCRVAmount); - - uint256 yCRVAmount = YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - _convertDolaToCollat(maxBorrowAmount) - ); - // recharge mocked proxy for swap, we need to swap DOLA to unwrapped collateral - IERC20(yCRV).transfer(address(exchangeProxy), yCRVAmount + 2); - - vm.startPrank(userPk, userPk); - // yvyCRV deposit to be leveraged - IERC20(yvyCRVAddr).approve(address(ale), styCRVAmount); - - // Sign Message for borrow on behalf - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(ale), - userPk, - maxBorrowAmount, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); - - bytes memory swapData = abi.encodeWithSelector( - MockExchangeProxy.swapDolaIn.selector, - yCRV, - maxBorrowAmount - ); - - ALE.DBRHelper memory dbrData; - - ale.depositAndLeveragePosition( - styCRVAmount, - maxBorrowAmount, - address(market), - address(exchangeProxy), - swapData, - permit, - bytes(""), - dbrData, - true - ); - - // Balance in escrow is equal to the collateral deposited + the extra collateral swapped from the leverage - uint256 expectedCollateral = styCRVAmount + - YearnVaultV2Helper.assetToCollateral( - IYearnVaultV2(yvyCRVAddr), - _convertDolaToUnderlying(maxBorrowAmount) - ); - assertApproxEqAbs( - IERC20(yvyCRVAddr).balanceOf(address(market.predictEscrow(userPk))), - expectedCollateral, - 1 - ); - assertApproxEqAbs( - ISimpleERC20Escrow(escrow).balance(), - expectedCollateral, - 1 - ); - assertEq(DOLA.balanceOf(userPk), 0); - } - - function test_leveragePosition_buyDBR(uint256 styCRVAmount) public { - // We are going to deposit some st-yCRV, then leverage the position - vm.assume(styCRVAmount < IERC20(yvyCRVAddr).balanceOf(userPk)); - vm.assume(styCRVAmount > 0.1 ether); - - uint maxBorrowAmount = _getMaxBorrowAmount(styCRVAmount); - - uint256 yCRVAmount = YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - _convertDolaToCollat(maxBorrowAmount) - ); - - // recharge mocked proxy for swap, we need to swap DOLA to unwrapped collateral - IERC20(yCRV).transfer(address(exchangeProxy), yCRVAmount + 2); - - vm.startPrank(userPk, userPk); - // Initial yvyCRV deposit - IERC20(yvyCRVAddr).approve(address(market), styCRVAmount); - market.deposit(styCRVAmount); - - // Calculate the amount of DOLA needed to borrow to buy the DBR needed to cover for the borrowing period - (uint256 dolaForDBR, uint256 dbrAmount) = ale - .approximateDolaAndDbrNeeded(maxBorrowAmount, 10 days, 8); - - // Sign Message for borrow on behalf - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(ale), - userPk, - maxBorrowAmount + dolaForDBR, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); - - bytes memory swapData = abi.encodeWithSelector( - MockExchangeProxy.swapDolaIn.selector, - yCRV, - maxBorrowAmount - ); - - ALE.DBRHelper memory dbrData = ALE.DBRHelper( - dolaForDBR, - (dbrAmount * 89) / 100, - 0 - ); - - ale.leveragePosition( - maxBorrowAmount, - address(market), - address(exchangeProxy), - swapData, - permit, - bytes(""), - dbrData - ); - - // Balance in escrow is equal to the collateral deposited + the extra collateral swapped from the leverage - checkEq(styCRVAmount, _convertDolaToCollat(maxBorrowAmount), userPk); - assertApproxEqAbs( - ISimpleERC20Escrow(escrow).balance(), - styCRVAmount + - YearnVaultV2Helper.assetToCollateral( - IYearnVaultV2(yvyCRVAddr), - _convertDolaToUnderlying(maxBorrowAmount) - ), - 1 - ); - assertEq(DOLA.balanceOf(userPk), 0); - - assertGt(dbr.balanceOf(userPk), (dbrAmount * 89) / 100); - } - - function test_deleveragePosition_sellDBR(uint256 styCRVAmount) public { - vm.assume(styCRVAmount < IERC20(yvyCRVAddr).balanceOf(userPk)); // 16.94M yvyCRV - vm.assume(styCRVAmount > 0.00000001 ether); - // We are going to deposit some st-yCRV, then borrow and then deleverage the position - - gibDBR(userPk, styCRVAmount); - - uint borrowAmount = (_getMaxBorrowAmount(styCRVAmount) * 97) / 100; - - vm.startPrank(userPk, userPk); - // Initial styCRV deposit - IERC20(yvyCRVAddr).approve(address(market), styCRVAmount); - market.deposit(styCRVAmount); - market.borrow(borrowAmount); - vm.stopPrank(); - - assertEq( - IERC20(yvyCRVAddr).balanceOf(address(market.predictEscrow(userPk))), - styCRVAmount - ); - assertEq(DOLA.balanceOf(userPk), borrowAmount); - - // We are going to withdraw only 1/10 of the collateral to deleverage - uint256 amountToWithdraw = IERC20(yvyCRVAddr).balanceOf( - address(market.predictEscrow(userPk)) - ) / 10; - - uint256 dolaAmountForSwap = _convertUnderlyingToDola( - YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - amountToWithdraw - ) - ); - - // recharge mocked proxy for swap, we need to swap DOLA to unwrapped collateral - vm.startPrank(gov); - DOLA.mint(address(exchangeProxy), dolaAmountForSwap + 1); - vm.stopPrank(); - - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(ale), - userPk, - amountToWithdraw, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); - - ALE.DBRHelper memory dbrData = ALE.DBRHelper( - dbr.balanceOf(userPk), - 0, - 0 - ); // sell all DBR - - bytes memory swapData = abi.encodeWithSelector( - MockExchangeProxy.swapDolaOut.selector, - yCRV, - YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - amountToWithdraw - ) - 1 - ); - - vm.startPrank(userPk, userPk); - dbr.approve(address(ale), type(uint).max); - - ale.deleveragePosition( - _convertCollatToDola(amountToWithdraw) - 1, // repay little less bc of yearn 1 wei conversion loss - address(market), - amountToWithdraw, - address(exchangeProxy), - swapData, - permit, - bytes(""), - dbrData - ); - - // Some collateral has been withdrawn - assertEq( - IERC20(yvyCRVAddr).balanceOf(address(market.predictEscrow(userPk))), - styCRVAmount - amountToWithdraw - ); - assertEq( - ISimpleERC20Escrow(escrow).balance(), - styCRVAmount - amountToWithdraw - ); - // User still has dola and actually he has more bc he sold his DBRs - assertGt(DOLA.balanceOf(userPk), borrowAmount); - - assertEq(dbr.balanceOf(userPk), 0); - } - - function test_deleveragePosition(uint256 styCRVAmount) public { - vm.assume(styCRVAmount < IERC20(yvyCRVAddr).balanceOf(userPk)); // 16.94M yvyCRV - vm.assume(styCRVAmount > 0.00000001 ether); - - // We are going to deposit some yvyCRV, then borrow and then deleverage the position - - gibDBR(userPk, styCRVAmount); - - uint borrowAmount = (_getMaxBorrowAmount(styCRVAmount) * 97) / 100; - - vm.startPrank(userPk, userPk); - // Initial styCRV deposit - IERC20(yvyCRVAddr).approve(address(market), styCRVAmount); - market.deposit(styCRVAmount); - market.borrow(borrowAmount); - vm.stopPrank(); - - address userEscrow = address(market.predictEscrow(userPk)); - assertEq(IERC20(yvyCRVAddr).balanceOf(userEscrow), styCRVAmount); - assertEq(DOLA.balanceOf(userPk), borrowAmount); - - // We are going to withdraw only 1/10 of the collateral to deleverage - uint256 amountToWithdraw = IERC20(yvyCRVAddr).balanceOf(userEscrow) / - 10; - uint256 dolaAmountForSwap = _convertUnderlyingToDola( - YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - amountToWithdraw - ) - ); - - // recharge mocked proxy for swap, we need to swap DOLA to unwrapped collateral - vm.startPrank(gov); - DOLA.mint(address(exchangeProxy), dolaAmountForSwap + 2); - vm.stopPrank(); - - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(ale), - userPk, - amountToWithdraw, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); - - ALE.DBRHelper memory dbrData = ALE.DBRHelper(0, 0, borrowAmount / 2); // repay partially debt with DOLA in the wallet - - bytes memory swapData = abi.encodeWithSelector( - MockExchangeProxy.swapDolaOut.selector, - yCRV, - YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - amountToWithdraw - ) - 1 // swap little less because of yearn 1 wei conversion loss if withdrawing from strategy - ); - - vm.startPrank(userPk, userPk); - DOLA.approve(address(ale), borrowAmount / 2); - - ale.deleveragePosition( - _convertCollatToDola(amountToWithdraw) - 1, - address(market), - amountToWithdraw, - address(exchangeProxy), - swapData, - permit, - bytes(""), - dbrData - ); - - // Some collateral has been withdrawn - assertEq( - IERC20(yvyCRVAddr).balanceOf(userEscrow), - styCRVAmount - amountToWithdraw - ); - assertEq( - ISimpleERC20Escrow(userEscrow).balance(), - styCRVAmount - amountToWithdraw - ); - // User still has dola but has some debt repaid - assertApproxEqAbs(DOLA.balanceOf(userPk), borrowAmount / 2, 2); - } - - function test_depositAndLeveragePosition_buyDBR_with_yCRV( - uint256 amount - ) public { - // Fill userPk with some yCRV - deal(CRV, userPk, 15_000_000 ether); - vm.startPrank(userPk); - IERC20(CRV).approve(address(yCRV), 15_000_000 ether); - IYCRV(yCRV).mint(15_000_000 ether); - vm.stopPrank(); - - // We are going to deposit and convert some yCRV and leverage the position - vm.assume(amount < IERC20(yCRV).balanceOf(userPk)); - vm.assume(amount > 0.00001 ether); - - uint stYCRVDeposit = YearnVaultV2Helper.assetToCollateral( - IYearnVaultV2(yvyCRVAddr), - amount - ); - uint maxBorrowAmount = _getMaxBorrowAmount(stYCRVDeposit); - - // recharge mocked proxy for swap, we need to swap DOLA to unwrapped collateral - uint collateralToSwap = _convertDolaToCollat(maxBorrowAmount); - - uint underlyingAmountToSwap = YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - collateralToSwap - ); - - IERC20(yCRV).transfer( - address(exchangeProxy), - underlyingAmountToSwap + 2 - ); // 2 rounding when calculating it - - vm.startPrank(userPk, userPk); - // Approve for initial yCRV deposit - IERC20(yCRV).approve(address(ale), amount); - - // Calculate the amount of DOLA needed to borrow to buy the DBR needed to cover for the borrowing period - (uint256 dolaForDBR, uint256 dbrAmount) = ale - .approximateDolaAndDbrNeeded(maxBorrowAmount, 10 days, 8); - - // Sign Message for borrow on behalf - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(ale), - userPk, - maxBorrowAmount + dolaForDBR, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); - - bytes memory swapData = abi.encodeWithSelector( - MockExchangeProxy.swapDolaIn.selector, - yCRV, - maxBorrowAmount - ); - - ALE.DBRHelper memory dbrData = ALE.DBRHelper( - dolaForDBR, - (dbrAmount * 90) / 100, - 0 - ); - - ale.depositAndLeveragePosition( - amount, - maxBorrowAmount, - address(market), - address(exchangeProxy), - swapData, - permit, - bytes(""), - dbrData, - false - ); - // Balance in escrow is equal to the collateral deposited + the extra collateral swapped from the leverage - checkEq(stYCRVDeposit, collateralToSwap, userPk); - assertApproxEqAbs( - ISimpleERC20Escrow(escrow).balance(), - stYCRVDeposit + collateralToSwap, - 1 - ); - } - - function test_transformToCollateralAndDeposit(uint256 yCRVAmount) public { - // Fill userPk with max available deposit yCRV - uint256 maxDeposit = IYearnVaultV2(yvyCRVAddr).availableDepositLimit(); // minting is 1:1 - deal(CRV, userPk, maxDeposit); - vm.startPrank(userPk); - IERC20(CRV).approve(address(yCRV), maxDeposit); - IYCRV(yCRV).mint(maxDeposit); - assertEq(IERC20(yCRV).balanceOf(userPk), maxDeposit); - - vm.assume( - yCRVAmount < IYearnVaultV2(yvyCRVAddr).availableDepositLimit() - ); - vm.assume(yCRVAmount > 0.00000001 ether); - - uint256 yCRVBalBefore = IERC20(yCRV).balanceOf(userPk); - IERC20(yCRV).approve(address(helper), yCRVAmount); - helper.transformToCollateralAndDeposit(yCRVAmount, ""); - - assertEq(yCRVBalBefore - yCRVAmount, IERC20(yCRV).balanceOf(userPk)); - - assertEq( - IERC20(yvyCRVAddr).balanceOf(address(market.predictEscrow(userPk))), - YearnVaultV2Helper.assetToCollateral( - IYearnVaultV2(yvyCRVAddr), - yCRVAmount - ) - ); - } - - function test_withdrawAndTransformFromCollateral( - uint256 yCRVAmount - ) public { - // Fill userPk with max available deposit yCRV - uint256 maxDeposit = IYearnVaultV2(yvyCRVAddr).availableDepositLimit(); // minting is 1:1 - deal(CRV, userPk, maxDeposit); - vm.startPrank(userPk); - IERC20(CRV).approve(address(yCRV), maxDeposit); - IYCRV(yCRV).mint(maxDeposit); - assertEq(IERC20(yCRV).balanceOf(userPk), maxDeposit); - - vm.assume( - yCRVAmount < IYearnVaultV2(yvyCRVAddr).availableDepositLimit() - ); - vm.assume(yCRVAmount > 0.00000001 ether); - - uint256 yCRVBalBefore = IERC20(yCRV).balanceOf(userPk); - IERC20(yCRV).approve(address(helper), yCRVAmount); - helper.transformToCollateralAndDeposit(yCRVAmount, ""); - - uint256 yCRVBalAfter = IERC20(yCRV).balanceOf(userPk); - assertEq(yCRVBalBefore - yCRVAmount, yCRVBalAfter); - - uint256 amountToWithdraw = IERC20(yvyCRVAddr).balanceOf( - address(market.predictEscrow(userPk)) - ) / 10; - - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(helper), - userPk, - amountToWithdraw, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - YVYCRVHelper.Permit memory permit = YVYCRVHelper.Permit( - block.timestamp, - v, - r, - s - ); - - helper.withdrawAndTransformFromCollateral(amountToWithdraw, permit, ""); - - assertApproxEqAbs( - IERC20(yCRV).balanceOf(userPk) - yCRVBalAfter, - YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - amountToWithdraw - ), - 1 - ); - } - - function test_fail_setMarket_NoMarket() public { - address fakeMarket = address(0x69); - - vm.expectRevert( - abi.encodeWithSelector(ALE.NoMarket.selector, fakeMarket) - ); - ale.setMarket(fakeMarket, address(0), address(0), true); - } - - function test_fail_updateMarketHelper_NoMarket() public { - address wrongMarket = address(0x69); - address newHelper = address(0x70); - - vm.expectRevert( - abi.encodeWithSelector(ALE.MarketNotSet.selector, wrongMarket) - ); - ale.updateMarketHelper(wrongMarket, newHelper); - } - - function test_return_assetAmount_when_TotalSupply_is_Zero() public { - stdstore - .target(address(helper.vault())) - .sig(helper.vault().totalSupply.selector) - .checked_write(uint256(0)); - - uint256 assetAmount = 1 ether; - assertEq( - assetAmount, - YearnVaultV2Helper.assetToCollateral( - IYearnVaultV2(yvyCRVAddr), - assetAmount - ) - ); - } - - function test_fail_collateral_is_zero_leveragePosition() public { - // We are going to deposit some CRV, then leverage the position - uint styCRVAmount = 1 ether; - - gibDBR(userPk, styCRVAmount); - - uint maxBorrowAmount = _getMaxBorrowAmount(styCRVAmount); - - uint256 yCRVAmount = YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - _convertDolaToCollat(maxBorrowAmount) - ); - // recharge mocked proxy for swap, we need to swap DOLA to unwrapped collateral - IERC20(yCRV).transfer(address(exchangeProxy), yCRVAmount + 2); - - vm.startPrank(userPk, userPk); - // Initial CRV deposit - IERC20(yvyCRVAddr).approve(address(market), styCRVAmount); - market.deposit(styCRVAmount); - - // Sign Message for borrow on behalf - bytes32 hash = keccak256( - abi.encodePacked( - "\x19\x01", - market.DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)" - ), - address(ale), - userPk, - maxBorrowAmount, - 0, - block.timestamp - ) - ) - ) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); - - bytes memory swapData = abi.encodeWithSelector( - MockExchangeProxy.swapDolaIn.selector, - yCRV, - maxBorrowAmount - ); - - ALE.DBRHelper memory dbrData; - - // Mock call to return 0 buySellToken balance for the ALE - vm.mockCall( - yCRV, - abi.encodeWithSelector(IERC20.balanceOf.selector, address(ale)), - abi.encode(uint256(0)) - ); - - vm.expectRevert(ALE.CollateralIsZero.selector); - ale.leveragePosition( - maxBorrowAmount, - address(market), - address(exchangeProxy), - swapData, - permit, - bytes(""), - dbrData - ); - } - - function _convertCollatToDola(uint amount) internal view returns (uint) { - uint256 underlying = YearnVaultV2Helper.collateralToAsset( - IYearnVaultV2(yvyCRVAddr), - amount - ); - return _convertUnderlyingToDola(underlying); - } - - function _convertDolaToCollat(uint amount) internal view returns (uint) { - uint256 underlying = _convertDolaToUnderlying(amount); - return - YearnVaultV2Helper.assetToCollateral( - IYearnVaultV2(yvyCRVAddr), - underlying - ); - } - - function _convertDolaToUnderlying( - uint amount - ) internal view returns (uint) { - return (amount * 1e18) / oracle.viewPrice(yCRV, 0); - } - - function _convertUnderlyingToDola( - uint amount - ) internal view returns (uint) { - return (amount * oracle.viewPrice(yCRV, 0)) / 1e18; - } - - function _getMaxBorrowAmount( - uint amountCollat - ) internal view returns (uint) { - return - (_convertCollatToDola(amountCollat) * - market.collateralFactorBps()) / 10_000; - } - - function gibDBR(address _address, uint _amount) internal { - vm.startPrank(gov); - dbr.mint(_address, _amount); - vm.stopPrank(); - } - - function gibDOLA(address _address, uint _amount) internal { - vm.startPrank(gov); - DOLA.mint(_address, _amount); - vm.stopPrank(); - } - - function codeAt(address _addr) public view returns (bytes memory o_code) { - assembly { - // retrieve the size of the code, this needs assembly - let size := extcodesize(_addr) - // allocate output byte array - this could also be done without assembly - // by using o_code = new bytes(size) - o_code := mload(0x40) - // new "memory end" including padding - mstore( - 0x40, - add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))) - ) - // store length in memory - mstore(o_code, size) - // actually retrieve the code, this needs assembly - extcodecopy(_addr, add(o_code, 0x20), 0, size) - } - } -} diff --git a/test/util/aleTests/ALEst-yETH4626HelperForkTest.t.sol b/test/util/aleTests/ALEst-yETH4626HelperForkTest.t.sol index c1c60509..111660db 100644 --- a/test/util/aleTests/ALEst-yETH4626HelperForkTest.t.sol +++ b/test/util/aleTests/ALEst-yETH4626HelperForkTest.t.sol @@ -7,10 +7,10 @@ import "src/DBR.sol"; import {Market, IBorrowController} from "src/Market.sol"; import {Oracle, IChainlinkFeed} from "src/Oracle.sol"; import {Fed} from "src/Fed.sol"; -import {ALE} from "src/util/ALE.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YETHFeed} from "test/mocks/YETHFeed.sol"; import {ERC4626Helper} from "src/util/ERC4626Helper.sol"; -import {IMultiMarketTransformHelper} from "src/interfaces/IMultiMarketTransformHelper.sol"; +import {IMultiMarketConvertHelper} from "src/interfaces/IMultiMarketConvertHelper.sol"; import {console} from "forge-std/console.sol"; import {IERC4626} from "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; import {BaseHelperForkTest, MockExchangeProxy} from "test/util/aleTests/BaseHelperForkTest.t.sol"; @@ -40,8 +40,8 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { IChainlinkFeed feed; BorrowController borrowController; - address styETHHolder = 0x42b126099beDdCE8f5CcC06b4b39E8343e8F4260; - address yETHHolder = 0x12227DFe5363cbE55919e230653810de0fF317e2; // 2 yEthAddr + address styETHHolder = 0xE37f3343b3f2b784589B677cfD6C9a166aAC2A19; + address yETHHolder = 0x69ACcb968B19a53790f43e57558F5E443A91aF22; // 2 yEthAddr //ERC-20s IMintable DOLA; @@ -53,7 +53,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { Fed fed; MockExchangeProxy exchangeProxy; - ALE ale; + ALEV2 ale; IFlashMinter flash; //STYETHHelper helper; YETHFeed feedyETH; @@ -63,7 +63,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { uint collateralFactorBps; function getBlockNumber() public view override returns (uint256) { - return 20590050; // Random block number + return 22241605; } function setUp() public override { @@ -75,7 +75,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { borrowController = BorrowController(borrowControllerAddr); dbr = DolaBorrowingRights(dbrAddr); - helper = new ERC4626Helper(gov, pauseGuardian); + helper = ERC4626Helper(erc4626HelperAddr); initBase(address(helper)); feedyETH = new YETHFeed(); @@ -93,7 +93,8 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); dbr.addMarket(address(market)); - ale = new ALE(address(exchangeProxy), triDBRAddr); + ale = ALEV2(payable(aleV2Addr)); + ale.allowProxy(address(exchangeProxy)); ale.setMarket(address(market), yEthAddr, address(helper), true); vm.stopPrank(); @@ -109,9 +110,6 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { oracle.setFeed(yEthAddr, IChainlinkFeed(address(feedyETH)), 18); borrowController.allow(address(ale)); - flash = IFlashMinter(address(ale.flash())); - DOLA.addMinter(address(flash)); - flash.setMaxFlashLimit(1000000e18); vm.stopPrank(); collateralFactorBps = market.collateralFactorBps(); @@ -175,7 +173,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -183,7 +181,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; ale.leveragePosition( maxBorrowAmount, @@ -257,7 +255,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -265,7 +263,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 98) / 100, 0 @@ -349,11 +347,11 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR @@ -369,8 +367,8 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ale.deleveragePosition( _convertCollatToDola(amountToWithdraw), address(market), - amountToWithdraw, address(exchangeProxy), + amountToWithdraw, swapData, permit, abi.encode(address(market)), @@ -445,9 +443,9 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper(0, 0, borrowAmount / 2); // repay partially debt with DOLA in the wallet + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper(0, 0, borrowAmount / 2); // repay partially debt with DOLA in the wallet bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaOut.selector, @@ -461,8 +459,8 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ale.deleveragePosition( _convertCollatToDola(amountToWithdraw), address(market), - amountToWithdraw, address(exchangeProxy), + amountToWithdraw, swapData, permit, abi.encode(address(market)), @@ -478,7 +476,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { assertApproxEqAbs(DOLA.balanceOf(userPk), borrowAmount / 2, 1); } - function test_transformToCollateralAndDeposit(uint256 yETHAmount) public { + function test_convertToCollateralAndDeposit(uint256 yETHAmount) public { //vm.assume(yETHAmount < 1 ether); uint256 yETHAmount = 1 ether; @@ -488,7 +486,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { vm.startPrank(userPk, userPk); IERC20(yEthAddr).approve(address(helper), yETHAmount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( yETHAmount, userPk, abi.encode(address(market)) @@ -502,7 +500,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); } - function test_withdrawAndTransformFromCollateral( + function test_withdrawAndConvertFromCollateral( uint256 yETHAmount ) public { // vm.assume(yETHAmount < IstyETH(styEthAddr).availableDepositLimit()); @@ -514,7 +512,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { vm.startPrank(userPk, userPk); IERC20(yEthAddr).approve(address(helper), yETHAmount); - helper.transformToCollateralAndDeposit( + helper.convertToCollateralAndDeposit( yETHAmount, userPk, abi.encode(address(market)) @@ -545,8 +543,8 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - IMultiMarketTransformHelper.Permit - memory permit = IMultiMarketTransformHelper.Permit( + IMultiMarketConvertHelper.Permit + memory permit = IMultiMarketConvertHelper.Permit( block.timestamp, v, r, @@ -555,7 +553,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { assertEq(IERC20(yEthAddr).balanceOf(userPk), 0); - helper.withdrawAndTransformFromCollateral( + helper.withdrawAndConvertFromCollateral( amountToWithdraw, userPk, permit, @@ -573,7 +571,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { address fakeMarket = address(0x69); vm.expectRevert( - abi.encodeWithSelector(ALE.NoMarket.selector, fakeMarket) + abi.encodeWithSelector(ALEV2.NoMarket.selector, fakeMarket) ); vm.prank(gov); ale.setMarket(fakeMarket, address(0), address(0), true); @@ -584,7 +582,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { address newHelper = address(0x70); vm.expectRevert( - abi.encodeWithSelector(ALE.MarketNotSet.selector, wrongMarket) + abi.encodeWithSelector(ALEV2.MarketNotSet.selector, wrongMarket) ); vm.prank(gov); ale.updateMarketHelper(wrongMarket, newHelper); @@ -647,7 +645,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -655,7 +653,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; // Mock call to return 0 buySellToken balance for the ALE vm.mockCall( @@ -664,7 +662,7 @@ contract ALEstYETH4626HelperForkTest is BaseHelperForkTest { abi.encode(uint256(0)) ); - vm.expectRevert(ALE.CollateralIsZero.selector); + vm.expectRevert(ALEV2.CollateralIsZero.selector); ale.leveragePosition( maxBorrowAmount, address(market), diff --git a/test/util/aleTests/ALEyvyCRVHelperForkTest.t.sol b/test/util/aleTests/ALEyvyCRVHelperForkTest.t.sol index 588b6982..56e74f7a 100644 --- a/test/util/aleTests/ALEyvyCRVHelperForkTest.t.sol +++ b/test/util/aleTests/ALEyvyCRVHelperForkTest.t.sol @@ -6,11 +6,10 @@ import {BorrowController} from "src/BorrowController.sol"; import "src/DBR.sol"; import {Market, IBorrowController} from "src/Market.sol"; import {Oracle, IChainlinkFeed} from "src/Oracle.sol"; +import {ALEV2} from "src/util/ALEV2.sol"; import {YVYCRVHelper, YearnVaultV2Helper, IYearnVaultV2} from "src/util/YVYCRVHelper.sol"; import {YCRVFeed} from "test/mocks/YCRVFeed.sol"; import {Fed, IMarket} from "src/Fed.sol"; -import {ALE} from "src/util/ALE.sol"; -import {YCRVFeed} from "test/mocks/YCRVFeed.sol"; import {console} from "forge-std/console.sol"; import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol"; import {BaseHelperForkTest, MockExchangeProxy} from "test/util/aleTests/BaseHelperForkTest.t.sol"; @@ -64,7 +63,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { DolaBorrowingRights dbr; // ALE - ALE ale; + ALEV2 ale; YVYCRVHelper helper; IFlashMinter flash; @@ -84,7 +83,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { borrowController = BorrowController(borrowControllerAddr); dbr = DolaBorrowingRights(dbrAddr); - helper = new YVYCRVHelper(gov, pauseGuardian); + helper = YVYCRVHelper(yvyCRVHelperAddr); initBase(address(helper)); feedYCRV = new YCRVFeed(); @@ -98,14 +97,15 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { market.pauseBorrows(false); borrowController.setDailyLimit(address(market), 5000000 ether); fed.changeMarketCeiling(IMarket(address(market)), 100000000 ether); + ale = ALEV2(payable(aleV2Addr)); + ale.allowProxy(address(exchangeProxy)); + ale.setMarket(address(market), yCRV, address(helper), true); vm.stopPrank(); vm.prank(chair); fed.expansion(IMarket(address(market)), 5000000 ether); - ale = new ALE(address(exchangeProxy), triDBRAddr); - ale.setMarket(address(market), yCRV, address(helper), true); - + //FiRM oracle = Oracle(address(market.oracle())); collateral = IERC20(address(market.collateral())); @@ -119,7 +119,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { flash = IFlashMinter(address(ale.flash())); DOLA.addMinter(address(flash)); - flash.setMaxFlashLimit(5000000e18); // 5M DOLA + flash.setMaxFlashLimit(10000000e18); // 5M DOLA vm.stopPrank(); collateralFactorBps = market.collateralFactorBps(); @@ -148,7 +148,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { } function getBlockNumber() public view override returns (uint256) { - return 20590050; + return 22241605; } function checkEq( @@ -171,7 +171,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { gibDBR(userPk, styCRVAmount); uint maxBorrowAmount = _getMaxBorrowAmount(styCRVAmount); - + console.log("maxBorrowAmount", maxBorrowAmount); uint256 yCRVAmount = YearnVaultV2Helper.collateralToAsset( IYearnVaultV2(yvyCRVAddr), _convertDolaToCollat(maxBorrowAmount) @@ -205,7 +205,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -213,7 +213,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; ale.leveragePosition( maxBorrowAmount, @@ -285,7 +285,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -293,7 +293,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; ale.depositAndLeveragePosition( styCRVAmount, @@ -371,7 +371,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -379,7 +379,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 89) / 100, 0 @@ -470,11 +470,11 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dbr.balanceOf(userPk), - 0, + 1, 0 ); // sell all DBR @@ -493,8 +493,8 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ale.deleveragePosition( _convertCollatToDola(amountToWithdraw) - 1, // repay little less bc of yearn 1 wei conversion loss address(market), - amountToWithdraw, address(exchangeProxy), + amountToWithdraw, swapData, permit, bytes(""), @@ -572,9 +572,9 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); - ALE.DBRHelper memory dbrData = ALE.DBRHelper(0, 0, borrowAmount / 2); // repay partially debt with DOLA in the wallet + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper(0, 0, borrowAmount / 2); // repay partially debt with DOLA in the wallet bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaOut.selector, @@ -591,8 +591,8 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ale.deleveragePosition( _convertCollatToDola(amountToWithdraw) - 1, address(market), - amountToWithdraw, address(exchangeProxy), + amountToWithdraw, swapData, permit, bytes(""), @@ -674,7 +674,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -682,7 +682,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData = ALE.DBRHelper( + ALEV2.DBRHelper memory dbrData = ALEV2.DBRHelper( dolaForDBR, (dbrAmount * 90) / 100, 0 @@ -708,7 +708,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); } - function test_transformToCollateralAndDeposit(uint256 yCRVAmount) public { + function test_convertToCollateralAndDeposit(uint256 yCRVAmount) public { // Fill userPk with max available deposit yCRV uint256 maxDeposit = IYearnVaultV2(yvyCRVAddr).availableDepositLimit(); // minting is 1:1 deal(CRV, userPk, maxDeposit); @@ -724,7 +724,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { uint256 yCRVBalBefore = IERC20(yCRV).balanceOf(userPk); IERC20(yCRV).approve(address(helper), yCRVAmount); - helper.transformToCollateralAndDeposit(yCRVAmount, ""); + helper.convertToCollateralAndDeposit(yCRVAmount, ""); assertEq(yCRVBalBefore - yCRVAmount, IERC20(yCRV).balanceOf(userPk)); @@ -737,7 +737,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); } - function test_withdrawAndTransformFromCollateral( + function test_withdrawAndConvertFromCollateral( uint256 yCRVAmount ) public { // Fill userPk with max available deposit yCRV @@ -755,7 +755,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { uint256 yCRVBalBefore = IERC20(yCRV).balanceOf(userPk); IERC20(yCRV).approve(address(helper), yCRVAmount); - helper.transformToCollateralAndDeposit(yCRVAmount, ""); + helper.convertToCollateralAndDeposit(yCRVAmount, ""); uint256 yCRVBalAfter = IERC20(yCRV).balanceOf(userPk); assertEq(yCRVBalBefore - yCRVAmount, yCRVBalAfter); @@ -791,7 +791,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { s ); - helper.withdrawAndTransformFromCollateral(amountToWithdraw, permit, ""); + helper.withdrawAndConvertFromCollateral(amountToWithdraw, permit, ""); assertApproxEqAbs( IERC20(yCRV).balanceOf(userPk) - yCRVBalAfter, @@ -807,8 +807,9 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { address fakeMarket = address(0x69); vm.expectRevert( - abi.encodeWithSelector(ALE.NoMarket.selector, fakeMarket) + abi.encodeWithSelector(ALEV2.NoMarket.selector, fakeMarket) ); + vm.prank(gov); ale.setMarket(fakeMarket, address(0), address(0), true); } @@ -817,8 +818,9 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { address newHelper = address(0x70); vm.expectRevert( - abi.encodeWithSelector(ALE.MarketNotSet.selector, wrongMarket) + abi.encodeWithSelector(ALEV2.MarketNotSet.selector, wrongMarket) ); + vm.prank(gov); ale.updateMarketHelper(wrongMarket, newHelper); } @@ -879,7 +881,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { ); (uint8 v, bytes32 r, bytes32 s) = vm.sign(1, hash); - ALE.Permit memory permit = ALE.Permit(block.timestamp, v, r, s); + ALEV2.Permit memory permit = ALEV2.Permit(block.timestamp, v, r, s); bytes memory swapData = abi.encodeWithSelector( MockExchangeProxy.swapDolaIn.selector, @@ -887,7 +889,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { maxBorrowAmount ); - ALE.DBRHelper memory dbrData; + ALEV2.DBRHelper memory dbrData; // Mock call to return 0 buySellToken balance for the ALE vm.mockCall( @@ -896,7 +898,7 @@ contract ALEyvyCRVHelperForkTest is BaseHelperForkTest { abi.encode(uint256(0)) ); - vm.expectRevert(ALE.CollateralIsZero.selector); + vm.expectRevert(ALEV2.CollateralIsZero.selector); ale.leveragePosition( maxBorrowAmount, address(market),