diff --git a/test/forge/BaseTest.sol b/test/forge/BaseTest.sol index 1544d6ccc..1d83a08de 100644 --- a/test/forge/BaseTest.sol +++ b/test/forge/BaseTest.sol @@ -24,6 +24,8 @@ contract BaseTest is Test { uint256 internal constant MAX_TEST_AMOUNT = 1e28; uint256 internal constant MIN_TEST_SHARES = MIN_TEST_AMOUNT * SharesMathLib.VIRTUAL_SHARES; uint256 internal constant MAX_TEST_SHARES = MAX_TEST_AMOUNT * SharesMathLib.VIRTUAL_SHARES; + uint256 internal constant MIN_TEST_LLTV = 0.01 ether; + uint256 internal constant MAX_TEST_LLTV = 0.99 ether; uint256 internal constant MIN_COLLATERAL_PRICE = 1e10; uint256 internal constant MAX_COLLATERAL_PRICE = 1e40; uint256 internal constant MAX_COLLATERAL_ASSETS = type(uint128).max; @@ -36,8 +38,6 @@ contract BaseTest is Test { address internal LIQUIDATOR = _addrFromHashedString("Morpho Liquidator"); address internal OWNER = _addrFromHashedString("Morpho Owner"); - uint256 internal constant LLTV = 0.8 ether; - Morpho internal morpho; ERC20 internal borrowableToken; ERC20 internal collateralToken; @@ -45,6 +45,7 @@ contract BaseTest is Test { Irm internal irm; MarketParams internal marketParams; Id internal id; + uint256 internal lltv; function setUp() public virtual { vm.label(OWNER, "Owner"); @@ -74,14 +75,8 @@ contract BaseTest is Test { irm = new Irm(); vm.label(address(irm), "IRM"); - marketParams = - MarketParams(address(borrowableToken), address(collateralToken), address(oracle), address(irm), LLTV); - id = marketParams.id(); - vm.startPrank(OWNER); morpho.enableIrm(address(irm)); - morpho.enableLltv(LLTV); - morpho.createMarket(marketParams); vm.stopPrank(); borrowableToken.approve(address(morpho), type(uint256).max); @@ -108,6 +103,20 @@ contract BaseTest is Test { morpho.setAuthorization(BORROWER, true); vm.stopPrank(); + _setLltv(0.8 ether); + } + + function _setLltv(uint256 _lltv) internal { + lltv = _lltv; + marketParams = + MarketParams(address(borrowableToken), address(collateralToken), address(oracle), address(irm), _lltv); + id = marketParams.id(); + + vm.startPrank(OWNER); + if (!morpho.isLltvEnabled(_lltv)) morpho.enableLltv(_lltv); + if (morpho.lastUpdate(marketParams.id()) == 0) morpho.createMarket(marketParams); + vm.stopPrank(); + vm.roll(block.number + 1); vm.warp(block.timestamp + 1 days); } @@ -170,17 +179,21 @@ contract BaseTest is Test { return (amountCollateral, amountBorrowed, priceCollateral); } - function _boundValidLltv(uint256 lltv) internal view returns (uint256) { - return bound(lltv, 0, WAD - 1); + function _boundTestLltv(uint256 _lltv) internal view returns (uint256) { + return bound(_lltv, MIN_TEST_LLTV, MAX_TEST_LLTV); + } + + function _boundValidLltv(uint256 _lltv) internal view returns (uint256) { + return bound(_lltv, 0, WAD - 1); } - function _boundInvalidLltv(uint256 lltv) internal view returns (uint256) { - return bound(lltv, WAD, type(uint256).max); + function _boundInvalidLltv(uint256 _lltv) internal view returns (uint256) { + return bound(_lltv, WAD, type(uint256).max); } - function _liquidationIncentive(uint256 lltv) internal pure returns (uint256) { + function _liquidationIncentive(uint256 _lltv) internal pure returns (uint256) { return - UtilsLib.min(MAX_LIQUIDATION_INCENTIVE_FACTOR, WAD.wDivDown(WAD - LIQUIDATION_CURSOR.wMulDown(WAD - lltv))); + UtilsLib.min(MAX_LIQUIDATION_INCENTIVE_FACTOR, WAD.wDivDown(WAD - LIQUIDATION_CURSOR.wMulDown(WAD - _lltv))); } function _accrueInterest(MarketParams memory market) internal { diff --git a/test/forge/integration/AuthorizationIntegrationTest.sol b/test/forge/integration/AuthorizationIntegrationTest.sol index ebc0da0d1..ab95b66f9 100644 --- a/test/forge/integration/AuthorizationIntegrationTest.sol +++ b/test/forge/integration/AuthorizationIntegrationTest.sol @@ -88,4 +88,22 @@ contract AuthorizationIntegrationTest is BaseTest { assertEq(morpho.isAuthorized(authorization.authorizer, authorization.authorized), authorization.isAuthorized); assertEq(morpho.nonce(authorization.authorizer), 1); } + + function testAuthorizationFailsWithReusedSig(Authorization memory authorization, uint256 privateKey) public { + authorization.deadline = bound(authorization.deadline, block.timestamp + 1, type(uint256).max); + + // Private key must be less than the secp256k1 curve order. + privateKey = bound(privateKey, 1, type(uint32).max); + authorization.nonce = 0; + authorization.authorizer = vm.addr(privateKey); + + Signature memory sig; + bytes32 digest = SigUtils.getTypedDataHash(morpho.DOMAIN_SEPARATOR(), authorization); + (sig.v, sig.r, sig.s) = vm.sign(privateKey, digest); + + morpho.setAuthorizationWithSig(authorization, sig); + + vm.expectRevert(bytes(ErrorsLib.INVALID_NONCE)); + morpho.setAuthorizationWithSig(authorization, sig); + } } diff --git a/test/forge/integration/CreateMarketIntegrationTest.sol b/test/forge/integration/CreateMarketIntegrationTest.sol index 3c4d62fcf..f393c6550 100644 --- a/test/forge/integration/CreateMarketIntegrationTest.sol +++ b/test/forge/integration/CreateMarketIntegrationTest.sol @@ -9,7 +9,7 @@ contract CreateMarketIntegrationTest is BaseTest { using MarketParamsLib for MarketParams; function testCreateMarketWithNotEnabledIrmAndNotEnabledLltv(MarketParams memory marketParamsFuzz) public { - vm.assume(marketParamsFuzz.irm != address(irm) && marketParamsFuzz.lltv != LLTV); + vm.assume(marketParamsFuzz.irm != address(irm) && marketParamsFuzz.lltv != lltv); vm.prank(OWNER); vm.expectRevert(bytes(ErrorsLib.IRM_NOT_ENABLED)); @@ -21,7 +21,7 @@ contract CreateMarketIntegrationTest is BaseTest { marketParamsFuzz.lltv = _boundValidLltv(marketParamsFuzz.lltv); vm.startPrank(OWNER); - if (marketParamsFuzz.lltv != LLTV) morpho.enableLltv(marketParamsFuzz.lltv); + if (marketParamsFuzz.lltv != lltv) morpho.enableLltv(marketParamsFuzz.lltv); vm.expectRevert(bytes(ErrorsLib.IRM_NOT_ENABLED)); morpho.createMarket(marketParamsFuzz); @@ -29,7 +29,7 @@ contract CreateMarketIntegrationTest is BaseTest { } function testCreateMarketWithEnabledIrmAndNotEnabledLltv(MarketParams memory marketParamsFuzz) public { - vm.assume(marketParamsFuzz.lltv != LLTV); + vm.assume(marketParamsFuzz.lltv != lltv); vm.startPrank(OWNER); if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm); @@ -45,7 +45,7 @@ contract CreateMarketIntegrationTest is BaseTest { vm.startPrank(OWNER); if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm); - if (marketParamsFuzz.lltv != LLTV) morpho.enableLltv(marketParamsFuzz.lltv); + if (marketParamsFuzz.lltv != lltv) morpho.enableLltv(marketParamsFuzz.lltv); vm.expectEmit(true, true, true, true, address(morpho)); emit EventsLib.CreateMarket(marketParamsFuzz.id(), marketParamsFuzz); @@ -65,7 +65,7 @@ contract CreateMarketIntegrationTest is BaseTest { vm.startPrank(OWNER); if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm); - if (marketParamsFuzz.lltv != LLTV) morpho.enableLltv(marketParamsFuzz.lltv); + if (marketParamsFuzz.lltv != lltv) morpho.enableLltv(marketParamsFuzz.lltv); morpho.createMarket(marketParamsFuzz); vm.expectRevert(bytes(ErrorsLib.MARKET_ALREADY_CREATED)); @@ -79,7 +79,7 @@ contract CreateMarketIntegrationTest is BaseTest { vm.startPrank(OWNER); if (marketParamsFuzz.irm != marketParams.irm) morpho.enableIrm(marketParamsFuzz.irm); - if (marketParamsFuzz.lltv != LLTV) morpho.enableLltv(marketParamsFuzz.lltv); + if (marketParamsFuzz.lltv != lltv) morpho.enableLltv(marketParamsFuzz.lltv); morpho.createMarket(marketParamsFuzz); vm.stopPrank(); diff --git a/test/forge/integration/LiquidateIntegrationTest.sol b/test/forge/integration/LiquidateIntegrationTest.sol index 311460e61..c4bfa4a18 100644 --- a/test/forge/integration/LiquidateIntegrationTest.sol +++ b/test/forge/integration/LiquidateIntegrationTest.sol @@ -8,14 +8,16 @@ contract LiquidateIntegrationTest is BaseTest { using MorphoLib for Morpho; using SharesMathLib for uint256; - function testLiquidateNotCreatedMarket(MarketParams memory marketParamsFuzz) public { + function testLiquidateNotCreatedMarket(MarketParams memory marketParamsFuzz, uint256 lltv) public { + _setLltv(_boundTestLltv(lltv)); vm.assume(neq(marketParamsFuzz, marketParams)); vm.expectRevert(bytes(ErrorsLib.MARKET_NOT_CREATED)); morpho.liquidate(marketParamsFuzz, address(this), 1, 0, hex""); } - function testLiquidateZeroAmount() public { + function testLiquidateZeroAmount(uint256 lltv) public { + _setLltv(_boundTestLltv(lltv)); vm.prank(BORROWER); vm.expectRevert(bytes(ErrorsLib.INCONSISTENT_INPUT)); @@ -37,12 +39,14 @@ contract LiquidateIntegrationTest is BaseTest { uint256 amountSupplied, uint256 amountBorrowed, uint256 amountSeized, - uint256 priceCollateral + uint256 priceCollateral, + uint256 lltv ) public { + _setLltv(_boundTestLltv(lltv)); (amountCollateral, amountBorrowed, priceCollateral) = _boundHealthyPosition(amountCollateral, amountBorrowed, priceCollateral); - amountSupplied = bound(amountSupplied, amountBorrowed, MAX_TEST_AMOUNT); + amountSupplied = bound(amountSupplied, amountBorrowed, amountBorrowed + MAX_TEST_AMOUNT); _supply(amountSupplied); amountSeized = bound(amountSeized, 1, amountCollateral); @@ -67,18 +71,21 @@ contract LiquidateIntegrationTest is BaseTest { uint256 amountSupplied, uint256 amountBorrowed, uint256 amountSeized, - uint256 priceCollateral + uint256 priceCollateral, + uint256 lltv ) public { + _setLltv(_boundTestLltv(lltv)); (amountCollateral, amountBorrowed, priceCollateral) = _boundUnhealthyPosition(amountCollateral, amountBorrowed, priceCollateral); vm.assume(amountCollateral > 1); - amountSupplied = bound(amountSupplied, amountBorrowed, MAX_TEST_AMOUNT); + amountSupplied = bound(amountSupplied, amountBorrowed, amountBorrowed + MAX_TEST_AMOUNT); _supply(amountSupplied); uint256 incentive = _liquidationIncentive(marketParams.lltv); uint256 maxSeized = amountBorrowed.wMulDown(incentive).mulDivDown(ORACLE_PRICE_SCALE, priceCollateral); + vm.assume(maxSeized != 0); amountSeized = bound(amountSeized, 1, min(maxSeized, amountCollateral - 1)); uint256 expectedRepaid = amountSeized.mulDivUp(priceCollateral, ORACLE_PRICE_SCALE).wDivUp(incentive); @@ -129,8 +136,10 @@ contract LiquidateIntegrationTest is BaseTest { uint256 amountSupplied, uint256 amountBorrowed, uint256 sharesRepaid, - uint256 priceCollateral + uint256 priceCollateral, + uint256 lltv ) public { + _setLltv(_boundTestLltv(lltv)); (amountCollateral, amountBorrowed, priceCollateral) = _boundUnhealthyPosition(amountCollateral, amountBorrowed, priceCollateral); @@ -204,8 +213,10 @@ contract LiquidateIntegrationTest is BaseTest { uint256 amountCollateral, uint256 amountSupplied, uint256 amountBorrowed, - uint256 priceCollateral + uint256 priceCollateral, + uint256 lltv ) public { + _setLltv(_boundTestLltv(lltv)); LiquidateBadDebtTestParams memory params; (amountCollateral, amountBorrowed, priceCollateral) = diff --git a/test/forge/integration/OnlyOwnerIntegrationTest.sol b/test/forge/integration/OnlyOwnerIntegrationTest.sol index 6ffe4f396..45f0be8de 100644 --- a/test/forge/integration/OnlyOwnerIntegrationTest.sol +++ b/test/forge/integration/OnlyOwnerIntegrationTest.sol @@ -65,7 +65,7 @@ contract OnlyOwnerIntegrationTest is BaseTest { function testEnableLltvWhenNotOwner(address addressFuzz, uint256 lltvFuzz) public { vm.assume(addressFuzz != OWNER); - vm.assume(lltvFuzz != LLTV); + vm.assume(lltvFuzz != lltv); vm.prank(addressFuzz); vm.expectRevert(bytes(ErrorsLib.NOT_OWNER)); @@ -75,7 +75,7 @@ contract OnlyOwnerIntegrationTest is BaseTest { function testEnableLltvAlreadySet() public { vm.prank(OWNER); vm.expectRevert(bytes(ErrorsLib.ALREADY_SET)); - morpho.enableLltv(LLTV); + morpho.enableLltv(lltv); } function testEnableTooHighLltv(uint256 lltvFuzz) public { @@ -88,7 +88,7 @@ contract OnlyOwnerIntegrationTest is BaseTest { function testEnableLltv(uint256 lltvFuzz) public { lltvFuzz = _boundValidLltv(lltvFuzz); - vm.assume(lltvFuzz != LLTV); + vm.assume(lltvFuzz != lltv); vm.prank(OWNER); vm.expectEmit(true, true, true, true, address(morpho)); diff --git a/test/forge/invariant/TwoMarketsInvariantTest.sol b/test/forge/invariant/TwoMarketsInvariantTest.sol index d0742273e..d37eac98c 100644 --- a/test/forge/invariant/TwoMarketsInvariantTest.sol +++ b/test/forge/invariant/TwoMarketsInvariantTest.sol @@ -20,12 +20,12 @@ contract TwoMarketsInvariantTest is InvariantTest { vm.label(address(irm2), "IRM2"); marketParams2 = - MarketParams(address(borrowableToken), address(collateralToken), address(oracle), address(irm2), LLTV + 1); + MarketParams(address(borrowableToken), address(collateralToken), address(oracle), address(irm2), lltv + 1); id2 = marketParams2.id(); vm.startPrank(OWNER); morpho.enableIrm(address(irm2)); - morpho.enableLltv(LLTV + 1); + morpho.enableLltv(lltv + 1); morpho.createMarket(marketParams2); vm.stopPrank(); diff --git a/test/forge/libraries/periphery/MorphoBalancesLibTest.sol b/test/forge/libraries/periphery/MorphoBalancesLibTest.sol index db813c80d..ea50c4064 100644 --- a/test/forge/libraries/periphery/MorphoBalancesLibTest.sol +++ b/test/forge/libraries/periphery/MorphoBalancesLibTest.sol @@ -121,13 +121,13 @@ contract MorphoBalancesLibTest is BaseTest { if (amountBorrowed > 0) { uint256 collateralPrice = IOracle(marketParams.oracle).price(); collateralToken.setBalance( - BORROWER, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice) + BORROWER, amountBorrowed.wDivUp(lltv).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice) ); vm.startPrank(BORROWER); morpho.supplyCollateral( marketParams, - amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), + amountBorrowed.wDivUp(lltv).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex"" ); diff --git a/test/forge/libraries/periphery/MorphoLibTest.sol b/test/forge/libraries/periphery/MorphoLibTest.sol index af9b2f362..c763352c3 100644 --- a/test/forge/libraries/periphery/MorphoLibTest.sol +++ b/test/forge/libraries/periphery/MorphoLibTest.sol @@ -32,11 +32,11 @@ contract MorphoLibTest is BaseTest { morpho.supply(marketParams, amountSupplied, 0, address(this), hex""); uint256 collateralPrice = IOracle(marketParams.oracle).price(); - collateralToken.setBalance(BORROWER, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice)); + collateralToken.setBalance(BORROWER, amountBorrowed.wDivUp(lltv).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice)); vm.startPrank(BORROWER); morpho.supplyCollateral( - marketParams, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex"" + marketParams, amountBorrowed.wDivUp(lltv).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex"" ); morpho.borrow(marketParams, amountBorrowed, 0, BORROWER, BORROWER); vm.stopPrank(); diff --git a/test/forge/libraries/periphery/MorphoStorageLibTest.sol b/test/forge/libraries/periphery/MorphoStorageLibTest.sol index 56cfbc07f..3856b70d6 100644 --- a/test/forge/libraries/periphery/MorphoStorageLibTest.sol +++ b/test/forge/libraries/periphery/MorphoStorageLibTest.sol @@ -29,11 +29,11 @@ contract MorphoStorageLibTest is BaseTest { morpho.supply(marketParams, amountSupplied, 0, address(this), hex""); uint256 collateralPrice = IOracle(marketParams.oracle).price(); - collateralToken.setBalance(BORROWER, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice)); + collateralToken.setBalance(BORROWER, amountBorrowed.wDivUp(lltv).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice)); vm.startPrank(BORROWER); morpho.supplyCollateral( - marketParams, amountBorrowed.wDivUp(LLTV).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex"" + marketParams, amountBorrowed.wDivUp(lltv).mulDivUp(ORACLE_PRICE_SCALE, collateralPrice), BORROWER, hex"" ); morpho.borrow(marketParams, amountBorrowed, 0, BORROWER, BORROWER); vm.stopPrank(); @@ -63,7 +63,7 @@ contract MorphoStorageLibTest is BaseTest { slots[5] = MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id); slots[6] = MorphoStorageLib.marketLastUpdateAndFeeSlot(id); slots[7] = MorphoStorageLib.isIrmEnabledSlot(address(irm)); - slots[8] = MorphoStorageLib.isLltvEnabledSlot(LLTV); + slots[8] = MorphoStorageLib.isLltvEnabledSlot(lltv); slots[9] = MorphoStorageLib.isAuthorizedSlot(authorizer, BORROWER); slots[10] = MorphoStorageLib.nonceSlot(authorizer); slots[11] = MorphoStorageLib.idToBorrowableTokenSlot(id); @@ -86,7 +86,7 @@ contract MorphoStorageLibTest is BaseTest { assertEq(uint128(uint256(values[6])), morpho.lastUpdate(id)); assertEq(uint256(values[6] >> 128), morpho.fee(id)); assertEq(abi.decode(abi.encode(values[7]), (bool)), morpho.isIrmEnabled(address(irm))); - assertEq(abi.decode(abi.encode(values[8]), (bool)), morpho.isLltvEnabled(LLTV)); + assertEq(abi.decode(abi.encode(values[8]), (bool)), morpho.isLltvEnabled(lltv)); assertEq(abi.decode(abi.encode(values[9]), (bool)), morpho.isAuthorized(authorizer, BORROWER)); assertEq(uint256(values[10]), morpho.nonce(authorizer));