diff --git a/contracts/credit/CreditFacadeV3.sol b/contracts/credit/CreditFacadeV3.sol index 78ec03c4..23858ff7 100644 --- a/contracts/credit/CreditFacadeV3.sol +++ b/contracts/credit/CreditFacadeV3.sol @@ -482,6 +482,7 @@ contract CreditFacadeV3 is ICreditFacade, ACLNonReentrantTrait, BalanceHelperTra uint256 enabledTokensMask, uint256 flags ) internal returns (FullCheckParams memory fullCheckParams) { + uint256 limitedTokenMask; // Emits event for multicall start - used in analytics to track actions within multicalls emit StartMultiCall(borrower); // F:[FA-26] diff --git a/contracts/credit/CreditManagerV3.sol b/contracts/credit/CreditManagerV3.sol index 291febbf..85d2fdba 100644 --- a/contracts/credit/CreditManagerV3.sol +++ b/contracts/credit/CreditManagerV3.sol @@ -742,6 +742,13 @@ contract CreditManagerV3 is ICreditManagerV3, SanityCheckTrait, ReentrancyGuard, _calcAllCollateral(_priceOracle, creditAccount, enabledTokenMask, PERCENTAGE_FACTOR, collateralHints, false); // add withdrawal balances to TotalValue + if (hasWithdrawalValue(creditAccount)) { + (uint256 withdrawValueUSD, uint256 tokensToEnable) = + _calcDelayedWithdrawalValue(_priceOracle, creditAccount); + + totalUSD += withdrawValueUSD; + enabledTokenMask |= tokensToEnable; + } total = _convertFromUSD(_priceOracle, totalUSD, underlying); // F:[FA-41] hf = twvUSD * PERCENTAGE_FACTOR / borrowAmountPlusInterestRateAndFeesUSD; @@ -899,29 +906,29 @@ contract CreditManagerV3 is ICreditManagerV3, SanityCheckTrait, ReentrancyGuard, function _calcDelayedWithdrawalValue(IPriceOracleV2 _priceOracle, address creditAccount) internal view - returns (uint256 amount, uint256 enabledTokenMask) + returns (uint256 withdrawValueUSD, uint256 tokensToEnable) { (uint256 tokenMask1, uint256 balance1, uint256 tokenMask2, uint256 balance2) = withdrawManager.getWithdrawals(address(this), creditAccount); - (amount, enabledTokenMask) = _calcWithdrawalValueUSD(0, 0, _priceOracle, tokenMask1, balance1); - (amount, enabledTokenMask) = - _calcWithdrawalValueUSD(amount, enabledTokenMask, _priceOracle, tokenMask2, balance2); + (withdrawValueUSD, tokensToEnable) = _calcWithdrawalValueUSD(0, 0, _priceOracle, tokenMask1, balance1); + (withdrawValueUSD, tokensToEnable) = + _calcWithdrawalValueUSD(withdrawValueUSD, tokensToEnable, _priceOracle, tokenMask2, balance2); } function _calcWithdrawalValueUSD( - uint256 _amount, + uint256 _withdrawValueUSD, uint256 _tokensToEnbable, IPriceOracleV2 _priceOracle, uint256 tokenMask, uint256 balance - ) internal view returns (uint256 amount, uint256 tokensToEnable) { - amount = _amount; + ) internal view returns (uint256 withdrawValueUSD, uint256 tokensToEnable) { + withdrawValueUSD = _withdrawValueUSD; tokensToEnable = _tokensToEnbable; if (balance > 1) { address token = getTokenByMask(tokenMask); - amount += _convertToUSD(_priceOracle, balance, token); + withdrawValueUSD += _convertToUSD(_priceOracle, balance, token); tokensToEnable |= tokenMask; } } @@ -1603,7 +1610,7 @@ contract CreditManagerV3 is ICreditManagerV3, SanityCheckTrait, ReentrancyGuard, creditFacadeOnly returns (uint256 tokensToEnable) { - if (creditAccountInfo[creditAccount].flags & WITHDRAWAL_FLAG != 0) { + if (hasWithdrawalValue(creditAccount)) { return withdrawManager.cancelWithdrawals(creditAccount, ctype); } } @@ -1653,6 +1660,10 @@ contract CreditManagerV3 is ICreditManagerV3, SanityCheckTrait, ReentrancyGuard, creditAccountInfo[creditAccount].enabledTokensMask = uint248(enabledTokenMask); } + function hasWithdrawalValue(address creditAccount) internal view returns (bool) { + return creditAccountInfo[creditAccount].flags & WITHDRAWAL_FLAG != 0; + } + function _enableWithdrawalFlag(address creditAccount) internal { creditAccountInfo[creditAccount].flags |= WITHDRAWAL_FLAG; } diff --git a/contracts/test/unit/credit/CreditManager.t.sol b/contracts/test/unit/credit/CreditManager.t.sol index 67c5c11d..4503ec02 100644 --- a/contracts/test/unit/credit/CreditManager.t.sol +++ b/contracts/test/unit/credit/CreditManager.t.sol @@ -29,10 +29,11 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20Mock} from "@gearbox-protocol/core-v2/contracts/test/mocks/token/ERC20Mock.sol"; import {PERCENTAGE_FACTOR} from "@gearbox-protocol/core-v2/contracts/libraries/PercentageMath.sol"; +// LIBS & TRAITS +import {BitMask} from "../../../libraries/BitMask.sol"; // TESTS import "../../lib/constants.sol"; - import {BalanceHelper} from "../../helpers/BalanceHelper.sol"; // EXCEPTIONS @@ -63,6 +64,8 @@ import "forge-std/console.sol"; /// @title AddressRepository /// @notice Stores addresses of deployed contracts contract CreditManagerTest is DSTest, ICreditManagerV3Events, BalanceHelper { + using BitMask for uint256; + CheatCodes evm = CheatCodes(HEVM_ADDRESS); CreditManagerTestSuite cms; @@ -232,70 +235,6 @@ contract CreditManagerTest is DSTest, ICreditManagerV3Events, BalanceHelper { _addAndEnableTokens(creditAccount, maxAllowedEnabledTokenLength, 2); } - // function prepareForEnabledTokenOptimization( - // address creditAccount, - // bool[] memory tokenTypes, - // uint256 enabledTokensNum, - // uint256 zeroTokensNum, - // uint256 breakPoint - // ) internal returns (uint256) { - // if (enabledTokensNum == 0) { - // return 1; - // } - - // bool setBreakpoint; - - // if (enabledTokensNum != zeroTokensNum) { - // // When there are more enabled tokens than zero tokens, we have a breakpoint other than underlying - - // uint256 daiBalance = tokenTestSuite.balanceOf(Tokens.DAI, creditAccount); - // tokenTestSuite.burn(Tokens.DAI, creditAccount, daiBalance); - // setBreakpoint = true; - // } else { - // // When there is the same number of enabled and zero tokens, only the underlying will be checked in fullCheck, - // // hence all tokens + underlying will remain enabled before optimizer is run - - // enabledTokensNum += 1; - // } - - // for (uint256 i = 0; i < tokenTypes.length; i++) { - // if ((i == breakPoint) && setBreakpoint) { - // _addAndEnableTokens(creditAccount, 1, RAY); - // } else if (tokenTypes[i]) { - // _addAndEnableTokens(creditAccount, 1, 2); - // } else { - // _addAndEnableTokens(creditAccount, 1, 1); - // if ((i > breakPoint) && setBreakpoint) { - // enabledTokensNum--; - // } - // } - // } - - // return enabledTokensNum; - // } - - function calcEnabledTokens(address creditAccount) internal view returns (uint256) { - uint256 enabledMask = creditManager.enabledTokensMap(creditAccount); - - uint256 tokensEnabled; - - uint256 tokenMask; - unchecked { - for (uint256 i; i < 256; ++i) { - tokenMask = 1 << i; - if (enabledMask & tokenMask != 0) { - ++tokensEnabled; - } - - if (tokenMask >= enabledMask) { - break; - } - } - } - - return tokensEnabled; - } - function _openAccountAndTransferToCF() internal returns (address creditAccount) { (,,, creditAccount) = _openCreditAccount(); creditManager.transferAccountOwnership(USER, address(this)); @@ -1514,11 +1453,12 @@ contract CreditManagerTest is DSTest, ICreditManagerV3Events, BalanceHelper { hints[i] = 2 ** (totalTokens - i - 1); } } - // _baseFullCollateralCheck(creditAccount); creditManager.fullCollateralCheck(creditAccount, 2 ** (totalTokens) - 1, hints, 10000); - assertEq(calcEnabledTokens(creditAccount), 1, "Incorrect number of tokens enabled"); + assertEq( + creditManager.enabledTokensMap(creditAccount).calcEnabledTokens(), 1, "Incorrect number of tokens enabled" + ); } /// @dev [CM-42]: fullCollateralCheck fuzzing test