diff --git a/src/mocks/IrmArbitraryMock.sol b/src/mocks/IrmArbitraryMock.sol new file mode 100644 index 000000000..a9333f430 --- /dev/null +++ b/src/mocks/IrmArbitraryMock.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import "src/interfaces/IIrm.sol"; + +contract IrmArbitraryMock is IIrm { + uint256 internal rate; + + function setRate(uint256 newRate) external { + rate = newRate; + } + + function borrowRateView(MarketParams memory, Market memory) public view returns (uint256) { + return rate; + } + + function borrowRate(MarketParams memory marketParams, Market memory market) external view returns (uint256) { + return borrowRateView(marketParams, market); + } +} diff --git a/test/forge/BaseTest.sol b/test/forge/BaseTest.sol index d2684d985..65407b4cd 100644 --- a/test/forge/BaseTest.sol +++ b/test/forge/BaseTest.sol @@ -51,7 +51,7 @@ contract BaseTest is Test { ERC20Mock internal loanToken; ERC20Mock internal collateralToken; OracleMock internal oracle; - IrmMock internal irm; + IIrm internal irm; MarketParams internal marketParams; Id internal id; diff --git a/test/forge/HandlersTest.sol b/test/forge/HandlersTest.sol new file mode 100644 index 000000000..ab771d6d3 --- /dev/null +++ b/test/forge/HandlersTest.sol @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import "./BaseTest.sol"; + +contract HandlersTest is BaseTest { + using MarketParamsLib for MarketParams; + using MorphoBalancesLib for IMorpho; + using MorphoLib for IMorpho; + using SharesMathLib for uint256; + using MathLib for uint256; + + /* MODIFIERS */ + + modifier logCall(string memory name) { + console2.log(msg.sender, "->", name); + + _; + } + + modifier authorized(address onBehalf) { + if (onBehalf != msg.sender) { + vm.prank(onBehalf); + morpho.setAuthorization(msg.sender, true); + } + + _; + + vm.prank(onBehalf); + morpho.setAuthorization(msg.sender, false); + } + + /* UTILS */ + + function _randomSupplier(address[] memory users, MarketParams memory _marketParams, uint256 seed) + internal + view + returns (address) + { + Id _id = _marketParams.id(); + address[] memory candidates = new address[](users.length); + + for (uint256 i; i < users.length; ++i) { + address user = users[i]; + + if (morpho.supplyShares(_id, user) != 0) { + candidates[i] = user; + } + } + + return _randomNonZero(candidates, seed); + } + + function _randomBorrower(address[] memory users, MarketParams memory _marketParams, uint256 seed) + internal + view + returns (address) + { + Id _id = _marketParams.id(); + address[] memory candidates = new address[](users.length); + + for (uint256 i; i < users.length; ++i) { + address user = users[i]; + + if (morpho.borrowShares(_id, user) != 0) { + candidates[i] = user; + } + } + + return _randomNonZero(candidates, seed); + } + + function _randomHealthyCollateralSupplier(address[] memory users, MarketParams memory _marketParams, uint256 seed) + internal + view + returns (address) + { + Id _id = _marketParams.id(); + address[] memory candidates = new address[](users.length); + + for (uint256 i; i < users.length; ++i) { + address user = users[i]; + + if (morpho.collateral(_id, user) != 0 && _isHealthy(_marketParams, user)) { + candidates[i] = user; + } + } + + return _randomNonZero(candidates, seed); + } + + function _randomUnhealthyBorrower(address[] memory users, MarketParams memory _marketParams, uint256 seed) + internal + view + returns (address randomSenderToLiquidate) + { + address[] memory candidates = new address[](users.length); + + for (uint256 i; i < users.length; ++i) { + address user = users[i]; + + if (!_isHealthy(_marketParams, user)) { + candidates[i] = user; + } + } + + return _randomNonZero(candidates, seed); + } + + /* HANDLING TOKENS */ + + function _supplyAssets(MarketParams memory _marketParams, uint256 assets, address onBehalf) + internal + logCall("supplyAssets") + { + loanToken.setBalance(msg.sender, assets); + + vm.prank(msg.sender); + morpho.supply(_marketParams, assets, 0, onBehalf, hex""); + } + + function _supplyShares(MarketParams memory _marketParams, uint256 shares, address onBehalf) + internal + logCall("supplyShares") + { + (uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = morpho.expectedMarketBalances(_marketParams); + + loanToken.setBalance(msg.sender, shares.toAssetsUp(totalSupplyAssets, totalSupplyShares)); + + vm.prank(msg.sender); + morpho.supply(_marketParams, 0, shares, onBehalf, hex""); + } + + function _withdraw( + MarketParams memory _marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + address receiver + ) internal authorized(onBehalf) logCall("withdraw") { + vm.prank(msg.sender); + morpho.withdraw(_marketParams, assets, shares, onBehalf, receiver); + } + + function _borrow( + MarketParams memory _marketParams, + uint256 assets, + uint256 shares, + address onBehalf, + address receiver + ) internal authorized(onBehalf) logCall("borrow") { + vm.prank(msg.sender); + morpho.borrow(_marketParams, assets, shares, onBehalf, receiver); + } + + function _repayAssets(MarketParams memory _marketParams, uint256 assets, address onBehalf) + internal + logCall("repayAssets") + { + loanToken.setBalance(msg.sender, assets); + + vm.prank(msg.sender); + morpho.repay(_marketParams, assets, 0, onBehalf, hex""); + } + + function _repayShares(MarketParams memory _marketParams, uint256 shares, address onBehalf) + internal + logCall("repayShares") + { + (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = morpho.expectedMarketBalances(_marketParams); + + loanToken.setBalance(msg.sender, shares.toAssetsUp(totalBorrowAssets, totalBorrowShares)); + + vm.prank(msg.sender); + morpho.repay(_marketParams, 0, shares, onBehalf, hex""); + } + + function _supplyCollateral(MarketParams memory _marketParams, uint256 assets, address onBehalf) + internal + logCall("supplyCollateral") + { + collateralToken.setBalance(msg.sender, assets); + + vm.prank(msg.sender); + morpho.supplyCollateral(_marketParams, assets, onBehalf, hex""); + } + + function _withdrawCollateral(MarketParams memory _marketParams, uint256 assets, address onBehalf, address receiver) + internal + authorized(onBehalf) + logCall("withdrawCollateral") + { + vm.prank(msg.sender); + morpho.withdrawCollateral(_marketParams, assets, onBehalf, receiver); + } + + function _liquidateSeizedAssets(MarketParams memory _marketParams, address borrower, uint256 seizedAssets) + internal + logCall("liquidateSeizedAssets") + { + uint256 collateralPrice = oracle.price(); + uint256 repaidAssets = seizedAssets.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp( + _liquidationIncentiveFactor(_marketParams.lltv) + ); + + loanToken.setBalance(msg.sender, repaidAssets); + + vm.prank(msg.sender); + morpho.liquidate(_marketParams, borrower, seizedAssets, 0, hex""); + } + + function _liquidateRepaidShares(MarketParams memory _marketParams, address borrower, uint256 repaidShares) + internal + logCall("liquidateRepaidShares") + { + (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = morpho.expectedMarketBalances(_marketParams); + + loanToken.setBalance(msg.sender, repaidShares.toAssetsUp(totalBorrowAssets, totalBorrowShares)); + + vm.prank(msg.sender); + morpho.liquidate(_marketParams, borrower, 0, repaidShares, hex""); + } + + /* HANDLING REVERTS */ + + function _setFeeNoRevert(MarketParams memory marketParams, uint256 newFee) internal { + Id _id = marketParams.id(); + + newFee = bound(newFee, 0, MAX_FEE); + if (newFee == morpho.fee(_id)) return; + + vm.prank(OWNER); + morpho.setFee(marketParams, newFee); + } + + function _supplyAssetsOnBehalfNoRevert(MarketParams memory marketParams, uint256 assets, uint256 onBehalfSeed) + internal + { + address onBehalf = _randomCandidate(targetSenders(), onBehalfSeed); + + assets = _boundSupplyAssets(marketParams, onBehalf, assets); + if (assets == 0) return; + + _supplyAssets(marketParams, assets, onBehalf); + } + + function _supplySharesOnBehalfNoRevert(MarketParams memory marketParams, uint256 shares, uint256 onBehalfSeed) + internal + { + address onBehalf = _randomCandidate(targetSenders(), onBehalfSeed); + + shares = _boundSupplyShares(marketParams, onBehalf, shares); + if (shares == 0) return; + + _supplyShares(marketParams, shares, onBehalf); + } + + function _withdrawAssetsOnBehalfNoRevert( + MarketParams memory marketParams, + uint256 assets, + uint256 onBehalfSeed, + address receiver + ) internal { + receiver = _boundAddressNotZero(receiver); + + address onBehalf = _randomSupplier(targetSenders(), marketParams, onBehalfSeed); + if (onBehalf == address(0)) return; + + assets = _boundWithdrawAssets(marketParams, onBehalf, assets); + if (assets == 0) return; + + _withdraw(marketParams, assets, 0, onBehalf, receiver); + } + + function _borrowAssetsOnBehalfNoRevert( + MarketParams memory marketParams, + uint256 assets, + uint256 onBehalfSeed, + address receiver + ) internal { + receiver = _boundAddressNotZero(receiver); + + address onBehalf = _randomHealthyCollateralSupplier(targetSenders(), marketParams, onBehalfSeed); + if (onBehalf == address(0)) return; + + assets = _boundBorrowAssets(marketParams, onBehalf, assets); + if (assets == 0) return; + + _borrow(marketParams, assets, 0, onBehalf, receiver); + } + + function _repayAssetsOnBehalfNoRevert(MarketParams memory marketParams, uint256 assets, uint256 onBehalfSeed) + internal + { + address onBehalf = _randomBorrower(targetSenders(), marketParams, onBehalfSeed); + if (onBehalf == address(0)) return; + + assets = _boundRepayAssets(marketParams, onBehalf, assets); + if (assets == 0) return; + + _repayAssets(marketParams, assets, onBehalf); + } + + function _repaySharesOnBehalfNoRevert(MarketParams memory marketParams, uint256 shares, uint256 onBehalfSeed) + internal + { + address onBehalf = _randomBorrower(targetSenders(), marketParams, onBehalfSeed); + if (onBehalf == address(0)) return; + + shares = _boundRepayShares(marketParams, onBehalf, shares); + if (shares == 0) return; + + _repayShares(marketParams, shares, onBehalf); + } + + function _supplyCollateralOnBehalfNoRevert(MarketParams memory marketParams, uint256 assets, uint256 onBehalfSeed) + internal + { + address onBehalf = _randomCandidate(targetSenders(), onBehalfSeed); + + assets = _boundSupplyCollateralAssets(marketParams, onBehalf, assets); + if (assets == 0) return; + + _supplyCollateral(marketParams, assets, onBehalf); + } + + function _withdrawCollateralOnBehalfNoRevert( + MarketParams memory marketParams, + uint256 assets, + uint256 onBehalfSeed, + address receiver + ) internal { + receiver = _boundAddressNotZero(receiver); + + address onBehalf = _randomHealthyCollateralSupplier(targetSenders(), marketParams, onBehalfSeed); + if (onBehalf == address(0)) return; + + assets = _boundWithdrawCollateralAssets(marketParams, onBehalf, assets); + if (assets == 0) return; + + _withdrawCollateral(marketParams, assets, onBehalf, receiver); + } + + function _liquidateSeizedAssetsNoRevert( + MarketParams memory marketParams, + uint256 seizedAssets, + uint256 onBehalfSeed + ) internal { + address borrower = _randomUnhealthyBorrower(targetSenders(), marketParams, onBehalfSeed); + if (borrower == address(0)) return; + + seizedAssets = _boundLiquidateSeizedAssets(marketParams, borrower, seizedAssets); + if (seizedAssets == 0) return; + + _liquidateSeizedAssets(marketParams, borrower, seizedAssets); + } + + function _liquidateRepaidSharesNoRevert( + MarketParams memory marketParams, + uint256 repaidShares, + uint256 onBehalfSeed + ) internal { + address borrower = _randomUnhealthyBorrower(targetSenders(), marketParams, onBehalfSeed); + if (borrower == address(0)) return; + + repaidShares = _boundLiquidateRepaidShares(marketParams, borrower, repaidShares); + if (repaidShares == 0) return; + + _liquidateRepaidShares(marketParams, borrower, repaidShares); + } +} diff --git a/test/forge/HealthyTest.sol b/test/forge/HealthyTest.sol new file mode 100644 index 000000000..94617cae3 --- /dev/null +++ b/test/forge/HealthyTest.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import "forge-std/console.sol"; +import "forge-std/console2.sol"; + +import "src/mocks/IrmArbitraryMock.sol"; +import "./HandlersTest.sol"; + +contract HealthyTest is HandlersTest { + using MathLib for uint256; + using SharesMathLib for uint256; + using MorphoLib for IMorpho; + using MarketParamsLib for MarketParams; + + address sender; + + function setUp() public override { + super.setUp(); + IrmArbitraryMock arbitraryIrm = new IrmArbitraryMock(); + arbitraryIrm.setRate(uint256(5e16) / 365 days); // 5% APR // TODO: test with random rate + irm = arbitraryIrm; + vm.prank(OWNER); + morpho.enableIrm(address(irm)); + _setLltv(marketParams.lltv); + } + + uint256 internal constant N = 4; // TODO: test with a larger N + + uint256 internal constant MIN_VALUE = 1e7; + + struct Init { + address[N] user; + uint256[N] collateral; + uint256[N] supply; + uint256[N] borrow; + uint256 roll; + uint256 warp; + } + + function _setUpMarket(Init memory init) public { + uint256 maxElapsed = 14 days; // TODO: test with random values + uint256 maxBorrowMargin = 0.00191e18; // TODO: can be advantageously transformed into a precomputed formula + + sender = userOf(init, 0); + _targetSender(sender); + + for (uint256 i = 0; i < N; i++) { + address user = userOf(init, i); + uint256 collateral = init.collateral[i]; + uint256 supply = init.supply[i]; + uint256 borrow = init.borrow[i]; + + // supply + + collateral = bound(collateral, MIN_VALUE, type(uint128).max); + supply = bound(supply, MIN_VALUE, type(uint128).max / 1e6); + + collateralToken.setBalance(user, type(uint128).max); + loanToken.setBalance(user, type(uint128).max); + + vm.startPrank(user); + collateralToken.approve(address(morpho), type(uint256).max); + loanToken.approve(address(morpho), type(uint256).max); + if (collateral > 0) morpho.supplyCollateral(marketParams, collateral, user, ""); + if (supply > 0) morpho.supply(marketParams, supply, 0, user, ""); + vm.stopPrank(); + + // borrow + + uint256 maxBorrow = _maxBorrow(marketParams, user) * (1e18 - maxBorrowMargin) / 1e18; + uint256 liquidity = morpho.totalSupplyAssets(id) - morpho.totalBorrowAssets(id); + borrow = bound(borrow, 1, UtilsLib.min(maxBorrow, liquidity)); + + vm.startPrank(user); + if (borrow > 0) morpho.borrow(marketParams, borrow, 0, user, user); + vm.stopPrank(); + } + + vm.roll(block.number + bound(init.roll, 0, 2 ** 64)); + vm.warp(block.timestamp + bound(init.warp, 0, maxElapsed)); + } + + function setUpMarket(Init memory init) public { + (bool success,) = address(this).call(abi.encodeWithSelector(this._setUpMarket.selector, init)); + vm.assume(success); + } + + function _targetSender(address _sender) internal { + targetSender(_sender); + + vm.startPrank(_sender); + loanToken.approve(address(morpho), type(uint256).max); + collateralToken.approve(address(morpho), type(uint256).max); + vm.stopPrank(); + } + + function testHealthinessPreservation(Init memory init, uint256 assets, uint256 seed) public { + setUpMarket(init); + + assets = bound(assets, 1, 1e33); // round-up(2^128 / 10^6) + + vm.assume(morphoIsHealthy(marketParams, sender)); + + loanToken.setBalance(sender, type(uint128).max); + _supplyAssetsOnBehalfNoRevert(marketParams, assets, seed); + + assert(morphoIsHealthy(marketParams, sender)); + } + + function userOf(Init memory init, uint256 i) internal pure returns (address user) { + user = init.user[i]; + // to avoid too many zero address failures + // TODO: test zero address cases separately + if (user == address(0)) user = address(1); + } + + function morphoIsHealthy(MarketParams memory _marketParams, address borrower) public view returns (bool) { + Id _id = marketParams.id(); + uint256 maxBorrow = _maxBorrow(_marketParams, borrower); + uint256 borrowed = + morpho.borrowShares(_id, borrower).toAssetsUp(morpho.totalBorrowAssets(_id), morpho.totalBorrowShares(_id)); + + return maxBorrow >= borrowed; + } +} diff --git a/test/forge/InvariantTest.sol b/test/forge/InvariantTest.sol index 4024d5b18..468441bb5 100644 --- a/test/forge/InvariantTest.sol +++ b/test/forge/InvariantTest.sol @@ -1,15 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; -import "./BaseTest.sol"; - -contract InvariantTest is BaseTest { - using MathLib for uint256; - using SharesMathLib for uint256; - using MorphoLib for IMorpho; - using MorphoBalancesLib for IMorpho; - using MarketParamsLib for MarketParams; +import "./HandlersTest.sol"; +contract InvariantTest is HandlersTest { bytes4[] internal selectors; function setUp() public virtual override { @@ -23,12 +17,6 @@ contract InvariantTest is BaseTest { targetSelector(FuzzSelector({addr: address(this), selectors: selectors})); } - modifier logCall(string memory name) { - console2.log(msg.sender, "->", name); - - _; - } - function _targetSenders() internal virtual { _targetSender(_addrFromHashedString("Sender1")); _targetSender(_addrFromHashedString("Sender2")); @@ -55,88 +43,11 @@ contract InvariantTest is BaseTest { } } - /* HANDLERS */ + /* SELECTED FUNCTIONS */ function mine(uint256 blocks) external { blocks = bound(blocks, 1, 50_400); _forward(blocks); } - - /* UTILS */ - - function _randomSupplier(address[] memory users, MarketParams memory _marketParams, uint256 seed) - internal - view - returns (address) - { - Id _id = _marketParams.id(); - address[] memory candidates = new address[](users.length); - - for (uint256 i; i < users.length; ++i) { - address user = users[i]; - - if (morpho.supplyShares(_id, user) != 0) { - candidates[i] = user; - } - } - - return _randomNonZero(candidates, seed); - } - - function _randomBorrower(address[] memory users, MarketParams memory _marketParams, uint256 seed) - internal - view - returns (address) - { - Id _id = _marketParams.id(); - address[] memory candidates = new address[](users.length); - - for (uint256 i; i < users.length; ++i) { - address user = users[i]; - - if (morpho.borrowShares(_id, user) != 0) { - candidates[i] = user; - } - } - - return _randomNonZero(candidates, seed); - } - - function _randomHealthyCollateralSupplier(address[] memory users, MarketParams memory _marketParams, uint256 seed) - internal - view - returns (address) - { - Id _id = _marketParams.id(); - address[] memory candidates = new address[](users.length); - - for (uint256 i; i < users.length; ++i) { - address user = users[i]; - - if (morpho.collateral(_id, user) != 0 && _isHealthy(_marketParams, user)) { - candidates[i] = user; - } - } - - return _randomNonZero(candidates, seed); - } - - function _randomUnhealthyBorrower(address[] memory users, MarketParams memory _marketParams, uint256 seed) - internal - view - returns (address randomSenderToLiquidate) - { - address[] memory candidates = new address[](users.length); - - for (uint256 i; i < users.length; ++i) { - address user = users[i]; - - if (!_isHealthy(_marketParams, user)) { - candidates[i] = user; - } - } - - return _randomNonZero(candidates, seed); - } } diff --git a/test/forge/invariant/MorphoInvariantTest.sol b/test/forge/invariant/MorphoInvariantTest.sol index ac363ad75..b10728507 100644 --- a/test/forge/invariant/MorphoInvariantTest.sol +++ b/test/forge/invariant/MorphoInvariantTest.sol @@ -61,135 +61,11 @@ contract MorphoInvariantTest is InvariantTest { _targetSender(USER); } - modifier authorized(address onBehalf) { - if (onBehalf != msg.sender) { - vm.prank(onBehalf); - morpho.setAuthorization(msg.sender, true); - } - - _; - - vm.prank(onBehalf); - morpho.setAuthorization(msg.sender, false); - } - function _randomMarket(uint256 marketSeed) internal view returns (MarketParams memory _marketParams) { return allMarketParams[marketSeed % allMarketParams.length]; } - function _supplyAssets(MarketParams memory _marketParams, uint256 assets, address onBehalf) - internal - logCall("supplyAssets") - { - loanToken.setBalance(msg.sender, assets); - - vm.prank(msg.sender); - morpho.supply(_marketParams, assets, 0, onBehalf, hex""); - } - - function _supplyShares(MarketParams memory _marketParams, uint256 shares, address onBehalf) - internal - logCall("supplyShares") - { - (uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = morpho.expectedMarketBalances(_marketParams); - - loanToken.setBalance(msg.sender, shares.toAssetsUp(totalSupplyAssets, totalSupplyShares)); - - vm.prank(msg.sender); - morpho.supply(_marketParams, 0, shares, onBehalf, hex""); - } - - function _withdraw( - MarketParams memory _marketParams, - uint256 assets, - uint256 shares, - address onBehalf, - address receiver - ) internal authorized(onBehalf) logCall("withdraw") { - vm.prank(msg.sender); - morpho.withdraw(_marketParams, assets, shares, onBehalf, receiver); - } - - function _borrow( - MarketParams memory _marketParams, - uint256 assets, - uint256 shares, - address onBehalf, - address receiver - ) internal authorized(onBehalf) logCall("borrow") { - vm.prank(msg.sender); - morpho.borrow(_marketParams, assets, shares, onBehalf, receiver); - } - - function _repayAssets(MarketParams memory _marketParams, uint256 assets, address onBehalf) - internal - logCall("repayAssets") - { - loanToken.setBalance(msg.sender, assets); - - vm.prank(msg.sender); - morpho.repay(_marketParams, assets, 0, onBehalf, hex""); - } - - function _repayShares(MarketParams memory _marketParams, uint256 shares, address onBehalf) - internal - logCall("repayShares") - { - (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = morpho.expectedMarketBalances(_marketParams); - - loanToken.setBalance(msg.sender, shares.toAssetsUp(totalBorrowAssets, totalBorrowShares)); - - vm.prank(msg.sender); - morpho.repay(_marketParams, 0, shares, onBehalf, hex""); - } - - function _supplyCollateral(MarketParams memory _marketParams, uint256 assets, address onBehalf) - internal - logCall("supplyCollateral") - { - collateralToken.setBalance(msg.sender, assets); - - vm.prank(msg.sender); - morpho.supplyCollateral(_marketParams, assets, onBehalf, hex""); - } - - function _withdrawCollateral(MarketParams memory _marketParams, uint256 assets, address onBehalf, address receiver) - internal - authorized(onBehalf) - logCall("withdrawCollateral") - { - vm.prank(msg.sender); - morpho.withdrawCollateral(_marketParams, assets, onBehalf, receiver); - } - - function _liquidateSeizedAssets(MarketParams memory _marketParams, address borrower, uint256 seizedAssets) - internal - logCall("liquidateSeizedAssets") - { - uint256 collateralPrice = oracle.price(); - uint256 repaidAssets = seizedAssets.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE).wDivUp( - _liquidationIncentiveFactor(_marketParams.lltv) - ); - - loanToken.setBalance(msg.sender, repaidAssets); - - vm.prank(msg.sender); - morpho.liquidate(_marketParams, borrower, seizedAssets, 0, hex""); - } - - function _liquidateRepaidShares(MarketParams memory _marketParams, address borrower, uint256 repaidShares) - internal - logCall("liquidateRepaidShares") - { - (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = morpho.expectedMarketBalances(_marketParams); - - loanToken.setBalance(msg.sender, repaidShares.toAssetsUp(totalBorrowAssets, totalBorrowShares)); - - vm.prank(msg.sender); - morpho.liquidate(_marketParams, borrower, 0, repaidShares, hex""); - } - - /* HANDLERS */ + /* SELECTED FUNCTIONS */ function setPrice(uint256 price) external { price = bound(price, MIN_PRICE, MAX_PRICE); @@ -199,102 +75,46 @@ contract MorphoInvariantTest is InvariantTest { function setFeeNoRevert(uint256 marketSeed, uint256 newFee) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - Id _id = _marketParams.id(); - - newFee = bound(newFee, 0, MAX_FEE); - if (newFee == morpho.fee(_id)) return; - - vm.prank(OWNER); - morpho.setFee(_marketParams, newFee); + _setFeeNoRevert(_marketParams, newFee); } function supplyAssetsOnBehalfNoRevert(uint256 marketSeed, uint256 assets, uint256 onBehalfSeed) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomCandidate(targetSenders(), onBehalfSeed); - - assets = _boundSupplyAssets(_marketParams, USER, assets); - if (assets == 0) return; - - _supplyAssets(_marketParams, assets, onBehalf); + _supplyAssetsOnBehalfNoRevert(_marketParams, assets, onBehalfSeed); } function supplySharesOnBehalfNoRevert(uint256 marketSeed, uint256 shares, uint256 onBehalfSeed) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomCandidate(targetSenders(), onBehalfSeed); - - shares = _boundSupplyShares(_marketParams, onBehalf, shares); - if (shares == 0) return; - - _supplyShares(_marketParams, shares, onBehalf); + _supplySharesOnBehalfNoRevert(_marketParams, shares, onBehalfSeed); } function withdrawAssetsOnBehalfNoRevert(uint256 marketSeed, uint256 assets, uint256 onBehalfSeed, address receiver) external { - receiver = _boundAddressNotZero(receiver); - MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomSupplier(targetSenders(), _marketParams, onBehalfSeed); - if (onBehalf == address(0)) return; - - assets = _boundWithdrawAssets(_marketParams, onBehalf, assets); - if (assets == 0) return; - - _withdraw(_marketParams, assets, 0, onBehalf, receiver); + _withdrawAssetsOnBehalfNoRevert(_marketParams, assets, onBehalfSeed, receiver); } function borrowAssetsOnBehalfNoRevert(uint256 marketSeed, uint256 assets, uint256 onBehalfSeed, address receiver) external { - receiver = _boundAddressNotZero(receiver); - MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomHealthyCollateralSupplier(targetSenders(), _marketParams, onBehalfSeed); - if (onBehalf == address(0)) return; - - assets = _boundBorrowAssets(_marketParams, onBehalf, assets); - if (assets == 0) return; - - _borrow(_marketParams, assets, 0, onBehalf, receiver); + _borrowAssetsOnBehalfNoRevert(_marketParams, assets, onBehalfSeed, receiver); } function repayAssetsOnBehalfNoRevert(uint256 marketSeed, uint256 assets, uint256 onBehalfSeed) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomBorrower(targetSenders(), _marketParams, onBehalfSeed); - if (onBehalf == address(0)) return; - - assets = _boundRepayAssets(_marketParams, onBehalf, assets); - if (assets == 0) return; - - _repayAssets(_marketParams, assets, onBehalf); + _repayAssetsOnBehalfNoRevert(_marketParams, assets, onBehalfSeed); } function repaySharesOnBehalfNoRevert(uint256 marketSeed, uint256 shares, uint256 onBehalfSeed) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomBorrower(targetSenders(), _marketParams, onBehalfSeed); - if (onBehalf == address(0)) return; - - shares = _boundRepayShares(_marketParams, onBehalf, shares); - if (shares == 0) return; - - _repayShares(_marketParams, shares, onBehalf); + _repaySharesOnBehalfNoRevert(_marketParams, shares, onBehalfSeed); } function supplyCollateralOnBehalfNoRevert(uint256 marketSeed, uint256 assets, uint256 onBehalfSeed) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomCandidate(targetSenders(), onBehalfSeed); - - assets = _boundSupplyCollateralAssets(_marketParams, onBehalf, assets); - if (assets == 0) return; - - _supplyCollateral(_marketParams, assets, onBehalf); + _supplyCollateralOnBehalfNoRevert(_marketParams, assets, onBehalfSeed); } function withdrawCollateralOnBehalfNoRevert( @@ -303,41 +123,18 @@ contract MorphoInvariantTest is InvariantTest { uint256 onBehalfSeed, address receiver ) external { - receiver = _boundAddressNotZero(receiver); - MarketParams memory _marketParams = _randomMarket(marketSeed); - - address onBehalf = _randomHealthyCollateralSupplier(targetSenders(), _marketParams, onBehalfSeed); - if (onBehalf == address(0)) return; - - assets = _boundWithdrawCollateralAssets(_marketParams, onBehalf, assets); - if (assets == 0) return; - - _withdrawCollateral(_marketParams, assets, onBehalf, receiver); + _withdrawCollateralOnBehalfNoRevert(_marketParams, assets, onBehalfSeed, receiver); } function liquidateSeizedAssetsNoRevert(uint256 marketSeed, uint256 seizedAssets, uint256 onBehalfSeed) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - - address borrower = _randomUnhealthyBorrower(targetSenders(), _marketParams, onBehalfSeed); - if (borrower == address(0)) return; - - seizedAssets = _boundLiquidateSeizedAssets(_marketParams, borrower, seizedAssets); - if (seizedAssets == 0) return; - - _liquidateSeizedAssets(_marketParams, borrower, seizedAssets); + _liquidateSeizedAssetsNoRevert(_marketParams, seizedAssets, onBehalfSeed); } function liquidateRepaidSharesNoRevert(uint256 marketSeed, uint256 repaidShares, uint256 onBehalfSeed) external { MarketParams memory _marketParams = _randomMarket(marketSeed); - - address borrower = _randomUnhealthyBorrower(targetSenders(), _marketParams, onBehalfSeed); - if (borrower == address(0)) return; - - repaidShares = _boundLiquidateRepaidShares(_marketParams, borrower, repaidShares); - if (repaidShares == 0) return; - - _liquidateRepaidShares(_marketParams, borrower, repaidShares); + _liquidateRepaidSharesNoRevert(_marketParams, repaidShares, onBehalfSeed); } /* INVARIANTS */