diff --git a/test/prod/ProdTest.sol b/test/prod/ProdTest.sol new file mode 100644 index 000000000..712e681d0 --- /dev/null +++ b/test/prod/ProdTest.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.0; + +import {SafeTransferLib, ERC20} from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; + +import {PercentageMath} from "@morpho-dao/morpho-utils/math/PercentageMath.sol"; +import {Math} from "@morpho-dao/morpho-utils/math/Math.sol"; +import {WadRayMath} from "@morpho-dao/morpho-utils/math/WadRayMath.sol"; + +import {BaseConfig} from "config/BaseConfig.sol"; +import "@forge-std/console.sol"; +import "@forge-std/Test.sol"; + +contract ProdTest is Test, BaseConfig { + // Show block number for reproducibility. + function testShowBlockNumber() public view { + console.log("Testing at block", block.number); + } + + // Needed because AAVE packs the balance struct. + function deal( + address underlying, + address user, + uint256 amount + ) internal override { + if (underlying == aave) { + uint256 balance = ERC20(underlying).balanceOf(user); + + if (amount > balance) { + ERC20(underlying).transfer(user, amount - balance); + } else { + vm.prank(user); + ERC20(underlying).transfer(address(this), balance - amount); + } + + return; + } + + super.deal(underlying, user, amount); + } +} diff --git a/test/prod/aave-v2/TestDeltas.t.sol b/test/prod/aave-v2/TestDeltas.t.sol index a39911a13..637fdb464 100644 --- a/test/prod/aave-v2/TestDeltas.t.sol +++ b/test/prod/aave-v2/TestDeltas.t.sol @@ -27,7 +27,7 @@ contract TestDeltas is TestSetup { function testShouldClearP2P() public virtual { for (uint256 marketIndex; marketIndex < markets.length; ++marketIndex) { - // _revert(); // TODO: re-add as soon as https://github.com/foundry-rs/foundry/issues/3792 is resolved, to avoid sharing state changes with each market test + _revert(); DeltasTest memory test; test.market = markets[0]; @@ -72,13 +72,13 @@ contract TestDeltas is TestSetup { assertApproxEqAbs( test.p2pSupplyDelta.rayMul(test.indexes.poolSupplyIndex), p2pSupplyUnderlying, - 10, + 100, "p2p supply delta" ); assertApproxEqAbs( test.p2pBorrowDelta.rayMul(test.indexes.poolBorrowIndex), p2pBorrowUnderlying, - 10, + 100, "p2p borrow delta" ); assertEq(test.p2pSupplyAfter, test.p2pSupplyBefore, "p2p supply"); @@ -108,14 +108,14 @@ contract TestDeltas is TestSetup { assertApproxEqAbs( p2pSupplyUnderlying - supplyDeltaUnderlyingBefore, IAToken(test.market.poolToken).balanceOf(address(morpho)) - test.morphoSupplyBefore, - 10, + 100, "morpho pool supply" ); assertApproxEqAbs( p2pBorrowUnderlying - borrowDeltaUnderlyingBefore, IVariableDebtToken(test.market.debtToken).balanceOf(address(morpho)) - test.morphoBorrowBefore, - 10, + 100, "morpho pool borrow" ); } @@ -123,7 +123,7 @@ contract TestDeltas is TestSetup { function testShouldNotClearP2PWhenFullDelta() public virtual { for (uint256 marketIndex; marketIndex < markets.length; ++marketIndex) { - // _revert(); // TODO: re-add as soon as https://github.com/foundry-rs/foundry/issues/3792 is resolved, to avoid sharing state changes with each market test + _revert(); DeltasTest memory test; test.market = markets[0]; diff --git a/test/prod/aave-v2/TestLifecycle.t.sol b/test/prod/aave-v2/TestLifecycle.t.sol index 906640115..7fe6f8968 100644 --- a/test/prod/aave-v2/TestLifecycle.t.sol +++ b/test/prod/aave-v2/TestLifecycle.t.sol @@ -94,7 +94,7 @@ contract TestLifecycle is TestSetup { assertApproxEqAbs( supply.position.total, supply.amount, - 1, + 2, string.concat(supply.market.symbol, " total supply") ); if (supply.p2pDisabled) @@ -178,7 +178,7 @@ contract TestLifecycle is TestSetup { assertApproxEqAbs( borrow.position.total, borrow.amount, - 1, + 2, string.concat(borrow.market.symbol, " total borrow") ); if (borrow.p2pDisabled) @@ -332,7 +332,7 @@ contract TestLifecycle is TestSetup { oracle.getAssetPrice(supplyMarket.underlying), supplyMarket.decimals, supplyMarket.ltv - ).wadMul(1.001 ether); + ).wadMul(1.01 ether); MarketSideTest memory supply = _supply(supplyMarket, supplyAmount); _testSupply(supply); diff --git a/test/prod/aave-v2/TestUpgradeLens.t.sol b/test/prod/aave-v2/TestUpgradeLens.t.sol index 1822c6c6f..b7c807e94 100644 --- a/test/prod/aave-v2/TestUpgradeLens.t.sol +++ b/test/prod/aave-v2/TestUpgradeLens.t.sol @@ -75,7 +75,7 @@ contract TestUpgradeLens is TestSetup { oracle.getAssetPrice(supplyMarket.underlying), supplyMarket.decimals, supplyMarket.ltv - ).wadMul(1.001 ether); + ).wadMul(1.01 ether); (uint256 expectedSupplyRate, , , ) = lens.getNextUserSupplyRatePerYear( supplyMarket.poolToken, @@ -91,7 +91,7 @@ contract TestUpgradeLens is TestSetup { assertApproxEqAbs( lens.getCurrentUserSupplyRatePerYear(supplyMarket.poolToken, address(user)), expectedSupplyRate, - 1e15, + 1e16, string.concat(supplyMarket.symbol, " supply rate") ); @@ -108,7 +108,7 @@ contract TestUpgradeLens is TestSetup { assertApproxEqAbs( lens.getCurrentUserBorrowRatePerYear(borrowMarket.poolToken, address(user)), expectedBorrowRate, - 1e15, + 1e16, string.concat(borrowMarket.symbol, " borrow rate") ); } diff --git a/test/prod/aave-v2/setup/TestSetup.sol b/test/prod/aave-v2/setup/TestSetup.sol index 1df1c8263..73c27d19f 100644 --- a/test/prod/aave-v2/setup/TestSetup.sol +++ b/test/prod/aave-v2/setup/TestSetup.sol @@ -1,15 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.0; +import "../../ProdTest.sol"; + import "src/aave-v2/interfaces/aave/IVariableDebtToken.sol"; import "src/aave-v2/interfaces/aave/IAToken.sol"; import "src/aave-v2/interfaces/lido/ILido.sol"; import {ReserveConfiguration} from "src/aave-v2/libraries/aave/ReserveConfiguration.sol"; -import "@morpho-dao/morpho-utils/math/WadRayMath.sol"; -import "@morpho-dao/morpho-utils/math/PercentageMath.sol"; -import "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; -import "@morpho-dao/morpho-utils/math/Math.sol"; import {InterestRatesManager} from "src/aave-v2/InterestRatesManager.sol"; import {MatchingEngine} from "src/aave-v2/MatchingEngine.sol"; @@ -20,10 +18,8 @@ import "src/aave-v2/Morpho.sol"; import {User} from "../../../aave-v2/helpers/User.sol"; import "config/aave-v2/Config.sol"; -import "@forge-std/console.sol"; -import "@forge-std/Test.sol"; -contract TestSetup is Config, Test { +contract TestSetup is Config, ProdTest { using ReserveConfiguration for DataTypes.ReserveConfigurationMap; using WadRayMath for uint256; using PercentageMath for uint256; @@ -81,9 +77,11 @@ contract TestSetup is Config, Test { vm.label(address(user), "User"); - deal(aave, address(this), type(uint256).max); + // Only 104 bits are used for the balance in the AAVE balance slot + deal(aave, address(this), type(uint104).max); + // Only 255 bits are used for the balance in the USDC balance slot + deal(usdc, address(this), 2**255 - 1); deal(dai, address(this), type(uint256).max); - deal(usdc, address(this), type(uint256).max); deal(usdt, address(this), type(uint256).max); deal(wbtc, address(this), type(uint256).max); deal(wEth, address(this), type(uint256).max); diff --git a/test/prod/compound/TestDeltas.t.sol b/test/prod/compound/TestDeltas.t.sol index 0eaf438fc..91aad3fe7 100644 --- a/test/prod/compound/TestDeltas.t.sol +++ b/test/prod/compound/TestDeltas.t.sol @@ -27,7 +27,7 @@ contract TestDeltas is TestSetup { function testShouldClearP2P() public virtual { for (uint256 marketIndex; marketIndex < markets.length; ++marketIndex) { - // _revert(); // TODO: re-add as soon as https://github.com/foundry-rs/foundry/issues/3792 is resolved, to avoid sharing state changes with each market test + _revert(); DeltasTest memory test; test.market = markets[marketIndex]; @@ -76,13 +76,13 @@ contract TestDeltas is TestSetup { assertApproxEqAbs( test.p2pSupplyDelta.mul(test.indexes.poolSupplyIndex), p2pSupplyUnderlying, - 10**(test.market.decimals / 2 + 1), + 10**(test.market.decimals / 2 + 2), "p2p supply delta" ); assertApproxEqAbs( test.p2pBorrowDelta.mul(test.indexes.poolBorrowIndex), p2pBorrowUnderlying, - 10, + 100, "p2p borrow delta" ); assertEq(test.p2pSupplyAfter, test.p2pSupplyBefore, "p2p supply"); @@ -112,14 +112,14 @@ contract TestDeltas is TestSetup { p2pSupplyUnderlying - supplyDeltaUnderlyingBefore, ICToken(test.market.poolToken).balanceOfUnderlying(address(morpho)) - test.morphoSupplyBefore, - 10**(test.market.decimals / 2 + 1), + 10**(test.market.decimals / 2 + 2), "morpho pool supply" ); assertApproxEqAbs( p2pBorrowUnderlying - borrowDeltaUnderlyingBefore, ICToken(test.market.poolToken).borrowBalanceCurrent(address(morpho)) - test.morphoBorrowBefore, - 10, + 100, "morpho pool borrow" ); } @@ -127,7 +127,7 @@ contract TestDeltas is TestSetup { function testShouldNotClearP2PWhenFullDelta() public virtual { for (uint256 marketIndex; marketIndex < markets.length; ++marketIndex) { - // _revert(); // TODO: re-add as soon as https://github.com/foundry-rs/foundry/issues/3792 is resolved, to avoid sharing state changes with each market test + _revert(); DeltasTest memory test; test.market = markets[marketIndex]; diff --git a/test/prod/compound/TestLifecycle.t.sol b/test/prod/compound/TestLifecycle.t.sol index 669b4c116..fad39e72e 100644 --- a/test/prod/compound/TestLifecycle.t.sol +++ b/test/prod/compound/TestLifecycle.t.sol @@ -97,10 +97,9 @@ contract TestLifecycle is TestSetup { 0, string.concat(supply.market.symbol, " balance after supply") ); - assertApproxEqAbs( - supply.position.total, + assertGe( supply.amount, - 1, + supply.position.total, string.concat(supply.market.symbol, " total supply") ); if (supply.p2pDisabled) @@ -143,10 +142,9 @@ contract TestLifecycle is TestSetup { supply.morphoUnderlyingBalanceBefore, string.concat(supply.market.symbol, " morpho balance") ); - assertApproxEqAbs( + assertGe( ICToken(supply.market.poolToken).balanceOfUnderlying(address(morpho)), supply.morphoPoolSupplyBefore + supply.position.pool, - 10, string.concat(supply.market.symbol, " morpho pool supply") ); assertApproxEqAbs( @@ -401,7 +399,7 @@ contract TestLifecycle is TestSetup { borrowedPrice, oracle.getUnderlyingPrice(supplyMarket.poolToken), supplyMarket.collateralFactor - ).mul(1.001 ether); + ).mul(1.01 ether); MarketSideTest memory supply = _supply(supplyMarket, supplyAmount); _testSupply(supply); diff --git a/test/prod/compound/TestUpgradeLens.t.sol b/test/prod/compound/TestUpgradeLens.t.sol index 563a35a7f..119a7e93a 100644 --- a/test/prod/compound/TestUpgradeLens.t.sol +++ b/test/prod/compound/TestUpgradeLens.t.sol @@ -108,7 +108,7 @@ contract TestUpgradeLens is TestSetup { borrowedPrice, oracle.getUnderlyingPrice(supplyMarket.poolToken), supplyMarket.collateralFactor - ).mul(1.001 ether); + ).mul(1.01 ether); (uint256 expectedSupplyRate, , , ) = lens.getNextUserSupplyRatePerBlock( supplyMarket.poolToken, @@ -124,7 +124,7 @@ contract TestUpgradeLens is TestSetup { assertApproxEqAbs( lens.getCurrentUserSupplyRatePerBlock(supplyMarket.poolToken, address(user)), expectedSupplyRate, - 1, + 1e8, string.concat(supplyMarket.symbol, " supply rate") ); @@ -141,7 +141,7 @@ contract TestUpgradeLens is TestSetup { assertApproxEqAbs( lens.getCurrentUserBorrowRatePerBlock(borrowMarket.poolToken, address(user)), expectedBorrowRate, - 1, + 1e8, string.concat(borrowMarket.symbol, " borrow rate") ); } diff --git a/test/prod/compound/setup/TestSetup.sol b/test/prod/compound/setup/TestSetup.sol index 377c83891..d620ab1ce 100644 --- a/test/prod/compound/setup/TestSetup.sol +++ b/test/prod/compound/setup/TestSetup.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.0; +import "../../ProdTest.sol"; + import {CompoundMath} from "@morpho-dao/morpho-utils/math/CompoundMath.sol"; -import {PercentageMath} from "@morpho-dao/morpho-utils/math/PercentageMath.sol"; -import {SafeTransferLib, ERC20} from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; -import {Math} from "@morpho-dao/morpho-utils/math/Math.sol"; import {Types} from "src/compound/libraries/Types.sol"; import {PositionsManager} from "src/compound/PositionsManager.sol"; @@ -12,10 +11,8 @@ import {InterestRatesManager} from "src/compound/InterestRatesManager.sol"; import {User} from "../../../compound/helpers/User.sol"; import "config/compound/Config.sol"; -import "@forge-std/console.sol"; -import "@forge-std/Test.sol"; -contract TestSetup is Config, Test { +contract TestSetup is Config, ProdTest { using CompoundMath for uint256; using PercentageMath for uint256; using SafeTransferLib for ERC20; @@ -72,9 +69,11 @@ contract TestSetup is Config, Test { vm.label(address(user), "User"); - deal(aave, address(this), type(uint256).max); + // Only 104 bits are used for the balance in the AAVE balance slot + deal(aave, address(this), type(uint104).max); + // Only 255 bits are used for the balance in the USDC balance slot + deal(usdc, address(this), 2**255 - 1); deal(dai, address(this), type(uint256).max); - deal(usdc, address(this), type(uint256).max); deal(usdt, address(this), type(uint256).max); deal(wbtc, address(this), type(uint256).max); deal(wEth, address(this), type(uint256).max); @@ -192,6 +191,7 @@ contract TestSetup is Config, Test { uint96 _amount, uint256 _price ) internal view returns (uint256) { + ICToken poolToken = ICToken(_market.poolToken); return bound( _amount, @@ -200,9 +200,7 @@ contract TestSetup is Config, Test { Math.min( Math.min( (_market.maxBorrows - _market.totalBorrows) / 2, - _market.underlying == wEth - ? cEth.balance - : ERC20(_market.underlying).balanceOf(_market.poolToken) + poolToken.getCash() - poolToken.totalReserves() ), MAX_USD_AMOUNT.div(_price) ),