diff --git a/.github/actions/ci-foundry-upgrade/action.yml b/.github/actions/ci-foundry-upgrade/action.yml index 77a419976..5aa038c3a 100644 --- a/.github/actions/ci-foundry-upgrade/action.yml +++ b/.github/actions/ci-foundry-upgrade/action.yml @@ -25,7 +25,7 @@ runs: shell: bash - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 with: version: nightly diff --git a/.github/actions/ci-foundry/action.yml b/.github/actions/ci-foundry/action.yml index dadb951b3..225171300 100644 --- a/.github/actions/ci-foundry/action.yml +++ b/.github/actions/ci-foundry/action.yml @@ -33,7 +33,7 @@ runs: shell: bash - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 with: version: nightly diff --git a/.github/workflows/ci-storage-check-aave-v2.yml b/.github/workflows/ci-storage-check-aave-v2.yml index 399a152bb..574809710 100644 --- a/.github/workflows/ci-storage-check-aave-v2.yml +++ b/.github/workflows/ci-storage-check-aave-v2.yml @@ -33,7 +33,7 @@ jobs: shell: bash - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 with: version: nightly diff --git a/.github/workflows/ci-storage-check-compound.yml b/.github/workflows/ci-storage-check-compound.yml index c4f73bcf1..27d6faab3 100644 --- a/.github/workflows/ci-storage-check-compound.yml +++ b/.github/workflows/ci-storage-check-compound.yml @@ -33,7 +33,7 @@ jobs: shell: bash - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 with: version: nightly diff --git a/.github/workflows/ci-storage-snapshot-check-aave-v2.yml b/.github/workflows/ci-storage-snapshot-check-aave-v2.yml index cc28caa3b..814c4d59a 100644 --- a/.github/workflows/ci-storage-snapshot-check-aave-v2.yml +++ b/.github/workflows/ci-storage-snapshot-check-aave-v2.yml @@ -32,7 +32,7 @@ jobs: run: yarn install --frozen-lockfile - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 with: version: nightly diff --git a/.github/workflows/ci-storage-snapshot-check-compound.yml b/.github/workflows/ci-storage-snapshot-check-compound.yml index ab1b5f839..8ec14546a 100644 --- a/.github/workflows/ci-storage-snapshot-check-compound.yml +++ b/.github/workflows/ci-storage-snapshot-check-compound.yml @@ -32,7 +32,7 @@ jobs: run: yarn install --frozen-lockfile - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 with: version: nightly diff --git a/src/aave-v2/InterestRatesManager.sol b/src/aave-v2/InterestRatesManager.sol index 48d2c1d4b..df90a3459 100644 --- a/src/aave-v2/InterestRatesManager.sol +++ b/src/aave-v2/InterestRatesManager.sol @@ -53,8 +53,6 @@ contract InterestRatesManager is IInterestRatesManager, MorphoStorage { function updateIndexes(address _poolToken) external { Types.PoolIndexes storage marketPoolIndexes = poolIndexes[_poolToken]; - if (block.timestamp == marketPoolIndexes.lastUpdateTimestamp) return; - Types.Market storage market = market[_poolToken]; (uint256 newPoolSupplyIndex, uint256 newPoolBorrowIndex) = _getPoolIndexes( market.underlyingToken diff --git a/src/compound/InterestRatesManager.sol b/src/compound/InterestRatesManager.sol index 164a2d430..5bd4ab35c 100644 --- a/src/compound/InterestRatesManager.sol +++ b/src/compound/InterestRatesManager.sol @@ -52,8 +52,6 @@ contract InterestRatesManager is IInterestRatesManager, MorphoStorage { function updateP2PIndexes(address _poolToken) external { Types.LastPoolIndexes storage poolIndexes = lastPoolIndexes[_poolToken]; - if (block.number == poolIndexes.lastUpdateBlockNumber) return; - Types.MarketParameters memory marketParams = marketParameters[_poolToken]; uint256 poolSupplyIndex = ICToken(_poolToken).exchangeRateCurrent(); diff --git a/src/compound/lens/IndexesLens.sol b/src/compound/lens/IndexesLens.sol index e4b5c915b..55aee1450 100644 --- a/src/compound/lens/IndexesLens.sol +++ b/src/compound/lens/IndexesLens.sol @@ -179,7 +179,7 @@ abstract contract IndexesLens is LensStorage { { marketParameters = morpho.marketParameters(_poolToken); - if (!_updated || block.number == _lastPoolIndexes.lastUpdateBlockNumber) { + if (!_updated) { return ( morpho.p2pSupplyIndex(_poolToken), morpho.p2pBorrowIndex(_poolToken), diff --git a/test/aave-v2/TestLens.t.sol b/test/aave-v2/TestLens.t.sol index ac901243b..26f97a2bc 100644 --- a/test/aave-v2/TestLens.t.sol +++ b/test/aave-v2/TestLens.t.sol @@ -1658,4 +1658,40 @@ contract TestLens is TestSetup { morpho.setIsLiquidateBorrowPaused(aDai, true); assertTrue(lens.getMarketPauseStatus(aDai).isLiquidateBorrowPaused); } + + function testPoolIndexGrowthInsideBlock() public { + supplier1.approve(dai, type(uint256).max); + supplier1.supply(aDai, 10 ether); + + uint256 poolSupplyIndexBefore = lens.getIndexes(aDai).poolSupplyIndex; + + FlashLoan flashLoan = new FlashLoan(pool); + vm.prank(address(supplier2)); + ERC20(dai).transfer(address(flashLoan), 10_000 ether); // To pay the premium. + flashLoan.callFlashLoan(dai, 10_000 ether); + + uint256 poolSupplyIndexAfter = lens.getIndexes(aDai).poolSupplyIndex; + + assertGt(poolSupplyIndexAfter, poolSupplyIndexBefore); + } + + function testP2PIndexGrowthInsideBlock() public { + borrower1.approve(dai, type(uint256).max); + borrower1.supply(aDai, 10 ether); + borrower1.borrow(aDai, 5 ether); + setDefaultMaxGasForMatchingHelper(3e6, 3e6, 3e6, 0); + // Create delta. + borrower1.repay(aDai, type(uint256).max); + + uint256 p2pSupplyIndexBefore = lens.getCurrentP2PSupplyIndex(aDai); + + FlashLoan flashLoan = new FlashLoan(pool); + vm.prank(address(supplier2)); + ERC20(dai).transfer(address(flashLoan), 10_000 ether); // To pay the premium. + flashLoan.callFlashLoan(dai, 10_000 ether); + + uint256 p2pSupplyIndexAfter = lens.getCurrentP2PSupplyIndex(aDai); + + assertGt(p2pSupplyIndexAfter, p2pSupplyIndexBefore); + } } diff --git a/test/aave-v2/TestSupply.t.sol b/test/aave-v2/TestSupply.t.sol index 6d8d21f4c..1c46c433c 100644 --- a/test/aave-v2/TestSupply.t.sol +++ b/test/aave-v2/TestSupply.t.sol @@ -257,7 +257,7 @@ contract TestSupply is TestSetup { FlashLoan flashLoan = new FlashLoan(pool); vm.prank(address(supplier2)); - ERC20(dai).transfer(address(flashLoan), 10_000 ether); // to pay the premium. + ERC20(dai).transfer(address(flashLoan), 10_000 ether); // To pay the premium. flashLoan.callFlashLoan(dai, flashLoanAmount); vm.warp(block.timestamp + 1); @@ -277,6 +277,46 @@ contract TestSupply is TestSetup { assertGt(gasUsed2, gasUsed1 + 1e4); } + function testPoolIndexGrowthInsideBlock() public { + supplier1.approve(dai, type(uint256).max); + supplier1.supply(aDai, 1); + + (, uint256 poolSupplyIndexCachedBefore, ) = morpho.poolIndexes(aDai); + + FlashLoan flashLoan = new FlashLoan(pool); + vm.prank(address(supplier2)); + ERC20(dai).transfer(address(flashLoan), 10_000 ether); // To pay the premium. + flashLoan.callFlashLoan(dai, 10_000 ether); + + supplier1.supply(aDai, 1); + + (, uint256 poolSupplyIndexCachedAfter, ) = morpho.poolIndexes(aDai); + + assertGt(poolSupplyIndexCachedAfter, poolSupplyIndexCachedBefore); + } + + function testP2PIndexGrowthInsideBlock() public { + borrower1.approve(dai, type(uint256).max); + borrower1.supply(aDai, 10 ether); + borrower1.borrow(aDai, 5 ether); + setDefaultMaxGasForMatchingHelper(3e6, 3e6, 3e6, 0); + // Create delta. + borrower1.repay(aDai, type(uint256).max); + + uint256 p2pSupplyIndexBefore = morpho.p2pSupplyIndex(aDai); + + FlashLoan flashLoan = new FlashLoan(pool); + vm.prank(address(supplier2)); + ERC20(dai).transfer(address(flashLoan), 10_000 ether); // To pay the premium. + flashLoan.callFlashLoan(dai, 10_000 ether); + + borrower1.supply(aDai, 1); + + uint256 p2pSupplyIndexAfter = morpho.p2pSupplyIndex(aDai); + + assertGt(p2pSupplyIndexAfter, p2pSupplyIndexBefore); + } + /// @dev Helper for gas usage test function _getSupplyGasUsage(uint256 amount, uint256 maxGas) internal returns (uint256 gasUsed) { // 2 * NMAX borrowers borrow amount diff --git a/test/compound/TestLens.t.sol b/test/compound/TestLens.t.sol index c08944941..ddce0d8cd 100644 --- a/test/compound/TestLens.t.sol +++ b/test/compound/TestLens.t.sol @@ -74,10 +74,9 @@ contract TestLens is TestSetup { uint256 amount = 10_000 ether; uint256 toBorrow = amount / 2; - borrower1.approve(dai, amount); + borrower1.approve(dai, type(uint256).max); indexes.index1 = ICToken(cDai).exchangeRateCurrent(); borrower1.supply(cDai, amount); - uint256 p2pBorrowIndex = morpho.p2pBorrowIndex(cDai); borrower1.borrow(cDai, toBorrow); indexes.index2 = ICToken(cDai).exchangeRateCurrent(); @@ -94,6 +93,9 @@ contract TestLens is TestSetup { uint256 total; + // To update p2p indexes on Morpho (they can change inside of a block because the poolSupplyIndex can change due to rounding errors). + borrower1.supply(cDai, 1); + uint256 p2pBorrowIndex = morpho.p2pBorrowIndex(cDai); { uint256 onPool = amount.div(indexes.index1); uint256 matchedInP2P = toBorrow.div(morpho.p2pSupplyIndex(cDai)); @@ -1719,4 +1721,40 @@ contract TestLens is TestSetup { morpho.setIsLiquidateBorrowPaused(cDai, true); assertTrue(lens.getMarketPauseStatus(cDai).isLiquidateBorrowPaused); } + + function testPoolIndexGrowthInsideBlock() public { + supplier1.approve(dai, type(uint256).max); + supplier1.supply(cDai, 1 ether); + + uint256 poolBorrowIndexBefore = lens.getIndexes(cDai, true).poolSupplyIndex; + + vm.prank(address(supplier1)); + ERC20(dai).transfer(cDai, 10_000 ether); + + supplier1.supply(cDai, 1); + + uint256 poolSupplyIndexAfter = lens.getIndexes(cDai, true).poolSupplyIndex; + + assertGt(poolSupplyIndexAfter, poolBorrowIndexBefore); + } + + function testP2PIndexGrowthInsideBlock() public { + borrower1.approve(dai, type(uint256).max); + borrower1.supply(cDai, 1 ether); + borrower1.borrow(cDai, 0.5 ether); + setDefaultMaxGasForMatchingHelper(0, 0, 0, 0); + // Bypass the borrow repay in the same block by overwritting the storage slot lastBorrowBlock[borrower1]. + hevm.store(address(morpho), keccak256(abi.encode(address(borrower1), 178)), 0); + // Create delta. + borrower1.repay(cDai, type(uint256).max); + + uint256 p2pSupplyIndexBefore = lens.getCurrentP2PSupplyIndex(cDai); + + vm.prank(address(supplier1)); + ERC20(dai).transfer(cDai, 10_000 ether); + + uint256 p2pSupplyIndexAfter = lens.getCurrentP2PSupplyIndex(cDai); + + assertGt(p2pSupplyIndexAfter, p2pSupplyIndexBefore); + } } diff --git a/test/compound/TestSupply.t.sol b/test/compound/TestSupply.t.sol index 25c192034..5f6beaf16 100644 --- a/test/compound/TestSupply.t.sol +++ b/test/compound/TestSupply.t.sol @@ -321,6 +321,44 @@ contract TestSupply is TestSetup { assertGt(gasUsed2, gasUsed1 + 5e4); } + function testPoolIndexGrowthInsideBlock() public { + supplier1.approve(dai, type(uint256).max); + supplier1.supply(cDai, 1 ether); + + (, uint256 poolSupplyIndexCachedBefore, ) = morpho.lastPoolIndexes(cDai); + + vm.prank(address(supplier1)); + ERC20(dai).transfer(cDai, 10_000 ether); + + supplier1.supply(cDai, 1); + + (, uint256 poolSupplyIndexCachedAfter, ) = morpho.lastPoolIndexes(cDai); + + assertGt(poolSupplyIndexCachedAfter, poolSupplyIndexCachedBefore); + } + + function testP2PIndexGrowthInsideBlock() public { + borrower1.approve(dai, type(uint256).max); + borrower1.supply(cDai, 1 ether); + borrower1.borrow(cDai, 0.5 ether); + setDefaultMaxGasForMatchingHelper(0, 0, 0, 0); + // Bypass the borrow repay in the same block by overwritting the storage slot lastBorrowBlock[borrower1]. + hevm.store(address(morpho), keccak256(abi.encode(address(borrower1), 178)), 0); + // Create delta. + borrower1.repay(cDai, type(uint256).max); + + uint256 p2pSupplyIndexBefore = morpho.p2pSupplyIndex(cDai); + + vm.prank(address(supplier1)); + ERC20(dai).transfer(cDai, 10_000 ether); + + borrower1.supply(cDai, 1); + + uint256 p2pSupplyIndexAfter = morpho.p2pSupplyIndex(cDai); + + assertGt(p2pSupplyIndexAfter, p2pSupplyIndexBefore); + } + /// @dev Helper for gas usage test function _getSupplyGasUsage(uint256 amount, uint256 maxGas) internal returns (uint256 gasUsed) { // 2 * NMAX borrowers borrow amount