diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index 48a80a65f..708144f07 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -4,7 +4,7 @@ runs: using: composite steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 cache: yarn @@ -15,7 +15,7 @@ runs: version: nightly - name: Restore forge compilation cache - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: | cache diff --git a/.github/workflows/forge-fmt.yml b/.github/workflows/forge-fmt.yml index 24353f087..00a7922a6 100644 --- a/.github/workflows/forge-fmt.yml +++ b/.github/workflows/forge-fmt.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/forge-test.yml b/.github/workflows/forge-test.yml index c62c2468a..19564e85a 100644 --- a/.github/workflows/forge-test.yml +++ b/.github/workflows/forge-test.yml @@ -11,12 +11,12 @@ on: internal-fuzz-runs: description: The number of fuzz rounds to perform for each fuzzing unit test. required: false - default: 64 + default: 32 type: number integration-fuzz-runs: description: The number of fuzz rounds to perform for each fuzzing unit test. required: false - default: 64 + default: 32 type: number invariant-runs: description: The number of runs to perform for invariant tests. @@ -51,7 +51,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -68,7 +68,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -78,12 +78,12 @@ jobs: run: forge build - name: Save forge compilation cache - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 with: path: | cache out - key: forge-${{ github.ref_name }} + key: forge-${{ github.ref_name }}-${{ github.run_id }} storage-check: needs: build-no-ir @@ -92,14 +92,14 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - uses: ./.github/actions/install - name: Check Morpho storage layout - uses: Rubilmax/foundry-storage-check@v3.4 + uses: Rubilmax/foundry-storage-check@v3.7 with: contract: src/Morpho.sol:Morpho rpcUrl: wss://eth-mainnet.g.alchemy.com/v2/${{ secrets.ALCHEMY_KEY }} @@ -113,7 +113,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -133,7 +133,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -153,7 +153,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -180,7 +180,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -206,7 +206,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 6e4eaafc2..35c7d444e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -5,6 +5,7 @@ on: paths: - .github/actions/** - .github/workflows/** + - config/** - lib/** - src/** - test/** diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 44b09e087..f1f162bbf 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -13,7 +13,7 @@ jobs: uses: ./.github/workflows/forge-test.yml with: - internal-fuzz-runs: 128 - integration-fuzz-runs: 128 - invariant-depth: 1024 + internal-fuzz-runs: 64 + integration-fuzz-runs: 64 + invariant-depth: 512 secrets: inherit diff --git a/.gitmodules b/.gitmodules index 2170b705b..8c141fbe0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std - branch = v1.5.2 + branch = 4f57c59f066a03d13de8c65bb34fca8247f5fcb2 [submodule "lib/solmate"] path = lib/solmate url = https://github.com/Rari-Capital/solmate diff --git a/Makefile b/Makefile index d3ef15fa4..fa1885edf 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ install: forge install contracts: - FOUNDRY_TEST=/dev/null FOUNDRY_SCRIPT=/dev/null forge build --via-ir --extra-output-files irOptimized --sizes --force + FOUNDRY_PROFILE=build FOUNDRY_TEST=/dev/null FOUNDRY_SCRIPT=/dev/null forge build --via-ir --extra-output-files irOptimized --sizes --force test-invariant: @@ -77,7 +77,7 @@ lcov-html: genhtml lcov.info -o coverage gas-report: - forge test --match-contract TestIntegration --gas-report + @FORGE_GAS_REPORT=true make test-integration deploy-emode: FOUNDRY_TEST=/dev/null forge script script/EthEModeDeployScript.sol:EthEModeDeploy --via-ir --broadcast --slow -vvvvv --rpc-url mainnet --ledger diff --git a/config/ConfigLib.sol b/config/ConfigLib.sol index 7374f5fc4..3b3f2712a 100644 --- a/config/ConfigLib.sol +++ b/config/ConfigLib.sol @@ -20,12 +20,13 @@ library ConfigLib { string internal constant MORPHO_DAO_PATH = "$.morphoDao"; string internal constant MORPHO_ETH_PATH = "$.morphoEth"; - function getAddress(Config storage config, string memory key) internal returns (address) { + function getAddress(Config storage config, string memory key) internal view returns (address) { return config.json.readAddress(string.concat("$.", key)); } function getAddressArray(Config storage config, string[] memory keys) internal + view returns (address[] memory addresses) { addresses = new address[](keys.length); @@ -35,35 +36,35 @@ library ConfigLib { } } - function getChainId(Config storage config) internal returns (uint256) { + function getChainId(Config storage config) internal view returns (uint256) { return config.json.readUint(CHAIN_ID_PATH); } - function getRpcAlias(Config storage config) internal returns (string memory) { + function getRpcAlias(Config storage config) internal view returns (string memory) { return config.json.readString(RPC_ALIAS_PATH); } - function getForkBlockNumber(Config storage config) internal returns (uint256) { + function getForkBlockNumber(Config storage config) internal view returns (uint256) { return config.json.readUint(FORK_BLOCK_NUMBER_PATH); } - function getAddressesProvider(Config storage config) internal returns (address) { + function getAddressesProvider(Config storage config) internal view returns (address) { return config.json.readAddress(ADDRESSES_PROVIDER_PATH); } - function getMorphoDao(Config storage config) internal returns (address) { + function getMorphoDao(Config storage config) internal view returns (address) { return config.json.readAddress(MORPHO_DAO_PATH); } - function getMorphoEth(Config storage config) internal returns (address) { + function getMorphoEth(Config storage config) internal view returns (address) { return config.json.readAddress(MORPHO_ETH_PATH); } - function getWrappedNative(Config storage config) internal returns (address) { + function getWrappedNative(Config storage config) internal view returns (address) { return getAddress(config, config.json.readString(WRAPPED_NATIVE_PATH)); } - function getLsdNatives(Config storage config) internal returns (address[] memory) { + function getLsdNatives(Config storage config) internal view returns (address[] memory) { return getAddressArray(config, config.json.readStringArray(LSD_NATIVES_PATH)); } } diff --git a/config/ethereum-mainnet.json b/config/ethereum-mainnet.json index e2c41a034..a49833f55 100644 --- a/config/ethereum-mainnet.json +++ b/config/ethereum-mainnet.json @@ -1,24 +1,20 @@ { - "chainId": 1, - "rpcAlias": "mainnet", - "forkBlockNumber": 16841312, - "addressesProvider": "0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e", - "morphoDao": "0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa", - "morphoEth": "0x33333aea097c193e66081E930c33020272b33333", - "DAI": "0x6B175474E89094C44Da98b954EedeAC495271d0F", - "USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - "USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7", - "AAVE": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", - "LINK": "0x514910771AF9Ca656af840dff83E8264EcF986CA", - "WBTC": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", - "WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "wstETH": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", - "cbETH": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", - "rETH": "0xae78736Cd615f374D3085123A210448E74Fc6393", - "wrappedNative": "WETH", - "lsdNatives": [ - "wstETH", - "cbETH", - "rETH" - ] + "chainId": 1, + "rpcAlias": "mainnet", + "forkBlockNumber": 0, + "addressesProvider": "0x2f39d218133AFaB8F2B819B1066c7E434Ad94E9e", + "morphoDao": "0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa", + "morphoEth": "0x33333aea097c193e66081E930c33020272b33333", + "DAI": "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "USDT": "0xdAC17F958D2ee523a2206206994597C13D831ec7", + "AAVE": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "LINK": "0x514910771AF9Ca656af840dff83E8264EcF986CA", + "WBTC": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + "WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "wstETH": "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "cbETH": "0xBe9895146f7AF43049ca1c1AE358B0541Ea49704", + "rETH": "0xae78736Cd615f374D3085123A210448E74Fc6393", + "wrappedNative": "WETH", + "lsdNatives": ["wstETH", "cbETH", "rETH"] } diff --git a/foundry.toml b/foundry.toml index 8045f1917..4599b7374 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,6 +3,11 @@ names = true sizes = true libs = ["node_modules", "lib"] fs_permissions = [{ access = "read", path = "./config/"}] +gas_limit = "18446744073709551615" # 2^64 - 1, to remove the gas limit +evm_version = "shanghai" + +[profile.build] +evm_version = "paris" [fuzz] runs = 32 diff --git a/script/EthEModeDeployScript.sol b/script/EthEModeDeployScript.sol index 18e9cc132..7e98b5941 100644 --- a/script/EthEModeDeployScript.sol +++ b/script/EthEModeDeployScript.sol @@ -162,7 +162,7 @@ contract EthEModeDeploy is Script, Test, Configured { morpho.setIsBorrowPaused(wbtc, true); } - function _checkAssertions() internal { + function _checkAssertions() internal view { assertEq(pool.getUserEMode(address(morpho)), E_MODE_CATEGORY_ID); assertFalse(morpho.market(weth).pauseStatuses.isSupplyPaused); diff --git a/script/PauseFlagsScript.sol b/script/PauseFlagsScript.sol index fbfb008a1..2056379a4 100644 --- a/script/PauseFlagsScript.sol +++ b/script/PauseFlagsScript.sol @@ -59,7 +59,7 @@ contract PauseFlags is Script, Test, Configured { MORPHO.setIsP2PDisabled(wbtc, true); } - function _checkAssertions() internal { + function _checkAssertions() internal view { assertTrue(MORPHO.market(wstEth).pauseStatuses.isRepayPaused); assertTrue(MORPHO.market(dai).pauseStatuses.isRepayPaused); assertTrue(MORPHO.market(usdc).pauseStatuses.isRepayPaused); diff --git a/script/TransferOwnershipScript.sol b/script/TransferOwnershipScript.sol index 35393b894..c66e1a97e 100644 --- a/script/TransferOwnershipScript.sol +++ b/script/TransferOwnershipScript.sol @@ -43,7 +43,7 @@ contract TransferOwnership is Script, Test, Configured { ProxyAdmin(CURRENT_PROXY_ADMIN).changeProxyAdmin(TransparentUpgradeableProxy(payable(MORPHO)), DAO_PROXY_ADMIN); } - function _checkAssertions() internal { + function _checkAssertions() internal view { assertEq(Ownable2StepUpgradeable(MORPHO).owner(), MORPHO_DAO); assertEq( ProxyAdmin(DAO_PROXY_ADMIN).getProxyAdmin(TransparentUpgradeableProxy(payable(MORPHO))), DAO_PROXY_ADMIN diff --git a/test/extensions/TestIntegrationBulkerGateway.sol b/test/extensions/TestIntegrationBulkerGateway.sol index d58f7db8d..cbf99547a 100644 --- a/test/extensions/TestIntegrationBulkerGateway.sol +++ b/test/extensions/TestIntegrationBulkerGateway.sol @@ -49,14 +49,14 @@ contract TestIntegrationBulkerGateway is IntegrationTest { new BulkerGateway(address(0)); } - function testGetters() public { + function testGetters() public view { assertEq(bulker.MORPHO(), address(morpho)); assertEq(bulker.WETH(), 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); assertEq(bulker.stETH(), 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84); assertEq(bulker.wstETH(), 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0); } - function testShouldApproveAfterDeploy() public { + function testShouldApproveAfterDeploy() public view { assertEq(ERC20(bulker.WETH()).allowance(address(bulker), address(morpho)), type(uint256).max); assertEq(ERC20(bulker.stETH()).allowance(address(bulker), bulker.wstETH()), type(uint256).max); assertEq(ERC20(bulker.wstETH()).allowance(address(bulker), address(morpho)), type(uint256).max); @@ -64,7 +64,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { function testBulkerShouldApprove(uint256 seed, uint160 amount, uint256 privateKey) public { privateKey = bound(privateKey, 1, type(uint160).max); - amount = uint160(bound(amount, 1, type(uint160).max)); + amount = uint160(bound(amount, 1, type(uint104).max)); TestMarket memory market = testMarkets[_randomUnderlying(seed)]; address delegator = vm.addr(privateKey); @@ -82,7 +82,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { function testBulkerShouldTransferFrom(uint256 seed, uint256 amount, uint256 privateKey) public { privateKey = bound(privateKey, 1, type(uint160).max); - amount = bound(amount, 1, type(uint160).max); + amount = bound(amount, 1, type(uint104).max); TestMarket memory market = testMarkets[_randomUnderlying(seed)]; address delegator = vm.addr(privateKey); deal(market.underlying, delegator, amount); @@ -102,7 +102,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { } function testBulkerShouldWrapETH(uint256 amount) public { - amount = bound(amount, 1, type(uint160).max); + amount = bound(amount, 1, type(uint104).max); deal(address(user), amount); IBulkerGateway.ActionType[] memory actions = new IBulkerGateway.ActionType[](1); @@ -215,6 +215,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { TestMarket storage market = testMarkets[_randomUnderlying(seed)]; maxIterations = bound(maxIterations, 1, 10); amount = _boundSupply(market, amount); + amount = bound(amount, 1, type(uint104).max); deal(market.underlying, address(bulker), amount); @@ -238,6 +239,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { TestMarket storage market = testMarkets[_randomCollateral(seed)]; maxIterations = bound(maxIterations, 1, 10); amount = _boundSupply(market, amount); + amount = bound(amount, 1, type(uint104).max); deal(market.underlying, address(bulker), amount); @@ -314,6 +316,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { TestMarket storage market = testMarkets[_randomUnderlying(seed)]; maxIterations = bound(maxIterations, 1, 10); amount = _boundSupply(market, amount); + amount = bound(amount, 1, type(uint104).max); deal(market.underlying, address(user), amount); @@ -343,6 +346,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { TestMarket storage market = testMarkets[_randomCollateral(seed)]; amount = _boundSupply(market, amount); + amount = bound(amount, 1, type(uint104).max); deal(market.underlying, address(user), amount); vm.startPrank(address(user)); @@ -367,6 +371,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { } function testBulkerShouldSkim(uint256 seed, uint256 amount, address receiver) public { + amount = bound(amount, 1, type(uint104).max); _assumeTarget(receiver); TestMarket storage market = testMarkets[_randomUnderlying(seed)]; @@ -387,6 +392,7 @@ contract TestIntegrationBulkerGateway is IntegrationTest { } function testBulkerSkimShouldRevertIfReceiverIsBulker(uint256 seed, uint256 amount) public { + amount = bound(amount, 1, type(uint104).max); address receiver = address(bulker); TestMarket storage market = testMarkets[_randomUnderlying(seed)]; diff --git a/test/extensions/TestIntegrationSupplyVault.sol b/test/extensions/TestIntegrationSupplyVault.sol index aba893405..7b6c36fad 100644 --- a/test/extensions/TestIntegrationSupplyVault.sol +++ b/test/extensions/TestIntegrationSupplyVault.sol @@ -6,7 +6,7 @@ import "./TestSetupVaults.sol"; contract TestIntegrationSupplyVault is TestSetupVaults { using WadRayMath for uint256; - function testCorrectInitialisationDai() public { + function testCorrectInitialisationDai() public view { assertEq(daiSupplyVault.owner(), address(this)); assertEq(daiSupplyVault.name(), "MorphoAaveDAI"); assertEq(daiSupplyVault.symbol(), "maDAI"); @@ -14,7 +14,7 @@ contract TestIntegrationSupplyVault is TestSetupVaults { assertEq(daiSupplyVault.decimals(), 18); } - function testCorrectInitialisationWrappedNative() public { + function testCorrectInitialisationWrappedNative() public view { assertEq(wNativeSupplyVault.owner(), address(this)); assertEq(wNativeSupplyVault.name(), "MorphoAaveWNATIVE"); assertEq(wNativeSupplyVault.symbol(), "maWNATIVE"); @@ -215,7 +215,7 @@ contract TestIntegrationSupplyVault is TestSetupVaults { uint256 shares = promoter1.depositVault(daiSupplyVault, amount); uint256 assets = promoter1.redeemVault(daiSupplyVault, shares); - assertApproxEqAbs(assets, amount, 1, "unexpected withdrawn assets"); + assertApproxEqAbs(assets, amount, 2, "unexpected withdrawn assets"); } function testShouldRedeemAllAmountWhenMorphoPoolIndexesOutdated(uint256 amount, uint256 timePassed) public { diff --git a/test/helpers/BaseTest.sol b/test/helpers/BaseTest.sol index 50a121d47..a0c50a814 100644 --- a/test/helpers/BaseTest.sol +++ b/test/helpers/BaseTest.sol @@ -26,18 +26,18 @@ contract BaseTest is Test { uint256 private constant MAX_AMOUNT = 1e20 ether; /// @dev Asserts a is approximately equal to b, with a maximum absolute difference of DUST_THRESHOLD. - function assertApproxEqDust(uint256 a, uint256 b, string memory err) internal { + function assertApproxEqDust(uint256 a, uint256 b, string memory err) internal pure { assertApproxEqAbs(a, b, Constants.DUST_THRESHOLD, err); } /// @dev Asserts a is approximately less than or equal to b, with a maximum absolute difference of maxDelta. - function assertApproxLeAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err) internal { + function assertApproxLeAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err) internal pure { assertLe(a, b, err); assertApproxEqAbs(a, b, maxDelta, err); } /// @dev Asserts a is approximately greater than or equal to b, with a maximum absolute difference of maxDelta. - function assertApproxGeAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err) internal { + function assertApproxGeAbs(uint256 a, uint256 b, uint256 maxDelta, string memory err) internal pure { assertGe(a, b, err); assertApproxEqAbs(a, b, maxDelta, err); } @@ -49,12 +49,12 @@ contract BaseTest is Test { } /// @dev Bounds the fuzzing input to a realistic number of blocks. - function _boundBlocks(uint256 blocks) internal view returns (uint256) { - return bound(blocks, 1, type(uint24).max); + function _boundBlocks(uint256 blocks) internal pure returns (uint256) { + return bound(blocks, 1, 365 days / BLOCK_TIME); } /// @dev Bounds the fuzzing input to a realistic index. - function _boundIndex(uint256 index) internal view returns (uint256) { + function _boundIndex(uint256 index) internal pure returns (uint256) { return bound(index, WadRayMath.RAY, 20 * WadRayMath.RAY); } diff --git a/test/helpers/ForkTest.sol b/test/helpers/ForkTest.sol index 92c1049e9..4ad5c6a8a 100644 --- a/test/helpers/ForkTest.sol +++ b/test/helpers/ForkTest.sol @@ -5,7 +5,7 @@ import {IAToken} from "src/interfaces/aave/IAToken.sol"; import {IAaveOracle} from "@aave-v3-core/interfaces/IAaveOracle.sol"; import {IPriceOracleGetter} from "@aave-v3-core/interfaces/IPriceOracleGetter.sol"; import {IACLManager} from "@aave-v3-core/interfaces/IACLManager.sol"; -import {IPoolConfigurator} from "@aave-v3-core/interfaces/IPoolConfigurator.sol"; +import {IPoolConfigurator} from "test/helpers/IPoolConfigurator.sol"; import {IPoolDataProvider} from "@aave-v3-core/interfaces/IPoolDataProvider.sol"; import {IPool, IPoolAddressesProvider} from "@aave-v3-core/interfaces/IPool.sol"; import {IStableDebtToken} from "@aave-v3-core/interfaces/IStableDebtToken.sol"; @@ -39,15 +39,6 @@ contract ForkTest is BaseTest, Configured { using ReserveDataTestLib for DataTypes.ReserveData; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - /* STRUCTS */ - - struct StableDebtSupplyData { - uint256 currPrincipalStableDebt; - uint256 currTotalStableDebt; - uint256 currAvgStableBorrowRate; - uint40 stableDebtLastUpdateTimestamp; - } - /* CONSTANTS */ address internal constant POOL_ADMIN = address(0xB055); @@ -99,6 +90,7 @@ contract ForkTest is BaseTest, Configured { forkId = forkBlockNumber == 0 ? vm.createSelectFork(rpcUrl) : vm.createSelectFork(rpcUrl, forkBlockNumber); vm.chainId(config.getChainId()); + console.log("fork block number", block.number); } function _loadConfig() internal virtual override { @@ -121,6 +113,7 @@ contract ForkTest is BaseTest, Configured { function _label() internal virtual { vm.label(address(pool), "Pool"); vm.label(address(oracle), "PriceOracle"); + vm.label(address(rewardsController), "RewardsController"); vm.label(address(addressesProvider), "AddressesProvider"); vm.label(aclAdmin, "ACLAdmin"); @@ -180,30 +173,43 @@ contract ForkTest is BaseTest, Configured { } /// @dev Avoids to revert because of AAVE token snapshots: https://github.com/aave/aave-token-v2/blob/master/contracts/token/base/GovernancePowerDelegationERC20.sol#L174 - function _deal(address underlying, address user, uint256 amount) internal { + function deal(address underlying, address user, uint256 amount) internal override { if (amount == 0) return; - if (underlying == weth) deal(weth, weth.balance + amount); // Refill wrapped Ether. - + // Needed because AAVE packs the balance struct. if (underlying == aave) { - uint256 balance = ERC20(underlying).balanceOf(user); - - if (amount > balance) ERC20(underlying).safeTransfer(user, amount - balance); - if (amount < balance) { - vm.prank(user); - ERC20(underlying).safeTransfer(address(this), balance - amount); + uint256 initialBalance = ERC20(aave).balanceOf(user); + require(amount <= type(uint104).max, "deal: amount exceeds AAVE balance limit"); + // The slot of the balance struct "_balances" is 0. + bytes32 balanceSlot = keccak256(abi.encode(user, uint256(0))); + bytes32 initialValue = vm.load(aave, balanceSlot); + // The balance is stored in the first 104 bits. + bytes32 finalValue = ((initialValue >> 104) << 104) | bytes32(uint256(amount)); + vm.store(aave, balanceSlot, finalValue); + require(ERC20(aave).balanceOf(user) == uint256(amount), "deal: AAVE balance mismatch"); + + uint256 totSup = ERC20(aave).totalSupply(); + if (amount < initialBalance) { + totSup -= (initialBalance - amount); + } else { + totSup += (amount - initialBalance); } + // The slot of the balance struct "totalSupply" is 2. + bytes32 totalSupplySlot = bytes32(uint256(2)); + vm.store(aave, totalSupplySlot, bytes32(totSup)); return; } - deal(underlying, user, amount); + if (underlying == weth) super.deal(weth, weth.balance + amount); // Refill wrapped Ether. + + super.deal(underlying, user, amount); } /// @dev Reverts the fork to its initial fork state. function _revert() internal { - if (snapshotId < type(uint256).max) vm.revertTo(snapshotId); - snapshotId = vm.snapshot(); + if (snapshotId < type(uint256).max) vm.revertToState(snapshotId); + snapshotId = vm.snapshotState(); } /// @dev Returns the total supply used towards the supply cap. @@ -244,14 +250,10 @@ contract ForkTest is BaseTest, Configured { uint8 eModeCategoryId ) internal { poolAdmin.setEModeCategory( - eModeCategoryId, - eModeCategory.ltv, - eModeCategory.liquidationThreshold, - eModeCategory.liquidationBonus, - address(1), - "" + eModeCategoryId, eModeCategory.ltv, eModeCategory.liquidationThreshold, eModeCategory.liquidationBonus, "" ); - poolAdmin.setAssetEModeCategory(underlying, eModeCategoryId); + poolAdmin.setAssetBorrowableInEMode(underlying, eModeCategoryId, true); + poolAdmin.setAssetCollateralInEMode(underlying, eModeCategoryId, true); } function _assumeNotUnderlying(address input) internal view { diff --git a/test/helpers/IPoolConfigurator.sol b/test/helpers/IPoolConfigurator.sol new file mode 100644 index 000000000..3d8dd7580 --- /dev/null +++ b/test/helpers/IPoolConfigurator.sol @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +/** + * @title IPoolConfigurator + * @author Aave + * @notice Defines the basic interface for a Pool configurator. + */ +interface IPoolConfigurator { + /** + * @dev Emitted when a reserve is initialized. + * @param asset The address of the underlying asset of the reserve + * @param aToken The address of the associated aToken contract + * @param stableDebtToken The address of the associated stable rate debt token + * @param variableDebtToken The address of the associated variable rate debt token + * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve + */ + event ReserveInitialized( + address indexed asset, + address indexed aToken, + address stableDebtToken, + address variableDebtToken, + address interestRateStrategyAddress + ); + + /** + * @dev Emitted when borrowing is enabled or disabled on a reserve. + * @param asset The address of the underlying asset of the reserve + * @param enabled True if borrowing is enabled, false otherwise + */ + event ReserveBorrowing(address indexed asset, bool enabled); + + /** + * @dev Emitted when flashloans are enabled or disabled on a reserve. + * @param asset The address of the underlying asset of the reserve + * @param enabled True if flashloans are enabled, false otherwise + */ + event ReserveFlashLoaning(address indexed asset, bool enabled); + + /** + * @dev Emitted when the collateralization risk parameters for the specified asset are updated. + * @param asset The address of the underlying asset of the reserve + * @param ltv The loan to value of the asset when used as collateral + * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized + * @param liquidationBonus The bonus liquidators receive to liquidate this asset + */ + event CollateralConfigurationChanged( + address indexed asset, uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus + ); + + /** + * @dev Emitted when stable rate borrowing is enabled or disabled on a reserve + * @param asset The address of the underlying asset of the reserve + * @param enabled True if stable rate borrowing is enabled, false otherwise + */ + event ReserveStableRateBorrowing(address indexed asset, bool enabled); + + /** + * @dev Emitted when a reserve is activated or deactivated + * @param asset The address of the underlying asset of the reserve + * @param active True if reserve is active, false otherwise + */ + event ReserveActive(address indexed asset, bool active); + + /** + * @dev Emitted when a reserve is frozen or unfrozen + * @param asset The address of the underlying asset of the reserve + * @param frozen True if reserve is frozen, false otherwise + */ + event ReserveFrozen(address indexed asset, bool frozen); + + /** + * @dev Emitted when a reserve is paused or unpaused + * @param asset The address of the underlying asset of the reserve + * @param paused True if reserve is paused, false otherwise + */ + event ReservePaused(address indexed asset, bool paused); + + /** + * @dev Emitted when a reserve is dropped. + * @param asset The address of the underlying asset of the reserve + */ + event ReserveDropped(address indexed asset); + + /** + * @dev Emitted when a reserve factor is updated. + * @param asset The address of the underlying asset of the reserve + * @param oldReserveFactor The old reserve factor, expressed in bps + * @param newReserveFactor The new reserve factor, expressed in bps + */ + event ReserveFactorChanged(address indexed asset, uint256 oldReserveFactor, uint256 newReserveFactor); + + /** + * @dev Emitted when the borrow cap of a reserve is updated. + * @param asset The address of the underlying asset of the reserve + * @param oldBorrowCap The old borrow cap + * @param newBorrowCap The new borrow cap + */ + event BorrowCapChanged(address indexed asset, uint256 oldBorrowCap, uint256 newBorrowCap); + + /** + * @dev Emitted when the supply cap of a reserve is updated. + * @param asset The address of the underlying asset of the reserve + * @param oldSupplyCap The old supply cap + * @param newSupplyCap The new supply cap + */ + event SupplyCapChanged(address indexed asset, uint256 oldSupplyCap, uint256 newSupplyCap); + + /** + * @dev Emitted when the liquidation protocol fee of a reserve is updated. + * @param asset The address of the underlying asset of the reserve + * @param oldFee The old liquidation protocol fee, expressed in bps + * @param newFee The new liquidation protocol fee, expressed in bps + */ + event LiquidationProtocolFeeChanged(address indexed asset, uint256 oldFee, uint256 newFee); + + /** + * @dev Emitted when the unbacked mint cap of a reserve is updated. + * @param asset The address of the underlying asset of the reserve + * @param oldUnbackedMintCap The old unbacked mint cap + * @param newUnbackedMintCap The new unbacked mint cap + */ + event UnbackedMintCapChanged(address indexed asset, uint256 oldUnbackedMintCap, uint256 newUnbackedMintCap); + + /* @dev Emitted when an collateral configuration of an asset in an eMode is changed. + * @param asset The address of the underlying asset of the reserve + * @param categoryId The eMode category + * @param collateral True if the asset is enabled as collateral in the eMode, false otherwise. + */ + event AssetCollateralInEModeChanged(address indexed asset, uint8 categoryId, bool collateral); + + /** + * @dev Emitted when the borrowable configuration of an asset in an eMode changed. + * @param asset The address of the underlying asset of the reserve + * @param categoryId The eMode category + * @param borrowable True if the asset is enabled as borrowable in the eMode, false otherwise. + */ + event AssetBorrowableInEModeChanged(address indexed asset, uint8 categoryId, bool borrowable); + + /** + * @dev Emitted when a new eMode category is added. + * @param categoryId The new eMode category id + * @param ltv The ltv for the asset category in eMode + * @param liquidationThreshold The liquidationThreshold for the asset category in eMode + * @param liquidationBonus The liquidationBonus for the asset category in eMode + * @param label A human readable identifier for the category + */ + event EModeCategoryAdded( + uint8 indexed categoryId, uint256 ltv, uint256 liquidationThreshold, uint256 liquidationBonus, string label + ); + + /** + * @dev Emitted when a reserve interest strategy contract is updated. + * @param asset The address of the underlying asset of the reserve + * @param oldStrategy The address of the old interest strategy contract + * @param newStrategy The address of the new interest strategy contract + */ + event ReserveInterestRateStrategyChanged(address indexed asset, address oldStrategy, address newStrategy); + + /** + * @dev Emitted when an aToken implementation is upgraded. + * @param asset The address of the underlying asset of the reserve + * @param proxy The aToken proxy address + * @param implementation The new aToken implementation + */ + event ATokenUpgraded(address indexed asset, address indexed proxy, address indexed implementation); + + /** + * @dev Emitted when the implementation of a variable debt token is upgraded. + * @param asset The address of the underlying asset of the reserve + * @param proxy The variable debt token proxy address + * @param implementation The new aToken implementation + */ + event VariableDebtTokenUpgraded(address indexed asset, address indexed proxy, address indexed implementation); + + /** + * @dev Emitted when the debt ceiling of an asset is set. + * @param asset The address of the underlying asset of the reserve + * @param oldDebtCeiling The old debt ceiling + * @param newDebtCeiling The new debt ceiling + */ + event DebtCeilingChanged(address indexed asset, uint256 oldDebtCeiling, uint256 newDebtCeiling); + + /** + * @dev Emitted when the the siloed borrowing state for an asset is changed. + * @param asset The address of the underlying asset of the reserve + * @param oldState The old siloed borrowing state + * @param newState The new siloed borrowing state + */ + event SiloedBorrowingChanged(address indexed asset, bool oldState, bool newState); + + /** + * @dev Emitted when the bridge protocol fee is updated. + * @param oldBridgeProtocolFee The old protocol fee, expressed in bps + * @param newBridgeProtocolFee The new protocol fee, expressed in bps + */ + event BridgeProtocolFeeUpdated(uint256 oldBridgeProtocolFee, uint256 newBridgeProtocolFee); + + /** + * @dev Emitted when the total premium on flashloans is updated. + * @param oldFlashloanPremiumTotal The old premium, expressed in bps + * @param newFlashloanPremiumTotal The new premium, expressed in bps + */ + event FlashloanPremiumTotalUpdated(uint128 oldFlashloanPremiumTotal, uint128 newFlashloanPremiumTotal); + + /** + * @dev Emitted when the part of the premium that goes to protocol is updated. + * @param oldFlashloanPremiumToProtocol The old premium, expressed in bps + * @param newFlashloanPremiumToProtocol The new premium, expressed in bps + */ + event FlashloanPremiumToProtocolUpdated( + uint128 oldFlashloanPremiumToProtocol, uint128 newFlashloanPremiumToProtocol + ); + + /** + * @dev Emitted when the reserve is set as borrowable/non borrowable in isolation mode. + * @param asset The address of the underlying asset of the reserve + * @param borrowable True if the reserve is borrowable in isolation, false otherwise + */ + event BorrowableInIsolationChanged(address asset, bool borrowable); + + /** + * @notice Configures borrowing on a reserve. + * @dev Can only be disabled (set to false) if stable borrowing is disabled + * @param asset The address of the underlying asset of the reserve + * @param enabled True if borrowing needs to be enabled, false otherwise + */ + function setReserveBorrowing(address asset, bool enabled) external; + + /** + * @notice Configures the reserve collateralization parameters. + * @dev All the values are expressed in bps. A value of 10000, results in 100.00% + * @dev The `liquidationBonus` is always above 100%. A value of 105% means the liquidator will receive a 5% bonus + * @param asset The address of the underlying asset of the reserve + * @param ltv The loan to value of the asset when used as collateral + * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized + * @param liquidationBonus The bonus liquidators receive to liquidate this asset + */ + function configureReserveAsCollateral( + address asset, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus + ) external; + + /** + * @notice Enable or disable flashloans on a reserve + * @param asset The address of the underlying asset of the reserve + * @param enabled True if flashloans need to be enabled, false otherwise + */ + function setReserveFlashLoaning(address asset, bool enabled) external; + + /** + * @notice Activate or deactivate a reserve + * @param asset The address of the underlying asset of the reserve + * @param active True if the reserve needs to be active, false otherwise + */ + function setReserveActive(address asset, bool active) external; + + /** + * @notice Freeze or unfreeze a reserve. A frozen reserve doesn't allow any new supply, borrow + * or rate swap but allows repayments, liquidations, rate rebalances and withdrawals. + * @param asset The address of the underlying asset of the reserve + * @param freeze True if the reserve needs to be frozen, false otherwise + */ + function setReserveFreeze(address asset, bool freeze) external; + + /** + * @notice Sets the borrowable in isolation flag for the reserve. + * @dev When this flag is set to true, the asset will be borrowable against isolated collaterals and the + * borrowed amount will be accumulated in the isolated collateral's total debt exposure + * @dev Only assets of the same family (e.g. USD stablecoins) should be borrowable in isolation mode to keep + * consistency in the debt ceiling calculations + * @param asset The address of the underlying asset of the reserve + * @param borrowable True if the asset should be borrowable in isolation, false otherwise + */ + function setBorrowableInIsolation(address asset, bool borrowable) external; + + /** + * @notice Pauses a reserve. A paused reserve does not allow any interaction (supply, borrow, repay, + * swap interest rate, liquidate, atoken transfers). + * @param asset The address of the underlying asset of the reserve + * @param paused True if pausing the reserve, false if unpausing + */ + function setReservePause(address asset, bool paused) external; + + /** + * @notice Updates the reserve factor of a reserve. + * @param asset The address of the underlying asset of the reserve + * @param newReserveFactor The new reserve factor of the reserve + */ + function setReserveFactor(address asset, uint256 newReserveFactor) external; + + /** + * @notice Sets the interest rate strategy of a reserve. + * @param asset The address of the underlying asset of the reserve + * @param newRateStrategyAddress The address of the new interest strategy contract + */ + function setReserveInterestRateStrategyAddress(address asset, address newRateStrategyAddress) external; + + /** + * @notice Pauses or unpauses all the protocol reserves. In the paused state all the protocol interactions + * are suspended. + * @param paused True if protocol needs to be paused, false otherwise + */ + function setPoolPause(bool paused) external; + + /** + * @notice Updates the borrow cap of a reserve. + * @param asset The address of the underlying asset of the reserve + * @param newBorrowCap The new borrow cap of the reserve + */ + function setBorrowCap(address asset, uint256 newBorrowCap) external; + + /** + * @notice Updates the supply cap of a reserve. + * @param asset The address of the underlying asset of the reserve + * @param newSupplyCap The new supply cap of the reserve + */ + function setSupplyCap(address asset, uint256 newSupplyCap) external; + + /** + * @notice Updates the liquidation protocol fee of reserve. + * @param asset The address of the underlying asset of the reserve + * @param newFee The new liquidation protocol fee of the reserve, expressed in bps + */ + function setLiquidationProtocolFee(address asset, uint256 newFee) external; + + /** + * @notice Updates the unbacked mint cap of reserve. + * @param asset The address of the underlying asset of the reserve + * @param newUnbackedMintCap The new unbacked mint cap of the reserve + */ + function setUnbackedMintCap(address asset, uint256 newUnbackedMintCap) external; + + /** + * @notice Enables/disables an asset to be borrowable in a selected eMode. + * - eMode.borrowable always has less priority then reserve.borrowable + * @param asset The address of the underlying asset of the reserve + * @param categoryId The eMode categoryId + * @param borrowable True if the asset should be borrowable in the given eMode category, false otherwise. + */ + function setAssetBorrowableInEMode(address asset, uint8 categoryId, bool borrowable) external; + + /** + * @notice Enables/disables an asset to be collateral in a selected eMode. + * @param asset The address of the underlying asset of the reserve + * @param categoryId The eMode categoryId + * @param collateral True if the asset should be collateral in the given eMode category, false otherwise. + */ + function setAssetCollateralInEMode(address asset, uint8 categoryId, bool collateral) external; + + /** + * @notice Adds a new efficiency mode (eMode) category or alters a existing one. + * @param categoryId The id of the category to be configured + * @param ltv The ltv associated with the category + * @param liquidationThreshold The liquidation threshold associated with the category + * @param liquidationBonus The liquidation bonus associated with the category + * @param label A label identifying the category + */ + function setEModeCategory( + uint8 categoryId, + uint16 ltv, + uint16 liquidationThreshold, + uint16 liquidationBonus, + string calldata label + ) external; + + /** + * @notice Drops a reserve entirely. + * @param asset The address of the reserve to drop + */ + function dropReserve(address asset) external; + + /** + * @notice Updates the bridge fee collected by the protocol reserves. + * @param newBridgeProtocolFee The part of the fee sent to the protocol treasury, expressed in bps + */ + function updateBridgeProtocolFee(uint256 newBridgeProtocolFee) external; + + /** + * @notice Updates the total flash loan premium. + * Total flash loan premium consists of two parts: + * - A part is sent to aToken holders as extra balance + * - A part is collected by the protocol reserves + * @dev Expressed in bps + * @dev The premium is calculated on the total amount borrowed + * @param newFlashloanPremiumTotal The total flashloan premium + */ + function updateFlashloanPremiumTotal(uint128 newFlashloanPremiumTotal) external; + + /** + * @notice Updates the flash loan premium collected by protocol reserves + * @dev Expressed in bps + * @dev The premium to protocol is calculated on the total flashloan premium + * @param newFlashloanPremiumToProtocol The part of the flashloan premium sent to the protocol treasury + */ + function updateFlashloanPremiumToProtocol(uint128 newFlashloanPremiumToProtocol) external; + + /** + * @notice Sets the debt ceiling for an asset. + * @param newDebtCeiling The new debt ceiling + */ + function setDebtCeiling(address asset, uint256 newDebtCeiling) external; + + /** + * @notice Sets siloed borrowing for an asset + * @param siloed The new siloed borrowing state + */ + function setSiloedBorrowing(address asset, bool siloed) external; +} diff --git a/test/helpers/IntegrationTest.sol b/test/helpers/IntegrationTest.sol index 156fe64c7..351ab3842 100644 --- a/test/helpers/IntegrationTest.sol +++ b/test/helpers/IntegrationTest.sol @@ -234,7 +234,7 @@ contract IntegrationTest is ForkTest { internal bypassSupplyCap(underlying, amount) { - _deal(underlying, address(this), amount); + deal(underlying, address(this), amount); ERC20(underlying).safeApprove(address(pool), amount); pool.deposit(underlying, amount, onBehalf, 0); } @@ -292,7 +292,7 @@ contract IntegrationTest is ForkTest { } /// @dev Bounds the fuzzing input to an arbitrary reasonable amount of iterations. - function _boundMaxIterations(uint256 maxIterations) internal view returns (uint256) { + function _boundMaxIterations(uint256 maxIterations) internal pure returns (uint256) { return bound(maxIterations, 0, 32); } @@ -307,7 +307,7 @@ contract IntegrationTest is ForkTest { uint256 maxIterations ) internal returns (uint256 collateral, uint256 borrowed) { collateral = collateralMarket.minBorrowCollateral(borrowedMarket, amount, eModeCategoryId); - _deal(collateralMarket.underlying, borrower, collateral); + deal(collateralMarket.underlying, borrower, collateral); vm.startPrank(borrower); ERC20(collateralMarket.underlying).safeApprove(address(morpho), collateral); @@ -482,6 +482,10 @@ contract IntegrationTest is ForkTest { output = _boundAddressValid(input); vm.assume(output != address(this)); + vm.assume(output != address(morpho)); + // The following addresses are blacklisted on USDT. + vm.assume(output != address(0)); + vm.assume(output != address(1)); for (uint256 i; i < allUnderlyings.length; ++i) { TestMarket storage market = testMarkets[allUnderlyings[i]]; @@ -500,7 +504,10 @@ contract IntegrationTest is ForkTest { } } - function _assertMarketUpdatedIndexes(Types.Market memory market, Types.Indexes256 memory futureIndexes) internal { + function _assertMarketUpdatedIndexes(Types.Market memory market, Types.Indexes256 memory futureIndexes) + internal + view + { assertEq(market.lastUpdateTimestamp, block.timestamp, "lastUpdateTimestamp != block.timestamp"); assertEq( market.indexes.supply.poolIndex, futureIndexes.supply.poolIndex, "poolSupplyIndex != futurePoolSupplyIndex" @@ -516,7 +523,7 @@ contract IntegrationTest is ForkTest { ); } - function _assertMarketAccountingZero(Types.Market memory market) internal { + function _assertMarketAccountingZero(Types.Market memory market) internal pure { assertEq(market.deltas.supply.scaledDelta, 0, "scaledSupplyDelta != 0"); assertEq(market.deltas.supply.scaledP2PTotal, 0, "scaledTotalSupplyP2P != 0"); assertEq(market.deltas.borrow.scaledDelta, 0, "scaledBorrowDelta != 0"); diff --git a/test/helpers/TestMarketLib.sol b/test/helpers/TestMarketLib.sol index 88e755d7c..0d76b8595 100644 --- a/test/helpers/TestMarketLib.sol +++ b/test/helpers/TestMarketLib.sol @@ -45,7 +45,7 @@ library TestMarketLib { /// @dev Returns the quantity that can be borrowed/withdrawn from the market. function liquidity(TestMarket storage market) internal view returns (uint256) { - return ERC20(market.underlying).balanceOf(market.aToken); + return ERC20(market.underlying).balanceOf(market.aToken) * 99 / 100; // prevent an underflow in Aave's IRM } /// @dev Returns the quantity currently supplied on the market on AaveV3. @@ -53,9 +53,9 @@ library TestMarketLib { return ERC20(market.aToken).totalSupply(); } - /// @dev Returns the quantity currently borrowed (with variable & stable rates) on the market on AaveV3. + /// @dev Returns the quantity currently borrowed (with variable only, stable being deprecated) on the market on AaveV3. function totalBorrow(TestMarket storage market) internal view returns (uint256) { - return totalVariableBorrow(market) + totalStableBorrow(market); + return totalVariableBorrow(market); } /// @dev Returns the quantity currently borrowed with variable rate from the market on AaveV3. @@ -63,11 +63,6 @@ library TestMarketLib { return ERC20(market.variableDebtToken).totalSupply(); } - /// @dev Returns the quantity currently borrowed with stable rate from the market on AaveV3. - function totalStableBorrow(TestMarket storage market) internal view returns (uint256) { - return ERC20(market.stableDebtToken).totalSupply(); - } - /// @dev Returns the quantity currently supplied on behalf of the user, on the market on AaveV3. function supplyOf(TestMarket storage market, address user) internal view returns (uint256) { return ERC20(market.aToken).balanceOf(user); @@ -78,11 +73,6 @@ library TestMarketLib { return ERC20(market.variableDebtToken).balanceOf(user); } - /// @dev Returns the quantity currently borrowed on behalf of the user, with stable rate, on the market on AaveV3. - function stableBorrowOf(TestMarket storage market, address user) internal view returns (uint256) { - return ERC20(market.stableDebtToken).balanceOf(user); - } - /// @dev Calculates the underlying amount that can be supplied on the given market on AaveV3, reaching the borrow cap. function borrowGap(TestMarket storage market) internal view returns (uint256) { return market.borrowCap.zeroFloorSub(totalBorrow(market)); @@ -167,6 +157,6 @@ library TestMarketLib { ) internal view returns (uint256) { uint256 lt = getLt(collateralMarket, eModeCategoryId); - return rawCollateralValue(quote(collateralMarket, borrowedMarket, amount).percentDiv(lt)); + return rawCollateralValue(quote(collateralMarket, borrowedMarket, amount).percentDivUp(lt)); } } diff --git a/test/integration/TestIntegrationAssetAsCollateral.sol b/test/integration/TestIntegrationAssetAsCollateral.sol index bffe087a9..0eb278939 100644 --- a/test/integration/TestIntegrationAssetAsCollateral.sol +++ b/test/integration/TestIntegrationAssetAsCollateral.sol @@ -37,7 +37,7 @@ contract TestIntegrationAssetAsCollateral is IntegrationTest { morpho.setAssetIsCollateral(dai, isCollateral); } - function invariantAssetAsCollateral() public { + function invariantAssetAsCollateral() public view { if (morpho.market(dai).isCollateral) assertTrue(_isUsingAsCollateral(dai)); if (!_isUsingAsCollateral(dai)) assertFalse(morpho.market(dai).isCollateral); } diff --git a/test/integration/TestIntegrationBorrow.sol b/test/integration/TestIntegrationBorrow.sol index 367857ddd..3a41014df 100644 --- a/test/integration/TestIntegrationBorrow.sol +++ b/test/integration/TestIntegrationBorrow.sol @@ -25,7 +25,7 @@ contract TestIntegrationBorrow is IntegrationTest { address onBehalf, address receiver, BorrowTest memory test - ) internal returns (BorrowTest memory) { + ) internal view returns (BorrowTest memory) { test.morphoMarket = morpho.market(market.underlying); test.indexes = morpho.updatedIndexes(market.underlying); test.scaledP2PBorrow = morpho.scaledP2PBorrowBalance(market.underlying, onBehalf); @@ -47,7 +47,6 @@ contract TestIntegrationBorrow is IntegrationTest { // Assert Morpho's position on pool. assertApproxEqAbs(market.variableBorrowOf(address(morpho)), amount, 2, "morphoVariableBorrow != amount"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert receiver's underlying balance. assertEq( @@ -65,7 +64,7 @@ contract TestIntegrationBorrow is IntegrationTest { address onBehalf, address receiver, BorrowTest memory test - ) internal returns (BorrowTest memory) { + ) internal view returns (BorrowTest memory) { test.morphoMarket = morpho.market(market.underlying); test.indexes = morpho.updatedIndexes(market.underlying); test.scaledP2PBorrow = morpho.scaledP2PBorrowBalance(market.underlying, onBehalf); @@ -99,7 +98,6 @@ contract TestIntegrationBorrow is IntegrationTest { // Assert Morpho's position on pool. assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 2, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert receiver's underlying balance. assertEq( @@ -542,7 +540,6 @@ contract TestIntegrationBorrow is IntegrationTest { TestMarket storage market = testMarkets[_randomUnderlying(seed)]; - poolAdmin.setReserveStableRateBorrowing(market.underlying, false); poolAdmin.setReserveBorrowing(market.underlying, false); vm.expectRevert(Errors.BorrowNotEnabled.selector); diff --git a/test/integration/TestIntegrationClaimToTreasury.sol b/test/integration/TestIntegrationClaimToTreasury.sol index 5deed239b..d985fe3ea 100644 --- a/test/integration/TestIntegrationClaimToTreasury.sol +++ b/test/integration/TestIntegrationClaimToTreasury.sol @@ -59,7 +59,7 @@ contract TestIntegrationClaimToTreasury is IntegrationTest { function testClaimToTreasuryShouldPassIfZeroUnderlyings( uint256[] calldata claimedAmounts, - uint256[] calldata balanceAmounts + uint104[] calldata balanceAmounts ) public { address[] memory claimedUnderlyings; uint256[] memory beforeBalanceTreasury = new uint256[](allUnderlyings.length); @@ -86,7 +86,7 @@ contract TestIntegrationClaimToTreasury is IntegrationTest { } } - function testClaimToTreasuryShouldPassIfAmountsClaimedEqualZero(uint256[] calldata balanceAmounts) public { + function testClaimToTreasuryShouldPassIfAmountsClaimedEqualZero(uint104[] calldata balanceAmounts) public { uint256[] memory claimedAmounts = new uint256[](balanceAmounts.length); address[] memory claimedUnderlyings = allUnderlyings; address treasuryVault = address(1); @@ -106,8 +106,8 @@ contract TestIntegrationClaimToTreasury is IntegrationTest { } } - function testClaimToTreasury( - uint256[] memory balanceAmounts, + function testClaimToTreasuryy( + uint104[] memory balanceAmounts, uint256[] memory idleAmounts, uint256[] memory claimedAmounts ) public { @@ -121,8 +121,9 @@ contract TestIntegrationClaimToTreasury is IntegrationTest { vm.assume(treasuryVault != address(0)); for (uint256 i = 0; i < claimedUnderlyings.length; ++i) { - balanceAmounts[i] = - bound(balanceAmounts[i], 0, type(uint256).max - ERC20(claimedUnderlyings[i]).balanceOf(treasuryVault)); + balanceAmounts[i] = uint104( + bound(balanceAmounts[i], 0, type(uint104).max - ERC20(claimedUnderlyings[i]).balanceOf(treasuryVault)) + ); idleAmounts[i] = bound(idleAmounts[i], 0, balanceAmounts[i]); morpho.market(claimedUnderlyings[i]).idleSupply = idleAmounts[i]; morpho.market(claimedUnderlyings[i]).aToken = address(1); @@ -158,4 +159,12 @@ contract TestIntegrationClaimToTreasury is IntegrationTest { ); } } + + function testDeal(uint104 amount) public { + for (uint256 i; i < allUnderlyings.length; ++i) { + deal(allUnderlyings[i], address(this), amount); + uint256 balanceAfter = ERC20(allUnderlyings[i]).balanceOf(address(this)); + assertEq(balanceAfter, amount); + } + } } diff --git a/test/integration/TestIntegrationEModeNative.sol b/test/integration/TestIntegrationEModeNative.sol index fae2a600a..16c17f24a 100644 --- a/test/integration/TestIntegrationEModeNative.sol +++ b/test/integration/TestIntegrationEModeNative.sol @@ -44,7 +44,7 @@ contract TestIntegrationEModeNative is IntegrationTest { user.withdrawCollateral( lsdNative, - wNativeMarket.collateralized(lsdNativeMarket, rawCollateral, eModeCategoryId) - borrowed, + (rawCollateral - lsdNativeMarket.minCollateral(wNativeMarket, borrowed, eModeCategoryId)) / 2, onBehalf, receiver ); diff --git a/test/integration/TestIntegrationFee.sol b/test/integration/TestIntegrationFee.sol index 572a6676b..999ec73e8 100644 --- a/test/integration/TestIntegrationFee.sol +++ b/test/integration/TestIntegrationFee.sol @@ -9,7 +9,7 @@ contract TestIntegrationFee is IntegrationTest { using WadRayMath for uint256; using PercentageMath for uint256; - function _assertFee(Types.Market memory marketBefore) internal { + function _assertFee(Types.Market memory marketBefore) internal view { Types.Market memory marketAfter = morpho.market(marketBefore.underlying); uint256 p2pBorrow = marketBefore.deltas.borrow.scaledP2PTotal.rayMul(marketBefore.indexes.borrow.p2pIndex) diff --git a/test/integration/TestIntegrationLiquidate.sol b/test/integration/TestIntegrationLiquidate.sol index 184272395..0f09dc82d 100644 --- a/test/integration/TestIntegrationLiquidate.sol +++ b/test/integration/TestIntegrationLiquidate.sol @@ -377,7 +377,7 @@ contract TestIntegrationLiquidate is IntegrationTest { TestMarket storage borrowedMarket, TestMarket storage collateralMarket, address borrower - ) internal { + ) internal view { assertLe(test.seized, test.collateralBalanceBefore, "seized > collateral"); assertLe( test.repaid, @@ -404,7 +404,7 @@ contract TestIntegrationLiquidate is IntegrationTest { TestMarket storage borrowedMarket, TestMarket storage collateralMarket, address borrower - ) internal { + ) internal view { assertLe(test.seized, test.collateralBalanceBefore, "seized > collateral"); assertLe( test.repaid, diff --git a/test/integration/TestIntegrationMorphoGetters.sol b/test/integration/TestIntegrationMorphoGetters.sol index 0fc333e3c..c41769693 100644 --- a/test/integration/TestIntegrationMorphoGetters.sol +++ b/test/integration/TestIntegrationMorphoGetters.sol @@ -8,7 +8,7 @@ contract TestIntegrationMorphoGetters is IntegrationTest { using TestMarketLib for TestMarket; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - function testDomainSeparator() public { + function testDomainSeparator() public view { assertEq( morpho.DOMAIN_SEPARATOR(), keccak256( @@ -23,31 +23,31 @@ contract TestIntegrationMorphoGetters is IntegrationTest { ); } - function testPool() public { + function testPool() public view { assertEq(morpho.pool(), address(pool)); } - function testAddressesProvider() public { + function testAddressesProvider() public view { assertEq(morpho.addressesProvider(), address(addressesProvider)); } - function testPositionsManager() public { + function testPositionsManager() public view { assertEq(morpho.positionsManager(), address(positionsManager)); } - function testRewardsManager() public { + function testRewardsManager() public view { assertEq(morpho.rewardsManager(), address(rewardsManager)); } - function testIsClaimRewardsPaused() public { + function testIsClaimRewardsPaused() public view { assertEq(morpho.isClaimRewardsPaused(), false); } - function testEModeCategoryId() public { + function testEModeCategoryId() public view { assertEq(morpho.eModeCategoryId(), pool.getUserEMode(address(morpho))); } - function testMarketsCreated() public { + function testMarketsCreated() public view { address[] memory markets = morpho.marketsCreated(); for (uint256 i; i < markets.length; ++i) { @@ -55,7 +55,7 @@ contract TestIntegrationMorphoGetters is IntegrationTest { } } - function testDefaultIterations() public { + function testDefaultIterations() public view { Types.Iterations memory defaultIterations = morpho.defaultIterations(); assertEq(defaultIterations.repay, DEFAULT_MAX_ITERATIONS); diff --git a/test/integration/TestIntegrationMorphoSetters.sol b/test/integration/TestIntegrationMorphoSetters.sol index 2a76a4754..6ad4d57d7 100644 --- a/test/integration/TestIntegrationMorphoSetters.sol +++ b/test/integration/TestIntegrationMorphoSetters.sol @@ -11,7 +11,7 @@ contract TestIntegrationMorphoSetters is IntegrationTest { uint16 private constant DEFAULT_P2P_INDEX_CURSOR = 1_000; address private constant DEFAULT_PRANKER = address(uint160(uint256(keccak256(abi.encode(42069))))); - function testShouldBeInitialized() public { + function testShouldBeInitialized() public view { assertEq(Ownable2StepUpgradeable(address(morpho)).owner(), address(this), "owner"); assertEq(morpho.pool(), address(pool), "pool"); assertEq(morpho.addressesProvider(), address(addressesProvider), "addressesProvider"); diff --git a/test/integration/TestIntegrationPermit2.sol b/test/integration/TestIntegrationPermit2.sol index 2b3e7c2de..df984cb01 100644 --- a/test/integration/TestIntegrationPermit2.sol +++ b/test/integration/TestIntegrationPermit2.sol @@ -60,10 +60,10 @@ contract TestIntegrationPermit2 is IntegrationTest { vm.prank(delegator); ERC20(market.underlying).safeApprove(address(PERMIT2), type(uint256).max); - timestamp = bound(timestamp, 0, Math.min(deadline, type(uint48).max) - block.timestamp); + timestamp = bound(timestamp, 0, Math.min(deadline, type(uint32).max) - block.timestamp); vm.warp(block.timestamp + timestamp); - _deal(market.underlying, delegator, amount); + deal(market.underlying, delegator, amount); uint256 balanceBefore = ERC20(market.underlying).balanceOf(delegator); uint256 balanceSupplyBefore = morpho.supplyBalance(market.underlying, delegator); @@ -105,7 +105,7 @@ contract TestIntegrationPermit2 is IntegrationTest { timestamp = bound(timestamp, 0, Math.min(deadline, type(uint48).max) - block.timestamp); vm.warp(block.timestamp + timestamp); - _deal(market.underlying, delegator, amount); + deal(market.underlying, delegator, amount); uint256 balanceBefore = ERC20(market.underlying).balanceOf(delegator); uint256 balanceSupplyBefore = morpho.supplyBalance(market.underlying, delegator); @@ -158,7 +158,7 @@ contract TestIntegrationPermit2 is IntegrationTest { amount = morpho.borrowBalance(market.underlying, onBehalf) - 1; Types.Signature memory sig = _signPermit2(market.underlying, delegator, spender, amount, deadline, privateKey); - _deal(market.underlying, delegator, amount); + deal(market.underlying, delegator, amount); uint256 balanceBefore = ERC20(market.underlying).balanceOf(delegator); @@ -200,7 +200,7 @@ contract TestIntegrationPermit2 is IntegrationTest { Types.Signature memory sig = _signPermit2(market.underlying, delegator, spender, type(uint160).max, deadline, privateKey); - _deal(market.underlying, delegator, type(uint160).max); + deal(market.underlying, delegator, type(uint160).max); vm.prank(delegator); morpho.repayWithPermit(market.underlying, type(uint160).max, onBehalf, deadline, sig); @@ -232,7 +232,7 @@ contract TestIntegrationPermit2 is IntegrationTest { vm.prank(delegator); ERC20(market.underlying).safeApprove(address(PERMIT2), type(uint256).max); - _deal(market.underlying, delegator, amount); + deal(market.underlying, delegator, amount); timestamp = bound(timestamp, deadline + 1, type(uint256).max); vm.warp(timestamp); @@ -267,7 +267,7 @@ contract TestIntegrationPermit2 is IntegrationTest { vm.prank(delegator); ERC20(market.underlying).safeApprove(address(PERMIT2), type(uint256).max); - _deal(market.underlying, delegator, 2 * amount); + deal(market.underlying, delegator, 2 * amount); timestamp = bound(timestamp, 0, Math.min(deadline, type(uint48).max) - block.timestamp); vm.warp(block.timestamp + timestamp); @@ -305,7 +305,7 @@ contract TestIntegrationPermit2 is IntegrationTest { vm.prank(delegator); ERC20(market.underlying).safeApprove(address(PERMIT2), type(uint256).max); - _deal(market.underlying, delegator, amount); + deal(market.underlying, delegator, amount); timestamp = bound(timestamp, 0, Math.min(deadline, type(uint48).max) - block.timestamp); vm.warp(block.timestamp + timestamp); @@ -339,7 +339,7 @@ contract TestIntegrationPermit2 is IntegrationTest { vm.prank(delegator); ERC20(market.underlying).safeApprove(address(PERMIT2), type(uint256).max); - _deal(market.underlying, delegator, amount); + deal(market.underlying, delegator, amount); timestamp = bound(timestamp, 0, Math.min(deadline, type(uint48).max) - block.timestamp); vm.warp(block.timestamp + timestamp); @@ -376,7 +376,7 @@ contract TestIntegrationPermit2 is IntegrationTest { vm.prank(delegator); ERC20(market.underlying).safeApprove(address(PERMIT2), type(uint256).max); - _deal(market.underlying, delegator, amount); + deal(market.underlying, delegator, amount); timestamp = bound(timestamp, 0, Math.min(deadline, type(uint48).max) - block.timestamp); vm.warp(block.timestamp + timestamp); diff --git a/test/integration/TestIntegrationRepay.sol b/test/integration/TestIntegrationRepay.sol index e0bad90fc..295af66ae 100644 --- a/test/integration/TestIntegrationRepay.sol +++ b/test/integration/TestIntegrationRepay.sol @@ -72,7 +72,6 @@ contract TestIntegrationRepay is IntegrationTest { market.supplyOf(address(morpho)), test.morphoSupplyBefore, 2, "morphoSupply != morphoSupplyBefore" ); assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 1, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert user's underlying balance. assertApproxEqAbs( @@ -156,7 +155,6 @@ contract TestIntegrationRepay is IntegrationTest { "morphoSupply != morphoSupplyBefore + promoted" ); assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 2, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert user's underlying balance. assertApproxLeAbs( @@ -241,7 +239,6 @@ contract TestIntegrationRepay is IntegrationTest { "morphoSupply != morphoSupplyBefore + supplyGapBefore" ); assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 1, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); assertApproxEqDust(_supplyGap(market), 0, "supplyGapAfter != 0"); // Assert user's underlying balance. @@ -329,7 +326,6 @@ contract TestIntegrationRepay is IntegrationTest { morphoSupply, test.morphoSupplyBefore + test.borrowed, 2, "morphoSupply != morphoSupplyBefore + borrowed" ); assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 1, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert user's underlying balance. assertApproxEqAbs( diff --git a/test/integration/TestIntegrationRewardsManager.sol b/test/integration/TestIntegrationRewardsManager.sol index ca8e15bc2..c37b540c4 100644 --- a/test/integration/TestIntegrationRewardsManager.sol +++ b/test/integration/TestIntegrationRewardsManager.sol @@ -12,6 +12,7 @@ import "test/helpers/IntegrationTest.sol"; contract TestIntegrationRewardsManager is IntegrationTest { using ConfigLib for Config; + using Math for uint256; // From the rewards manager event Accrued( @@ -27,6 +28,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { address[] internal assets; address[NB_REWARDS] internal expectedRewardTokens; + mapping(address => uint256) expectedRewardTokenRank; uint256[NB_REWARDS] internal noAccruedRewards = [0, 0]; RewardsDataTypes.RewardsConfigInput[] rewardsConfig; @@ -39,12 +41,18 @@ contract TestIntegrationRewardsManager is IntegrationTest { vUsdc = testMarkets[usdc].variableDebtToken; assets = [aDai, vDai, aUsdc, vUsdc]; expectedRewardTokens = [wNative, link]; + expectedRewardTokenRank[wNative] = 1; + expectedRewardTokenRank[link] = 2; _setUpRewardsConfig(); } function _setUpRewardsConfig() internal { ITransferStrategyBase transferStrategy = new PullRewardsTransferStrategyMock(); + + uint256 endRewards = block.timestamp + 365 days; + require(endRewards <= type(uint32).max, "rewards end too far in the future"); + for (uint256 i; i < expectedRewardTokens.length; ++i) { address rewardToken = expectedRewardTokens[i]; @@ -56,7 +64,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { RewardsDataTypes.RewardsConfigInput({ emissionPerSecond: 0.001 ether, totalSupply: 0, - distributionEnd: 1711944000, + distributionEnd: uint32(endRewards), asset: assets[i * 2], reward: rewardToken, transferStrategy: transferStrategy, @@ -67,7 +75,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { RewardsDataTypes.RewardsConfigInput({ emissionPerSecond: 0.001 ether, totalSupply: 0, - distributionEnd: 1711944000, + distributionEnd: uint32(endRewards), asset: assets[i * 2 + 1], reward: rewardToken, transferStrategy: transferStrategy, @@ -83,34 +91,44 @@ contract TestIntegrationRewardsManager is IntegrationTest { function _assertClaimRewards( address[] memory rewardTokens, uint256[] memory amounts, - uint256[] memory rewardBalancesBefore, + uint256[NB_REWARDS] memory rewardBalancesBefore, uint256[NB_REWARDS] memory expectedAccruedRewards, string memory suffix - ) internal { - assertEq(rewardTokens.length, NB_REWARDS, string.concat("rewardTokens length", " ", suffix)); - assertEq(amounts.length, NB_REWARDS, string.concat("amounts length", " ", suffix)); + ) internal view { + assertEq( + amounts.length, rewardTokens.length, string.concat("rewardTokens.length <> amount.length", " ", suffix) + ); - for (uint256 i; i < NB_REWARDS; ++i) { - address rewardToken = expectedRewardTokens[i]; - uint256 accruedRewards = expectedAccruedRewards[i]; + for (uint256 i; i < rewardTokens.length; ++i) { + if (amounts[i] == 0) continue; + + address rewardToken = rewardTokens[i]; + string memory str = string.concat("(", vm.toString(rewardToken), ")"); - string memory index = string.concat("[", vm.toString(i), "]"); + uint256 rankReward = expectedRewardTokenRank[rewardToken]; + assertTrue(rankReward != 0, string.concat("unexpected reward token", str, " ", suffix)); - assertEq(rewardTokens[i], rewardToken, string.concat("rewardTokens", index, " ", suffix)); - assertEq(amounts[i], accruedRewards, string.concat("amounts", index, " ", suffix)); + uint256 accruedRewards = expectedAccruedRewards[rankReward - 1]; + assertEq(amounts[i], accruedRewards, string.concat("amounts", str, " ", suffix)); assertEq( ERC20(rewardToken).balanceOf(address(user)), - rewardBalancesBefore[i] + accruedRewards, - string.concat("balance", index, " ", suffix) + rewardBalancesBefore[rankReward - 1] + accruedRewards, + string.concat("balance", str, " ", suffix) ); } } - function testGetMorpho() public { + function _rewardBalances() internal view returns (uint256[NB_REWARDS] memory rewardBalances) { + for (uint256 i; i < NB_REWARDS; i++) { + rewardBalances[i] = ERC20(expectedRewardTokens[i]).balanceOf(address(user)); + } + } + + function testGetMorpho() public view { assertEq(rewardsManager.MORPHO(), address(morpho)); } - function testGetRewardsController() public { + function testGetRewardsController() public view { assertEq(rewardsManager.REWARDS_CONTROLLER(), address(rewardsController)); } @@ -303,7 +321,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { currentTimestamp = block.timestamp; currentTimestamp = currentTimestamp > distributionEnd ? distributionEnd : currentTimestamp; - totalEmitted = emissionPerSecond * (currentTimestamp - lastUpdateTimestamp) * assetUnit + totalEmitted = emissionPerSecond * currentTimestamp.zeroFloorSub(lastUpdateTimestamp) * assetUnit / IScaledBalanceToken(aDai).scaledTotalSupply(); assertEq(rewardsManager.getAssetIndex(aDai, wNative), rewardIndex + totalEmitted, "index 2"); @@ -336,9 +354,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { assertGt(accruedRewards, 0, "accruedRewards > 0"); - uint256[] memory rewardBalancesBefore = new uint256[](NB_REWARDS); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); - rewardBalancesBefore[1] = ERC20(link).balanceOf(address(user)); + uint256[NB_REWARDS] memory rewardBalancesBefore = _rewardBalances(); vm.expectEmit(true, true, true, true); emit Accrued( @@ -359,7 +375,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { assertEq(accruedRewards, 0, "accruedRewards == 0"); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); + rewardBalancesBefore = _rewardBalances(); (rewardTokens, amounts) = morpho.claimRewards(assets, address(user)); @@ -379,9 +395,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { assertGt(accruedRewards, 0, "accruedRewards > 0"); - uint256[] memory rewardBalancesBefore = new uint256[](NB_REWARDS); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); - rewardBalancesBefore[1] = ERC20(link).balanceOf(address(user)); + uint256[NB_REWARDS] memory rewardBalancesBefore = _rewardBalances(); vm.expectEmit(true, true, true, true); emit Accrued(aDai, wNative, address(user), rewardsManager.getAssetIndex(aDai, wNative), accruedRewards); @@ -400,7 +414,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { assertEq(accruedRewards, 0, "accruedRewards == 0"); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); + rewardBalancesBefore = _rewardBalances(); (rewardTokens, amounts) = morpho.claimRewards(assets, address(user)); @@ -421,9 +435,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { assertGt(accruedRewards, 0, "accruedRewards > 0"); - uint256[] memory rewardBalancesBefore = new uint256[](NB_REWARDS); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); - rewardBalancesBefore[1] = ERC20(link).balanceOf(address(user)); + uint256[NB_REWARDS] memory rewardBalancesBefore = _rewardBalances(); vm.expectEmit(true, true, true, true); emit Accrued(vDai, wNative, address(user), rewardsManager.getAssetIndex(vDai, wNative), accruedRewards); @@ -443,7 +455,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { assertEq(accruedRewards, 0, "accruedRewards == 0"); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); + rewardBalancesBefore = _rewardBalances(); (rewardTokens, amounts) = morpho.claimRewards(assets, address(user)); @@ -467,27 +479,26 @@ contract TestIntegrationRewardsManager is IntegrationTest { (address[] memory rewardsList, uint256[] memory unclaimed) = rewardsManager.getAllUserRewards(assets, address(user)); - assertEq(rewardsList[0], wNative, "rewardsList[0]"); - assertEq(rewardsList[1], link, "rewardsList[1]"); - assertGt(unclaimed[0], 0, "unclaimed[0] > 0"); - assertGt(unclaimed[1], 0, "unclaimed[1] > 0"); + uint256 unclaimedWNative = unclaimed[unclaimed.length - 2]; + uint256 unclaimedLink = unclaimed[unclaimed.length - 1]; - uint256[] memory rewardBalancesBefore = new uint256[](NB_REWARDS); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); - rewardBalancesBefore[1] = ERC20(link).balanceOf(address(user)); + assertGt(unclaimedWNative, 0, "unclaimedWNative > 0"); + assertGt(unclaimedLink, 0, "unclaimedLink > 0"); + + uint256[NB_REWARDS] memory rewardBalancesBefore = _rewardBalances(); vm.expectEmit(true, true, true, true); - emit Accrued(vDai, wNative, address(user), rewardsManager.getAssetIndex(vDai, wNative), unclaimed[0]); + emit Accrued(vDai, wNative, address(user), rewardsManager.getAssetIndex(vDai, wNative), unclaimedWNative); vm.expectEmit(true, true, true, true); - emit Accrued(aUsdc, link, address(user), rewardsManager.getAssetIndex(aUsdc, link), unclaimed[1]); + emit Accrued(aUsdc, link, address(user), rewardsManager.getAssetIndex(aUsdc, link), unclaimedLink); vm.expectEmit(true, true, true, true); - emit Events.RewardsClaimed(address(this), address(user), wNative, unclaimed[0]); + emit Events.RewardsClaimed(address(this), address(user), wNative, unclaimedWNative); vm.expectEmit(true, true, true, true); - emit Events.RewardsClaimed(address(this), address(user), link, unclaimed[1]); + emit Events.RewardsClaimed(address(this), address(user), link, unclaimedLink); (address[] memory rewardTokens, uint256[] memory amounts) = morpho.claimRewards(assets, address(user)); - _assertClaimRewards(rewardTokens, amounts, rewardBalancesBefore, [unclaimed[0], unclaimed[1]], "(1)"); + _assertClaimRewards(rewardTokens, amounts, rewardBalancesBefore, [unclaimedWNative, unclaimedLink], "(1)"); user.approve(dai, type(uint256).max); user.repay(dai, type(uint256).max); @@ -497,11 +508,12 @@ contract TestIntegrationRewardsManager is IntegrationTest { (rewardsList, unclaimed) = rewardsManager.getAllUserRewards(assets, address(user)); - assertEq(unclaimed[0], 0, "unclaimed[0] == 0"); - assertEq(unclaimed[1], 0, "unclaimed[1] == 0"); + for (uint256 i; i < unclaimed.length; i++) { + string memory errorString = string.concat("unclaimed[", vm.toString(i), "] > 0"); + assertEq(unclaimed[i], 0, errorString); + } - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); - rewardBalancesBefore[1] = ERC20(link).balanceOf(address(user)); + rewardBalancesBefore = _rewardBalances(); (rewardTokens, amounts) = morpho.claimRewards(assets, address(user)); @@ -537,9 +549,7 @@ contract TestIntegrationRewardsManager is IntegrationTest { assertEq(rewardsAfter, rewardsBefore, "rewardsAfter != rewardsBefore (2)"); - uint256[] memory rewardBalancesBefore = new uint256[](NB_REWARDS); - rewardBalancesBefore[0] = ERC20(wNative).balanceOf(address(user)); - rewardBalancesBefore[1] = ERC20(link).balanceOf(address(user)); + uint256[NB_REWARDS] memory rewardBalancesBefore = _rewardBalances(); vm.expectEmit(true, true, true, true); emit Accrued(aDai, wNative, address(user), rewardsManager.getAssetIndex(aDai, wNative), rewardsAfter); diff --git a/test/integration/TestIntegrationSupply.sol b/test/integration/TestIntegrationSupply.sol index e37884fcd..4cacf8001 100644 --- a/test/integration/TestIntegrationSupply.sol +++ b/test/integration/TestIntegrationSupply.sol @@ -23,6 +23,7 @@ contract TestIntegrationSupply is IntegrationTest { function _assertSupplyPool(TestMarket storage market, uint256 amount, address onBehalf, SupplyTest memory test) internal + view returns (SupplyTest memory) { test.morphoMarket = morpho.market(market.underlying); @@ -53,7 +54,6 @@ contract TestIntegrationSupply is IntegrationTest { 1, "morphoSupply != morphoSupplyBefore + amount" ); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert user's underlying balance. assertApproxEqAbs( @@ -65,6 +65,7 @@ contract TestIntegrationSupply is IntegrationTest { function _assertSupplyP2P(TestMarket storage market, uint256 amount, address onBehalf, SupplyTest memory test) internal + view { test.morphoMarket = morpho.market(market.underlying); test.indexes = morpho.updatedIndexes(market.underlying); @@ -101,10 +102,9 @@ contract TestIntegrationSupply is IntegrationTest { // Assert Morpho's position on pool. assertApproxGeAbs( - market.supplyOf(address(morpho)), test.morphoSupplyBefore, 2, "morphoSupplyAfter != morphoSupplyBefore" + market.supplyOf(address(morpho)), test.morphoSupplyBefore, 3, "morphoSupplyAfter != morphoSupplyBefore" ); assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 1, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert user's underlying balance. assertApproxEqAbs( diff --git a/test/integration/TestIntegrationUpgrade.sol b/test/integration/TestIntegrationUpgrade.sol index eccd72ce2..c3b3a4d30 100644 --- a/test/integration/TestIntegrationUpgrade.sol +++ b/test/integration/TestIntegrationUpgrade.sol @@ -143,7 +143,7 @@ contract TestIntegrationUpgrade is IntegrationTest { u.userBorrows = morpho.userBorrows(user); } - function _assertStorageEq(StorageToCheck memory s1, StorageToCheck memory s2) internal { + function _assertStorageEq(StorageToCheck memory s1, StorageToCheck memory s2) internal pure { assertEq(s1.owner, s2.owner, "owner"); assertEq(s1.pool, s2.pool, "pool"); assertEq(s1.addressesProvider, s2.addressesProvider, "addressesProvider"); @@ -248,7 +248,7 @@ contract TestIntegrationUpgrade is IntegrationTest { _assertUserStorageEq(s1.borrower2, s2.borrower2); } - function _assertUserStorageEq(UserStorageToCheck memory u1, UserStorageToCheck memory u2) internal { + function _assertUserStorageEq(UserStorageToCheck memory u1, UserStorageToCheck memory u2) internal pure { assertEq(u1.scaledPoolSupplyBalance, u2.scaledPoolSupplyBalance, "scaledPoolSupplyBalance"); assertEq(u1.scaledPoolBorrowBalance, u2.scaledPoolBorrowBalance, "scaledPoolBorrowBalance"); assertEq(u1.scaledP2PSupplyBalance, u2.scaledP2PSupplyBalance, "scaledP2PSupplyBalance"); diff --git a/test/integration/TestIntegrationWETHGateway.sol b/test/integration/TestIntegrationWETHGateway.sol index b06dd59c8..adff39c18 100644 --- a/test/integration/TestIntegrationWETHGateway.sol +++ b/test/integration/TestIntegrationWETHGateway.sol @@ -19,15 +19,15 @@ contract TestIntegrationWETHGateway is IntegrationTest { wethGateway = new WETHGateway(address(morpho)); } - function invariantWETHAllowance() public { + function invariantWETHAllowance() public view { assertEq(ERC20(weth).allowance(address(wethGateway), address(morpho)), type(uint256).max); } - function invariantETHBalance() public { + function invariantETHBalance() public view { assertEq(address(wethGateway).balance, 0); } - function invariantWETHBalance() public { + function invariantWETHBalance() public view { assertEq(ERC20(weth).balanceOf(address(wethGateway)), 0); } @@ -134,7 +134,7 @@ contract TestIntegrationWETHGateway is IntegrationTest { if (receiver != address(this)) assertEq(address(this).balance, balanceBefore, "balanceAfter != balanceBefore"); assertApproxEqAbs(withdrawn, Math.min(toWithdraw, supply), 2, "withdrawn != minimum"); assertApproxEqAbs( - morpho.supplyBalance(weth, address(this)), supply - withdrawn, 2, "supplyBalance != supply - toWithdraw" + morpho.supplyBalance(weth, address(this)), supply - withdrawn, 3, "supplyBalance != supply - toWithdraw" ); assertApproxEqAbs( receiver.balance, @@ -181,7 +181,7 @@ contract TestIntegrationWETHGateway is IntegrationTest { assertApproxEqAbs( morpho.collateralBalance(weth, address(this)), collateral - withdrawn, - 2, + 3, "collateralBalance != collateral - toWithdraw" ); assertApproxEqAbs( diff --git a/test/integration/TestIntegrationWithdraw.sol b/test/integration/TestIntegrationWithdraw.sol index 1ca98c7ea..c07ba1e42 100644 --- a/test/integration/TestIntegrationWithdraw.sol +++ b/test/integration/TestIntegrationWithdraw.sol @@ -75,7 +75,6 @@ contract TestIntegrationWithdraw is IntegrationTest { market.supplyOf(address(morpho)), test.morphoSupplyBefore, 2, "morphoSupply != morphoSupplyBefore" ); assertApproxEqAbs(market.variableBorrowOf(address(morpho)), 0, 2, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert receiver's underlying balance. assertApproxEqAbs( @@ -168,7 +167,6 @@ contract TestIntegrationWithdraw is IntegrationTest { market.supplyOf(address(morpho)), test.morphoSupplyBefore, 2, "morphoSupply != morphoSupplyBefore" ); assertApproxEqAbs(market.variableBorrowOf(address(morpho)), promoted, 3, "morphoVariableBorrow != promoted"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert receiver's underlying balance. assertApproxLeAbs( @@ -186,7 +184,7 @@ contract TestIntegrationWithdraw is IntegrationTest { assertEq(test.morphoMarket.idleSupply, 0, "idleSupply != 0"); } - function testShouldWithdrawAllP2PSupplyWhenBorrowCapExceededWithIdleSupply( + function testShouldWithdrawP2PSupplyWhenBorrowCapExceededWithIdleSupply( uint256 seed, uint256 borrowCap, uint256 amount, @@ -204,18 +202,20 @@ contract TestIntegrationWithdraw is IntegrationTest { test.supplied = _boundSupply(market, amount); test.supplied = _promoteSupply(promoter1, market, test.supplied); // 100% peer-to-peer. - amount = bound(amount, test.supplied + 1, type(uint256).max); user.approve(market.underlying, test.supplied); user.supply(market.underlying, test.supplied, onBehalf); - _increaseIdleSupply(promoter2, market, test.supplied); + uint256 idleSupplyBefore = _increaseIdleSupply(promoter2, market, test.supplied); + amount = bound(amount, 1, idleSupplyBefore); + assertApproxEqAbs(idleSupplyBefore, morpho.market(market.underlying).idleSupply, 1, "idleSupply"); borrowCap = _boundBorrowCapExceeded(market, test.supplied, borrowCap); _setBorrowCap(market, borrowCap); test.balanceBefore = ERC20(market.underlying).balanceOf(receiver); test.morphoSupplyBefore = market.supplyOf(address(morpho)); + uint256 promoter2Supply = morpho.supplyBalance(market.underlying, address(promoter2)); vm.expectEmit(true, true, true, false, address(morpho)); emit Events.IdleSupplyUpdated(market.underlying, 0); @@ -236,36 +236,34 @@ contract TestIntegrationWithdraw is IntegrationTest { test.borrows = morpho.userBorrows(onBehalf); // Assert balances on Morpho. - assertEq(test.scaledP2PSupply, 0, "scaledP2PSupply != 0"); assertEq(test.scaledPoolSupply, 0, "scaledPoolSupply != 0"); assertEq(test.scaledCollateral, 0, "scaledCollateral != 0"); - assertApproxLeAbs(test.withdrawn, test.supplied, 2, "withdrawn != supplied"); + assertApproxLeAbs(test.withdrawn, amount, 2, "withdrawn != amount"); assertEq( - morpho.scaledPoolBorrowBalance(market.underlying, address(promoter1)), 0, "promoter1ScaledP2PBorrow != 0" + morpho.scaledPoolBorrowBalance(market.underlying, address(promoter1)), 0, "promoter1ScaledPoolBorrow != 0" ); assertEq( - morpho.scaledPoolBorrowBalance(market.underlying, address(promoter2)), 0, "promoter2ScaledPoolBorrow != 0" + morpho.scaledPoolSupplyBalance(market.underlying, address(promoter2)), 0, "promoter2ScaledPoolSupply != 0" ); assertEq(test.collaterals.length, 0, "collaterals.length"); assertEq(test.borrows.length, 0, "borrows.length"); // Assert Morpho getters. - assertEq(morpho.supplyBalance(market.underlying, onBehalf), 0, "supply != 0"); + assertApproxEqAbs(morpho.supplyBalance(market.underlying, onBehalf), test.supplied - amount, 3, "supply != 0"); assertEq(morpho.collateralBalance(market.underlying, onBehalf), 0, "collateral != 0"); assertApproxEqAbs( morpho.borrowBalance(market.underlying, address(promoter1)), test.supplied, 1, "promoter1Borrow != supplied" ); - assertApproxLeAbs( - morpho.supplyBalance(market.underlying, address(promoter2)), test.supplied, 2, "promoter2Supply != supplied" + assertApproxEqAbs( + morpho.supplyBalance(market.underlying, address(promoter2)), promoter2Supply, 2, "promoter2Supply" ); // Assert Morpho's position on pool. assertApproxEqAbs( market.supplyOf(address(morpho)), test.morphoSupplyBefore, 1, "morphoSupply != morphoSupplyBefore" ); - assertApproxGeAbs(market.variableBorrowOf(address(morpho)), 0, 2, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); + assertApproxGeAbs(market.variableBorrowOf(address(morpho)), 0, 3, "morphoVariableBorrow != 0"); // Assert user's underlying balance. assertApproxLeAbs( @@ -279,9 +277,9 @@ contract TestIntegrationWithdraw is IntegrationTest { assertEq(test.morphoMarket.deltas.supply.scaledDelta, 0, "scaledSupplyDelta != 0"); assertApproxEqAbs( test.morphoMarket.deltas.supply.scaledP2PTotal.rayMul(test.indexes.supply.p2pIndex), - test.supplied, - 1, - "totalSupplyP2P != supplied" + test.supplied + test.morphoMarket.idleSupply, + 2, + "totalSupplyP2P != supplied + idleSupplyAfter" ); assertEq(test.morphoMarket.deltas.borrow.scaledDelta, 0, "scaledBorrowDelta != 0"); assertApproxEqAbs( @@ -290,7 +288,9 @@ contract TestIntegrationWithdraw is IntegrationTest { 2, "totalBorrowP2P != supplied" ); - assertApproxEqAbs(test.morphoMarket.idleSupply, 0, 2, "idleSupply != 0"); + assertApproxEqAbs( + test.morphoMarket.idleSupply, idleSupplyBefore - amount, 2, "idleSupplyAfter != idleSupplyBefore - amount" + ); } function testShouldWithdrawAllP2PSupplyWhenDemotedZero( @@ -364,7 +364,6 @@ contract TestIntegrationWithdraw is IntegrationTest { market.supplyOf(address(morpho)), test.morphoSupplyBefore, 1, "morphoSupply != morphoSupplyBefore" ); assertApproxEqAbs(morphoVariableBorrow, test.supplied, 2, "morphoVariableBorrow != supplied"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert user's underlying balance. assertApproxLeAbs( diff --git a/test/integration/TestIntegrationWithdrawCollateral.sol b/test/integration/TestIntegrationWithdrawCollateral.sol index 5c2aaeda1..11341cbbf 100644 --- a/test/integration/TestIntegrationWithdrawCollateral.sol +++ b/test/integration/TestIntegrationWithdrawCollateral.sol @@ -72,10 +72,9 @@ contract TestIntegrationWithdrawCollateral is IntegrationTest { // Assert Morpho's position on pool. assertApproxEqAbs( - market.supplyOf(address(morpho)), test.morphoSupplyBefore, 2, "morphoSupply != morphoSupplyBefore" + market.supplyOf(address(morpho)), test.morphoSupplyBefore, 3, "morphoSupply != morphoSupplyBefore" ); assertEq(market.variableBorrowOf(address(morpho)), 0, "morphoVariableBorrow != 0"); - assertEq(market.stableBorrowOf(address(morpho)), 0, "morphoStableBorrow != 0"); // Assert receiver's underlying balance. assertApproxEqAbs( diff --git a/test/internal/TestInternalEMode.sol b/test/internal/TestInternalEMode.sol index 140bb0a09..b99e58137 100644 --- a/test/internal/TestInternalEMode.sol +++ b/test/internal/TestInternalEMode.sol @@ -44,8 +44,6 @@ contract TestInternalEMode is InternalTest, PositionsManagerInternal { _createMarket(usdc, 0, 3_333); _createMarket(wNative, 0, 3_333); - _setBalances(address(this), type(uint256).max); - ERC20(dai).approve(address(_pool), type(uint256).max); ERC20(wbtc).approve(address(_pool), type(uint256).max); ERC20(usdc).approve(address(_pool), type(uint256).max); diff --git a/test/internal/TestInternalMatchingEngine.sol b/test/internal/TestInternalMatchingEngine.sol index d0ff978dc..e06da6c09 100644 --- a/test/internal/TestInternalMatchingEngine.sol +++ b/test/internal/TestInternalMatchingEngine.sol @@ -40,7 +40,7 @@ contract TestInternalMatchingEngine is InternalTest, MatchingEngine { uint256 poolIndex, uint256 p2pIndex, uint256 remaining - ) public { + ) public pure { poolBalance = bound(poolBalance, 0, type(uint96).max); p2pBalance = bound(p2pBalance, 0, type(uint96).max); poolIndex = bound(poolIndex, WadRayMath.RAY, WadRayMath.RAY * 1_000); @@ -59,6 +59,7 @@ contract TestInternalMatchingEngine is InternalTest, MatchingEngine { function testDemote(uint256 poolBalance, uint256 p2pBalance, uint256 poolIndex, uint256 p2pIndex, uint256 remaining) public + pure { poolBalance = bound(poolBalance, 0, type(uint96).max); p2pBalance = bound(p2pBalance, 0, type(uint96).max); diff --git a/test/internal/TestInternalMorphoInternal.sol b/test/internal/TestInternalMorphoInternal.sol index e6a8f18d8..8abf8812a 100644 --- a/test/internal/TestInternalMorphoInternal.sol +++ b/test/internal/TestInternalMorphoInternal.sol @@ -219,7 +219,7 @@ contract TestInternalMorphoInternal is InternalTest { uint256 scaledPoolBorrow, uint256 scaledP2PBorrow, uint256 scaledCollateral - ) internal { + ) internal view { assertEq(marketBalances.scaledPoolSupplyBalance(user), scaledPoolSupply, "scaledPoolSupply"); assertEq(marketBalances.scaledP2PSupplyBalance(user), scaledP2PSupply, "scaledP2PSupply"); assertEq(marketBalances.scaledPoolBorrowBalance(user), scaledPoolBorrow, "scaledPoolBorrow"); @@ -378,7 +378,7 @@ contract TestInternalMorphoInternal is InternalTest { assertEq(balance, onPool.rayMulUp(poolBorrowIndex) + inP2P.rayMulUp(p2pBorrowIndex)); } - function testAssetLiquidityData() public { + function testAssetLiquidityData() public view { DataTypes.EModeCategory memory eModeCategory = _pool.getEModeCategoryData(0); (uint256 poolLtv, uint256 poolLt,, uint256 poolDecimals,,) = _pool.getConfiguration(dai).getParams(); diff --git a/test/internal/TestInternalPositionsManagerInternal.sol b/test/internal/TestInternalPositionsManagerInternal.sol index 9433da58f..eda012e81 100644 --- a/test/internal/TestInternalPositionsManagerInternal.sol +++ b/test/internal/TestInternalPositionsManagerInternal.sol @@ -308,7 +308,7 @@ contract TestInternalPositionsManagerInternal is InternalTest, PositionsManagerI assertEq(closeFactor, Constants.DEFAULT_CLOSE_FACTOR); } - function testAddToPool(uint256 amount, uint256 onPool, uint256 poolIndex) public { + function testAddToPool(uint256 amount, uint256 onPool, uint256 poolIndex) public pure { amount = bound(amount, 0, MAX_AMOUNT); onPool = bound(onPool, 0, MAX_AMOUNT); poolIndex = bound(poolIndex, WadRayMath.RAY, WadRayMath.RAY * 10); @@ -318,7 +318,7 @@ contract TestInternalPositionsManagerInternal is InternalTest, PositionsManagerI assertEq(newOnPool, onPool + amount.rayDivDown(poolIndex)); } - function testSubFromPool(uint256 amount, uint256 onPool, uint256 poolIndex) public { + function testSubFromPool(uint256 amount, uint256 onPool, uint256 poolIndex) public pure { amount = bound(amount, 0, MAX_AMOUNT); onPool = bound(onPool, 0, MAX_AMOUNT); poolIndex = bound(poolIndex, WadRayMath.RAY, WadRayMath.RAY * 10); @@ -350,8 +350,8 @@ contract TestInternalPositionsManagerInternal is InternalTest, PositionsManagerI uint256 expectedToProcess = Math.min(amount, expectedLoops * 1 ether); uint256 expectedMaxLoopsLeft = maxLoops - expectedLoops; - assertEq(toProcess, amount - expectedToProcess, "toProcess"); - assertEq(toSupplyOrRepay, expectedToProcess, "amountLeft"); + assertApproxEqAbs(toProcess, amount - expectedToProcess, 20, "toProcess"); + assertApproxEqAbs(toSupplyOrRepay, expectedToProcess, 20, "amountLeft"); assertEq(maxLoopsLeft, expectedMaxLoopsLeft, "maxLoopsLeft"); } @@ -373,8 +373,8 @@ contract TestInternalPositionsManagerInternal is InternalTest, PositionsManagerI uint256 expectedToProcess = Math.min(amount, maxExpectedLoops * 1 ether); uint256 expectedMaxLoopsLeft = maxLoops - expectedLoops; - assertEq(toProcess, amount - expectedToProcess, "toProcess"); - assertEq(toRepayOrWithdraw, expectedToProcess, "amountLeft"); + assertApproxEqAbs(toProcess, amount - expectedToProcess, 20, "toProcess"); + assertApproxEqAbs(toRepayOrWithdraw, expectedToProcess, 20, "amountLeft"); assertEq(maxLoopsLeft, expectedMaxLoopsLeft, "maxLoopsLeft"); } diff --git a/test/internal/TestInternalPositionsManagerInternalCaps.sol b/test/internal/TestInternalPositionsManagerInternalCaps.sol index 7138e054c..15643cc1f 100644 --- a/test/internal/TestInternalPositionsManagerInternalCaps.sol +++ b/test/internal/TestInternalPositionsManagerInternalCaps.sol @@ -34,7 +34,7 @@ contract TestInternalPositionsManagerInternalCaps is InternalTest, PositionsMana _createMarket(usdc, 0, 3_333); _createMarket(wNative, 0, 3_333); - _setBalances(address(this), type(uint256).max); + _setBalances(address(this), MAX_AMOUNT); _pool.supplyToPool(dai, 100 ether, _pool.getReserveNormalizedIncome(dai)); _pool.supplyToPool(wbtc, 1e8, _pool.getReserveNormalizedIncome(wbtc)); @@ -69,7 +69,7 @@ contract TestInternalPositionsManagerInternalCaps is InternalTest, PositionsMana Types.Market storage market = _market[dai]; Types.Indexes256 memory indexes = _computeIndexes(dai); - uint256 poolDebt = ERC20(market.variableDebtToken).totalSupply() + ERC20(market.stableDebtToken).totalSupply(); + uint256 poolDebt = ERC20(market.variableDebtToken).totalSupply(); // Borrow cap should be exceeded. borrowCap = bound( diff --git a/test/invariant/TestInvariantAccessControl.sol b/test/invariant/TestInvariantAccessControl.sol index edc9b62e5..150b07935 100644 --- a/test/invariant/TestInvariantAccessControl.sol +++ b/test/invariant/TestInvariantAccessControl.sol @@ -50,7 +50,7 @@ contract TestInvariantAccessControl is InvariantTest { onBehalf = _randomSender(onBehalf); maxIterations = _boundMaxIterations(maxIterations); - _deal(market.underlying, msg.sender, amount); + deal(market.underlying, msg.sender, amount); vm.prank(msg.sender); // Cannot startPrank because `morpho.supply` may revert and not call stopPrank. ERC20(market.underlying).safeApprove(address(morpho), amount); @@ -64,7 +64,7 @@ contract TestInvariantAccessControl is InvariantTest { amount = _boundSupply(market, amount); onBehalf = _randomSender(onBehalf); - _deal(market.underlying, msg.sender, amount); + deal(market.underlying, msg.sender, amount); vm.prank(msg.sender); // Cannot startPrank because `morpho.supplyCollateral` may revert and not call stopPrank. ERC20(market.underlying).safeApprove(address(morpho), amount); @@ -115,7 +115,7 @@ contract TestInvariantAccessControl is InvariantTest { /* INVARIANTS */ - function invariantInitialized() public { + function invariantInitialized() public view { assertEq(initialized, 0, "initialized"); } @@ -131,13 +131,14 @@ contract TestInvariantAccessControl is InvariantTest { for (uint256 j; j < borrowableInEModeUnderlyings.length; ++j) { TestMarket storage market = testMarkets[borrowableInEModeUnderlyings[j]]; - uint256 borrowable = (liquidityData.borrowable * 1 ether * 10 ** market.decimals).percentAdd(5) // Inflate borrowable because of WBTC decimals precision. - / (market.price * 1 ether); - if (borrowable == 0 || borrowable > market.liquidity()) continue; + // Inflate borrowable because of WBTC decimals precision. + uint256 borrowableInUnderlying = + (liquidityData.borrowable * 10 ** market.decimals).divUp(market.price).percentAdd(10); + if (borrowableInUnderlying == 0 || borrowableInUnderlying > market.liquidity()) continue; vm.prank(sender); vm.expectRevert(Errors.UnauthorizedBorrow.selector); - morpho.borrow(market.underlying, borrowable, sender, sender, 0); + morpho.borrow(market.underlying, borrowableInUnderlying, sender, sender, 0); } } } diff --git a/test/invariant/TestInvariantMorpho.sol b/test/invariant/TestInvariantMorpho.sol index 92e0fbc97..b30f955c3 100644 --- a/test/invariant/TestInvariantMorpho.sol +++ b/test/invariant/TestInvariantMorpho.sol @@ -77,7 +77,7 @@ contract TestInvariantMorpho is InvariantTest { onBehalf = _randomSender(onBehalf); maxIterations = _boundMaxIterations(maxIterations); - _deal(market.underlying, msg.sender, amount); + deal(market.underlying, msg.sender, amount); vm.prank(msg.sender); // Cannot startPrank because `morpho.supply` may revert and not call stopPrank. ERC20(market.underlying).safeApprove(address(morpho), amount); @@ -106,7 +106,7 @@ contract TestInvariantMorpho is InvariantTest { amount = _boundNotZero(amount); onBehalf = _randomSender(onBehalf); - _deal(market.underlying, msg.sender, amount); + deal(market.underlying, msg.sender, amount); vm.prank(msg.sender); // Cannot startPrank because `morpho.repay` may revert and not call stopPrank. ERC20(market.underlying).safeApprove(address(morpho), amount); @@ -144,7 +144,7 @@ contract TestInvariantMorpho is InvariantTest { /* INVARIANTS */ - function invariantBalanceOf() public { + function invariantBalanceOf() public view { for (uint256 i; i < allUnderlyings.length; ++i) { address underlying = allUnderlyings[i]; Types.Market memory market = morpho.market(underlying); @@ -153,13 +153,13 @@ contract TestInvariantMorpho is InvariantTest { } } - function invariantHealthFactor() public { + function invariantHealthFactor() public view { (,,,,, uint256 healthFactor) = pool.getUserAccountData(address(morpho)); assertGt(healthFactor, Constants.DEFAULT_LIQUIDATION_MAX_HF, "healthFactor"); } - function invariantCollateralsAndBorrows() public { + function invariantCollateralsAndBorrows() public view { address[] memory senders = targetSenders(); for (uint256 i; i < senders.length; i++) { diff --git a/test/mocks/PoolAdminMock.sol b/test/mocks/PoolAdminMock.sol index 24b38c7c3..0ad82926a 100644 --- a/test/mocks/PoolAdminMock.sol +++ b/test/mocks/PoolAdminMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.0; -import {IPoolConfigurator} from "@aave-v3-core/interfaces/IPoolConfigurator.sol"; +import {IPoolConfigurator} from "test/helpers/IPoolConfigurator.sol"; contract PoolAdminMock { IPoolConfigurator internal immutable _POOL_CONFIGURATOR; @@ -23,23 +23,20 @@ contract PoolAdminMock { uint16 ltv, uint16 liquidationThreshold, uint16 liquidationBonus, - address priceSourceEMode, string memory label ) external { - _POOL_CONFIGURATOR.setEModeCategory( - eModeCategoryId, ltv, liquidationThreshold, liquidationBonus, priceSourceEMode, label - ); + _POOL_CONFIGURATOR.setEModeCategory(eModeCategoryId, ltv, liquidationThreshold, liquidationBonus, label); } - function setAssetEModeCategory(address asset, uint8 eModeCategoryId) external { - _POOL_CONFIGURATOR.setAssetEModeCategory(asset, eModeCategoryId); + function setAssetBorrowableInEMode(address asset, uint8 eModeCategoryId, bool borrowable) external { + _POOL_CONFIGURATOR.setAssetBorrowableInEMode(asset, eModeCategoryId, borrowable); } - function setReserveBorrowing(address asset, bool enabled) external { - _POOL_CONFIGURATOR.setReserveBorrowing(asset, enabled); + function setAssetCollateralInEMode(address asset, uint8 eModeCategoryId, bool collateral) external { + _POOL_CONFIGURATOR.setAssetCollateralInEMode(asset, eModeCategoryId, collateral); } - function setReserveStableRateBorrowing(address asset, bool enabled) external { - _POOL_CONFIGURATOR.setReserveStableRateBorrowing(asset, enabled); + function setReserveBorrowing(address asset, bool enabled) external { + _POOL_CONFIGURATOR.setReserveBorrowing(asset, enabled); } } diff --git a/test/unit/TestUnitInterestRatesLib.sol b/test/unit/TestUnitInterestRatesLib.sol index 5015d4a11..00ead2631 100644 --- a/test/unit/TestUnitInterestRatesLib.sol +++ b/test/unit/TestUnitInterestRatesLib.sol @@ -19,7 +19,7 @@ contract TestUnitInterestRatesLib is BaseTest { uint256 internal constant MAX_DELTA = 1e9 ether; uint256 internal constant MAX_TOTAL_P2P = 1e9 ether; - function testComputeP2PIndexes(Types.IndexesParams memory indexesParams) public { + function testComputeP2PIndexes(Types.IndexesParams memory indexesParams) public pure { indexesParams.lastSupplyIndexes.poolIndex = _boundIndex(indexesParams.lastSupplyIndexes.poolIndex); indexesParams.lastSupplyIndexes.p2pIndex = _boundIndex(indexesParams.lastSupplyIndexes.p2pIndex); indexesParams.lastBorrowIndexes.poolIndex = _boundIndex(indexesParams.lastBorrowIndexes.poolIndex); @@ -74,7 +74,7 @@ contract TestUnitInterestRatesLib is BaseTest { uint256 lastPoolBorrowIndex, uint256 p2pIndexCursor, uint256 reserveFactor - ) public { + ) public pure { newPoolSupplyIndex = _boundIndex(newPoolSupplyIndex); newPoolBorrowIndex = _boundIndex(newPoolBorrowIndex); lastPoolSupplyIndex = bound(lastPoolSupplyIndex, MIN_INDEX, newPoolSupplyIndex); @@ -139,7 +139,7 @@ contract TestUnitInterestRatesLib is BaseTest { uint256 lastPoolBorrowIndex, uint256 p2pIndexCursor, uint256 reserveFactor - ) public { + ) public pure { newPoolSupplyIndex = _boundIndex(newPoolSupplyIndex); newPoolBorrowIndex = _boundIndex(newPoolBorrowIndex); lastPoolSupplyIndex = bound(lastPoolSupplyIndex, MIN_INDEX, newPoolSupplyIndex); @@ -178,7 +178,7 @@ contract TestUnitInterestRatesLib is BaseTest { uint256 lastPoolIndex, uint256 lastP2PIndex, uint256 proportionIdle - ) public { + ) public pure { poolGrowthFactor = bound(poolGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); p2pGrowthFactor = bound(p2pGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); lastPoolIndex = _boundIndex(lastPoolIndex); @@ -210,7 +210,7 @@ contract TestUnitInterestRatesLib is BaseTest { uint256 lastP2PIndex, uint256 p2pAmount, uint256 proportionIdle - ) public { + ) public pure { poolGrowthFactor = bound(poolGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); p2pGrowthFactor = bound(p2pGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); lastPoolIndex = _boundIndex(lastPoolIndex); @@ -242,7 +242,7 @@ contract TestUnitInterestRatesLib is BaseTest { uint256 lastP2PIndex, uint256 delta, uint256 proportionIdle - ) public { + ) public pure { poolGrowthFactor = bound(poolGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); p2pGrowthFactor = bound(p2pGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); lastPoolIndex = bound(lastPoolIndex, MIN_INDEX, MAX_INDEX); @@ -275,7 +275,7 @@ contract TestUnitInterestRatesLib is BaseTest { uint256 delta, uint256 p2pAmount, uint256 proportionIdle - ) public { + ) public pure { poolGrowthFactor = bound(poolGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); p2pGrowthFactor = bound(p2pGrowthFactor, MIN_GROWTH_FACTOR, MAX_GROWTH_FACTOR); lastPoolIndex = _boundIndex(lastPoolIndex); diff --git a/test/unit/TestUnitMarketSideDeltaLib.sol b/test/unit/TestUnitMarketSideDeltaLib.sol index 50baeb673..7bacedcf5 100644 --- a/test/unit/TestUnitMarketSideDeltaLib.sol +++ b/test/unit/TestUnitMarketSideDeltaLib.sol @@ -15,7 +15,7 @@ contract TestUnitMarketSideDeltaLib is BaseTest { function _boundIndexes(Types.MarketSideIndexes256 memory indexes) internal - view + pure returns (Types.MarketSideIndexes256 memory) { indexes.p2pIndex = _boundIndex(indexes.p2pIndex);