diff --git a/src/core/EulerAggregationVault.sol b/src/core/EulerAggregationVault.sol index d5988d47..a2d42395 100644 --- a/src/core/EulerAggregationVault.sol +++ b/src/core/EulerAggregationVault.sol @@ -50,8 +50,10 @@ contract EulerAggregationVault is bytes32 public constant AGGREGATION_LAYER_MANAGER = keccak256("AGGREGATION_LAYER_MANAGER"); bytes32 public constant AGGREGATION_LAYER_MANAGER_ADMIN = keccak256("AGGREGATION_LAYER_MANAGER_ADMIN"); - /// @dev interest rate smearing period + /// @dev Interest rate smearing period uint256 public constant INTEREST_SMEAR = 2 weeks; + /// @dev Minimum amount of shares to exist for gulp to be enabled + uint256 public constant MIN_SHARES_FOR_GULP = 1e7; constructor(address _rewardsModule, address _hooksModule, address _feeModule, address _allocationPointsModule) Dispatch(_rewardsModule, _hooksModule, _feeModule, _allocationPointsModule) @@ -475,7 +477,8 @@ contract EulerAggregationVault is AggregationVaultStorage storage $ = StorageLib._getAggregationVaultStorage(); - if (totalSupply() == 0) return; + // Do not gulp if total supply is too low + if (totalSupply() < MIN_SHARES_FOR_GULP) return; uint256 toGulp = totalAssetsAllocatable() - $.totalAssetsDeposited - $.interestLeft; if (toGulp == 0) return; diff --git a/test/fuzz/GulpFuzzTest.t.sol b/test/fuzz/GulpFuzzTest.t.sol new file mode 100644 index 00000000..67756c56 --- /dev/null +++ b/test/fuzz/GulpFuzzTest.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import { + EulerAggregationVaultBase, + EulerAggregationVault, + IEulerAggregationVault +} from "../common/EulerAggregationVaultBase.t.sol"; + +contract GulpFuzzTest is EulerAggregationVaultBase { + function setUp() public virtual override { + super.setUp(); + + uint256 initialStrategyAllocationPoints = 500e18; + _addStrategy(manager, address(eTST), initialStrategyAllocationPoints); + } + + function testFuzz_interestAccrued_under_uint168( + uint256 _interestAmount, + uint256 _depositAmount, + uint256 _timePassed + ) public { + _depositAmount = bound(_depositAmount, 0, type(uint112).max); + // this makes sure that the mint won't cause overflow in token accounting + _interestAmount = bound(_interestAmount, 0, type(uint112).max - _depositAmount); + _timePassed = bound(_timePassed, block.timestamp, type(uint40).max); + + assetTST.mint(user1, _depositAmount); + vm.startPrank(user1); + assetTST.approve(address(eulerAggregationVault), _depositAmount); + eulerAggregationVault.deposit(_depositAmount, user1); + vm.stopPrank(); + + assetTST.mint(address(eulerAggregationVault), _interestAmount); + eulerAggregationVault.gulp(); + + vm.warp(_timePassed); + uint256 interestAccrued = eulerAggregationVault.interestAccrued(); + + assertLe(interestAccrued, type(uint168).max); + } + + // this tests shows that when you have a very small deposit and a very large interestAmount minted to the contract + function testFuzz_gulp_under_uint168(uint256 _interestAmount, uint256 _depositAmount) public { + _depositAmount = bound(_depositAmount, 1e7, type(uint112).max); + _interestAmount = bound(_interestAmount, 0, type(uint256).max - _depositAmount); // this makes sure that the mint won't cause overflow + + assetTST.mint(address(eulerAggregationVault), _interestAmount); + + assetTST.mint(user1, _depositAmount); + vm.startPrank(user1); + assetTST.approve(address(eulerAggregationVault), _depositAmount); + eulerAggregationVault.deposit(_depositAmount, user1); + vm.stopPrank(); + + eulerAggregationVault.gulp(); + + EulerAggregationVault.AggregationVaultSavingRate memory aggregationVaultSavingRate = + eulerAggregationVault.getAggregationVaultSavingRate(); + + if (_interestAmount <= type(uint168).max) { + assertEq(aggregationVaultSavingRate.interestLeft, _interestAmount); + } else { + assertEq(aggregationVaultSavingRate.interestLeft, type(uint168).max); + } + } +} diff --git a/test/invariant/EulerAggregationLayerInvariants.t.sol b/test/invariant/EulerAggregationLayerInvariants.t.sol index 622bdcac..e907ae24 100644 --- a/test/invariant/EulerAggregationLayerInvariants.t.sol +++ b/test/invariant/EulerAggregationLayerInvariants.t.sol @@ -124,6 +124,13 @@ contract EulerAggregationVaultInvariants is EulerAggregationVaultBase { } } + function invariant_interestLeft() public view { + EulerAggregationVault.AggregationVaultSavingRate memory aggregationVaultSavingRate = + eulerAggregationVault.getAggregationVaultSavingRate(); + uint256 accruedInterest = eulerAggregationVault.interestAccrued(); + assertGe(aggregationVaultSavingRate.interestLeft, accruedInterest); + } + function _deployOtherStrategies() private { eTSTsecond = IEVault( factory.createProxy(address(0), true, abi.encodePacked(address(assetTST), address(oracle), unitOfAccount))