diff --git a/lib/solidity-utils b/lib/solidity-utils index a842c363..b3c5def7 160000 --- a/lib/solidity-utils +++ b/lib/solidity-utils @@ -1 +1 @@ -Subproject commit a842c36308e76b8202a46962a6c2d59daceb640a +Subproject commit b3c5def7c72b1fc9a1d20e7f2b67feb55e652a06 diff --git a/src/contracts/extensions/static-a-token/ERC20AaveLMUpgradeable.sol b/src/contracts/extensions/static-a-token/ERC20AaveLMUpgradeable.sol index 71d52c59..d56b3899 100644 --- a/src/contracts/extensions/static-a-token/ERC20AaveLMUpgradeable.sol +++ b/src/contracts/extensions/static-a-token/ERC20AaveLMUpgradeable.sol @@ -22,7 +22,7 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM { struct ERC20AaveLMStorage { address _referenceAsset; // a/v token to track rewards on INCENTIVES_CONTROLLER address[] _rewardTokens; - mapping(address user => RewardIndexCache cache) _startIndex; + mapping(address reward => RewardIndexCache cache) _startIndex; mapping(address user => mapping(address reward => UserRewardsData cache)) _userRewardsData; } @@ -39,6 +39,9 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM { IRewardsController public immutable INCENTIVES_CONTROLLER; constructor(IRewardsController rewardsController) { + if (address(rewardsController) == address(0)) { + revert ZeroIncentivesControllerIsForbidden(); + } INCENTIVES_CONTROLLER = rewardsController; } @@ -195,11 +198,11 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM { } /** - * @notice Compute the pending in WAD. Pending is the amount to add (not yet unclaimed) rewards in WAD. + * @notice Compute the pending in asset decimals. Pending is the amount to add (not yet unclaimed) rewards in asset decimals. * @param balance The balance of the user * @param rewardsIndexOnLastInteraction The index which was on the last interaction of the user * @param currentRewardsIndex The current rewards index in the system - * @return The amount of pending rewards in WAD + * @return The amount of pending rewards in asset decimals */ function _getPendingRewards( uint256 balance, @@ -216,7 +219,7 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM { * @notice Compute the claimable rewards for a user * @param user The address of the user * @param reward The address of the reward - * @param balance The balance of the user in WAD + * @param balance The balance of the user in asset decimals * @param currentRewardsIndex The current rewards index * @return The total rewards that can be claimed by the user (if `fresh` flag true, after updating rewards) */ @@ -299,7 +302,7 @@ abstract contract ERC20AaveLMUpgradeable is ERC20Upgradeable, IERC20AaveLM { ERC20AaveLMStorage storage $ = _getERC20AaveLMStorage(); $._rewardTokens.push(reward); - $._startIndex[reward] = RewardIndexCache(true, startIndex.toUint240()); + $._startIndex[reward] = RewardIndexCache(true, startIndex.toUint248()); emit RewardTokenRegistered(reward, startIndex); } diff --git a/src/contracts/extensions/static-a-token/ERC4626StataTokenUpgradeable.sol b/src/contracts/extensions/static-a-token/ERC4626StataTokenUpgradeable.sol index f61784a5..5abe3b0d 100644 --- a/src/contracts/extensions/static-a-token/ERC4626StataTokenUpgradeable.sol +++ b/src/contracts/extensions/static-a-token/ERC4626StataTokenUpgradeable.sol @@ -75,6 +75,12 @@ abstract contract ERC4626StataTokenUpgradeable is ERC4626Upgradeable, IERC4626St ///@inheritdoc IERC4626StataToken function depositATokens(uint256 assets, address receiver) external returns (uint256) { + // because aToken is rebasable, we allow user to specify more then he has to compensate growth during the tx mining + uint256 actualUserBalance = IERC20(aToken()).balanceOf(_msgSender()); + if (assets > actualUserBalance) { + assets = actualUserBalance; + } + uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares, false); @@ -89,14 +95,27 @@ abstract contract ERC4626StataTokenUpgradeable is ERC4626Upgradeable, IERC4626St SignatureParams memory sig, bool depositToAave ) external returns (uint256) { - IERC20Permit assetToDeposit = IERC20Permit( - depositToAave ? asset() : address(_getERC4626StataTokenStorage()._aToken) - ); + address assetToDeposit = depositToAave ? asset() : aToken(); try - assetToDeposit.permit(_msgSender(), address(this), assets, deadline, sig.v, sig.r, sig.s) + IERC20Permit(assetToDeposit).permit( + _msgSender(), + address(this), + assets, + deadline, + sig.v, + sig.r, + sig.s + ) {} catch {} + // because aToken is rebasable, we allow user to specify more then he has to compensate growth during the tx mining + // to make it consistent, we keep the same behaviour for the normal underlying too + uint256 actualUserBalance = IERC20(assetToDeposit).balanceOf(_msgSender()); + if (assets > actualUserBalance) { + assets = actualUserBalance; + } + uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares, depositToAave); return shares; @@ -180,7 +199,7 @@ abstract contract ERC4626StataTokenUpgradeable is ERC4626Upgradeable, IERC4626St // return remaining supply cap margin uint256 currentSupply = (IAToken(reserveData.aTokenAddress).scaledTotalSupply() + reserveData.accruedToTreasury).mulDiv(_rate(), RAY, Math.Rounding.Ceil); - return currentSupply > supplyCap ? 0 : supplyCap - currentSupply; + return currentSupply >= supplyCap ? 0 : supplyCap - currentSupply; } ///@inheritdoc IERC4626StataToken diff --git a/src/contracts/extensions/static-a-token/README.md b/src/contracts/extensions/static-a-token/README.md index 8b2668e6..68463073 100644 --- a/src/contracts/extensions/static-a-token/README.md +++ b/src/contracts/extensions/static-a-token/README.md @@ -45,7 +45,7 @@ For this project, the security procedures applied/being finished are: The `StaticATokenLM`(v1) was based on solmate. To allow more flexibility the new `StataTokenV2`(v2) is based on [openzeppelin-contracts-upgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable) which relies on [`ERC-7201`](https://eips.ethereum.org/EIPS/eip-7201) which isolates storage per contract. -The implementation is seperated in two ERC20 extentions and one actual "merger" contract stitching functionality together. +The implementation is separated in two ERC20 extensions and one actual "merger" contract stitching functionality together. 1. `ERC20AaveLM` is an abstract contract implementing the forwarding of liquidity mining from an underlying AaveERC20 - an ERC20 implementing `scaled` functions - to holders of a wrapper contract. The abstract contract is following `ERC-7201` and acts as erc20 extension. @@ -70,13 +70,13 @@ To account for that specific use-case a dedicated `depositWithPermit` was added. ### Direct AToken Interaction In v1 deposit was overleaded to allow underlying & aToken deposits. -While this appraoch was fine it seemed unclean and caused some confusion with integrators. +While this approach was fine it seemed unclean and caused some confusion with integrators. Therefore v2 introduces dedicated `depositATokens` and `redeemATokens` methods. -#### PermissionlessRescuable +#### Rescuable -[PermissionlessRescuable](https://github.com/bgd-labs/solidity-utils/blob/main/src/contracts/utils/PermissionlessRescuable.sol) has been applied to -the `StataTokenV2` which will allow the anyone to rescue surplus tokens on the contract to the treasury. +[Rescuable](https://github.com/bgd-labs/solidity-utils/blob/main/src/contracts/utils/Rescuable.sol) has been applied to +the `StataTokenV2` which will allow the aclAdmin to rescue surplus tokens on the contract to the treasury. #### Pausability diff --git a/src/contracts/extensions/static-a-token/interfaces/IERC20AaveLM.sol b/src/contracts/extensions/static-a-token/interfaces/IERC20AaveLM.sol index 79eb163c..656e96f9 100644 --- a/src/contracts/extensions/static-a-token/interfaces/IERC20AaveLM.sol +++ b/src/contracts/extensions/static-a-token/interfaces/IERC20AaveLM.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.10; interface IERC20AaveLM { struct UserRewardsData { - uint128 rewardsIndexOnLastInteraction; // (in RAYs) - uint128 unclaimedRewards; // (in RAYs) + uint128 rewardsIndexOnLastInteraction; + uint128 unclaimedRewards; } struct RewardIndexCache { @@ -12,6 +12,7 @@ interface IERC20AaveLM { uint248 lastUpdatedIndex; } + error ZeroIncentivesControllerIsForbidden(); error InvalidClaimer(address claimer); error RewardNotInitialized(address reward); @@ -58,18 +59,18 @@ interface IERC20AaveLM { function getTotalClaimableRewards(address reward) external view returns (uint256); /** - * @notice Get the total claimable rewards for a user in WAD + * @notice Get the total claimable rewards for a user in asset decimals * @param user The address of the user * @param reward The reward to claim - * @return uint256 The claimable amount of rewards in WAD + * @return uint256 The claimable amount of rewards in asset decimals */ function getClaimableRewards(address user, address reward) external view returns (uint256); /** - * @notice The unclaimed rewards for a user in WAD + * @notice The unclaimed rewards for a user in asset decimals * @param user The address of the user * @param reward The reward to claim - * @return uint256 The unclaimed amount of rewards in WAD + * @return uint256 The unclaimed amount of rewards in asset decimals */ function getUnclaimedRewards(address user, address reward) external view returns (uint256); diff --git a/tests/extensions/static-a-token/ERC20AaveLMUpgradable.t.sol b/tests/extensions/static-a-token/ERC20AaveLMUpgradable.t.sol index bcc552f8..aa65ce7e 100644 --- a/tests/extensions/static-a-token/ERC20AaveLMUpgradable.t.sol +++ b/tests/extensions/static-a-token/ERC20AaveLMUpgradable.t.sol @@ -93,6 +93,11 @@ contract ERC20AaveLMUpgradableTest is TestnetProcedures { ); } + function test_zeroIncentivesController() external { + vm.expectRevert(IERC20AaveLM.ZeroIncentivesControllerIsForbidden.selector); + new MockERC20AaveLMUpgradeable(IRewardsController(address(0))); + } + function test_noRewardsInitialized() external { vm.expectRevert( abi.encodeWithSelector(IERC20AaveLM.RewardNotInitialized.selector, rewardToken) diff --git a/tests/extensions/static-a-token/ERC4626StataTokenUpgradeable.t.sol b/tests/extensions/static-a-token/ERC4626StataTokenUpgradeable.t.sol index fb0d0b3a..9b7be78c 100644 --- a/tests/extensions/static-a-token/ERC4626StataTokenUpgradeable.t.sol +++ b/tests/extensions/static-a-token/ERC4626StataTokenUpgradeable.t.sol @@ -65,57 +65,70 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { } // ### DEPOSIT TESTS ### - function test_depositATokens(uint128 assets, address receiver) public { + function test_depositATokens( + uint128 unboundedUnderlyingBalance, + uint256 unboundedAmountToDeposit, + address receiver + ) public { _validateReceiver(receiver); - TestEnv memory env = _setupTestEnv(assets); - _fundAToken(env.amount, user); + TestEnv memory env = _setupTestEnv(unboundedUnderlyingBalance, unboundedAmountToDeposit); + _fundAToken(env.underlyingBalance, user); vm.startPrank(user); - IERC20(aToken).approve(address(erc4626Upgradeable), env.amount); - uint256 shares = erc4626Upgradeable.depositATokens(env.amount, receiver); + IERC20(aToken).approve(address(erc4626Upgradeable), env.amountToDeposit); + uint256 shares = erc4626Upgradeable.depositATokens(env.amountToDeposit, receiver); vm.stopPrank(); assertEq(erc4626Upgradeable.balanceOf(receiver), shares); - assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.amount); - assertEq(IERC20(aToken).balanceOf(user), 0); - assertEq(erc4626Upgradeable.totalAssets(), env.amount); + assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.actualAmountToDeposit); + assertEq(IERC20(aToken).balanceOf(user), env.underlyingBalance - env.actualAmountToDeposit); + assertEq(erc4626Upgradeable.totalAssets(), env.actualAmountToDeposit); } function test_depositATokens_self() external { - test_depositATokens(1 ether, user); + test_depositATokens(1 ether, 1 ether, user); } - function test_deposit_shouldRevert_insufficientAllowance(uint128 assets) external { - TestEnv memory env = _setupTestEnv(assets); - _fundAToken(env.amount, user); + function test_deposit_shouldRevert_insufficientAllowance(uint128 unboundedAssets) external { + TestEnv memory env = _setupTestEnv(unboundedAssets); + _fundAToken(env.underlyingBalance, user); vm.expectRevert(); // underflows vm.prank(user); - erc4626Upgradeable.depositATokens(env.amount, user); + erc4626Upgradeable.depositATokens(env.underlyingBalance, user); } - function test_depositWithPermit_shouldRevert_emptyPermit_noPreApproval(uint128 assets) external { - TestEnv memory env = _setupTestEnv(assets); - _fundAToken(env.amount, user); + function test_depositWithPermit_shouldRevert_emptyPermit_noPreApproval( + uint128 unboundedAssets + ) external { + TestEnv memory env = _setupTestEnv(unboundedAssets); + _fundAToken(env.underlyingBalance, user); IERC4626StataToken.SignatureParams memory sig; vm.expectRevert(); // will underflow vm.prank(user); - erc4626Upgradeable.depositWithPermit(env.amount, user, block.timestamp + 1000, sig, false); + erc4626Upgradeable.depositWithPermit( + env.underlyingBalance, + user, + block.timestamp + 1000, + sig, + false + ); } function test_depositWithPermit_emptyPermit_underlying_preApproval( - uint128 assets, + uint128 unboundedAssets, + uint256 unboundedAmountToDeposit, address receiver ) external { _validateReceiver(receiver); - TestEnv memory env = _setupTestEnv(assets); - _fundUnderlying(env.amount, user); + TestEnv memory env = _setupTestEnv(unboundedAssets, unboundedAmountToDeposit); + _fundUnderlying(env.underlyingBalance, user); IERC4626StataToken.SignatureParams memory sig; vm.prank(user); - IERC20(underlying).approve(address(erc4626Upgradeable), env.amount); + IERC20(underlying).approve(address(erc4626Upgradeable), env.amountToDeposit); vm.prank(user); uint256 shares = erc4626Upgradeable.depositWithPermit( - env.amount, + env.amountToDeposit, receiver, block.timestamp + 1000, sig, @@ -123,23 +136,25 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { ); assertEq(erc4626Upgradeable.balanceOf(receiver), shares); - assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.amount); + assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.actualAmountToDeposit); assertEq(IERC20(aToken).balanceOf(user), 0); + assertEq(IERC20(underlying).balanceOf(user), env.underlyingBalance - env.actualAmountToDeposit); } function test_depositWithPermit_emptyPermit_aToken_preApproval( - uint128 assets, + uint128 unboundedAssets, + uint256 unboundedAmountToDeposit, address receiver ) external { _validateReceiver(receiver); - TestEnv memory env = _setupTestEnv(assets); - _fundAToken(env.amount, user); + TestEnv memory env = _setupTestEnv(unboundedAssets, unboundedAmountToDeposit); + _fundAToken(env.underlyingBalance, user); IERC4626StataToken.SignatureParams memory sig; vm.prank(user); - IERC20(aToken).approve(address(erc4626Upgradeable), env.amount); + IERC20(aToken).approve(address(erc4626Upgradeable), env.amountToDeposit); vm.prank(user); uint256 shares = erc4626Upgradeable.depositWithPermit( - env.amount, + env.amountToDeposit, receiver, block.timestamp + 1000, sig, @@ -147,19 +162,23 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { ); assertEq(erc4626Upgradeable.balanceOf(receiver), shares); - assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.amount); - assertEq(IERC20(aToken).balanceOf(user), 0); + assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.actualAmountToDeposit); + assertEq(IERC20(aToken).balanceOf(user), env.underlyingBalance - env.actualAmountToDeposit); } - function test_depositWithPermit_underlying(uint128 assets, address receiver) external { + function test_depositWithPermit_underlying( + uint128 unboundedAssets, + uint256 unboundedAmountToDeposit, + address receiver + ) external { _validateReceiver(receiver); - TestEnv memory env = _setupTestEnv(assets); - _fundUnderlying(env.amount, user); + TestEnv memory env = _setupTestEnv(unboundedAssets, unboundedAmountToDeposit); + _fundUnderlying(env.underlyingBalance, user); SigUtils.Permit memory permit = SigUtils.Permit({ owner: user, spender: address(erc4626Upgradeable), - value: env.amount, + value: env.amountToDeposit, nonce: IERC20Permit(underlying).nonces(user), deadline: block.timestamp + 100 }); @@ -173,7 +192,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { IERC4626StataToken.SignatureParams memory sig = IERC4626StataToken.SignatureParams(v, r, s); vm.prank(user); uint256 shares = erc4626Upgradeable.depositWithPermit( - env.amount, + env.amountToDeposit, receiver, permit.deadline, sig, @@ -181,19 +200,23 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { ); assertEq(erc4626Upgradeable.balanceOf(receiver), shares); - assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.amount); - assertEq(IERC20(underlying).balanceOf(user), 0); + assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.actualAmountToDeposit); + assertEq(IERC20(underlying).balanceOf(user), env.underlyingBalance - env.actualAmountToDeposit); } - function test_depositWithPermit_aToken(uint128 assets, address receiver) external { + function test_depositWithPermit_aToken( + uint128 unboundedAssets, + uint256 unboundedAmountToDeposit, + address receiver + ) external { _validateReceiver(receiver); - TestEnv memory env = _setupTestEnv(assets); - _fundAToken(env.amount, user); + TestEnv memory env = _setupTestEnv(unboundedAssets, unboundedAmountToDeposit); + _fundAToken(env.underlyingBalance, user); SigUtils.Permit memory permit = SigUtils.Permit({ owner: user, spender: address(erc4626Upgradeable), - value: env.amount, + value: env.amountToDeposit, nonce: IERC20Permit(aToken).nonces(user), deadline: block.timestamp + 100 }); @@ -207,7 +230,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { IERC4626StataToken.SignatureParams memory sig = IERC4626StataToken.SignatureParams(v, r, s); vm.prank(user); uint256 shares = erc4626Upgradeable.depositWithPermit( - env.amount, + env.amountToDeposit, receiver, permit.deadline, sig, @@ -215,15 +238,15 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { ); assertEq(erc4626Upgradeable.balanceOf(receiver), shares); - assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.amount); - assertEq(IERC20(aToken).balanceOf(user), 0); + assertEq(IERC20(aToken).balanceOf(address(erc4626Upgradeable)), env.actualAmountToDeposit); + assertEq(IERC20(aToken).balanceOf(user), env.underlyingBalance - env.actualAmountToDeposit); } // ### REDEEM TESTS ### function test_redeemATokens(uint256 assets, address receiver) public { _validateReceiver(receiver); TestEnv memory env = _setupTestEnv(assets); - uint256 shares = _fund4626(env.amount, user); + uint256 shares = _fund4626(env.underlyingBalance, user); vm.prank(user); uint256 redeemedAssets = erc4626Upgradeable.redeemATokens(shares, receiver, user); @@ -237,7 +260,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { uint256 allowance ) external { TestEnv memory env = _setupTestEnv(assets); - uint256 shares = _fund4626(env.amount, user); + uint256 shares = _fund4626(env.underlyingBalance, user); allowance = bound(allowance, 0, shares - 1); vm.prank(user); @@ -248,15 +271,15 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { IERC20Errors.ERC20InsufficientAllowance.selector, address(this), allowance, - env.amount + env.underlyingBalance ) ); - erc4626Upgradeable.redeemATokens(env.amount, address(this), user); + erc4626Upgradeable.redeemATokens(env.underlyingBalance, address(this), user); } function test_redeemATokens_onBehalf(uint256 assets) external { TestEnv memory env = _setupTestEnv(assets); - uint256 shares = _fund4626(env.amount, user); + uint256 shares = _fund4626(env.underlyingBalance, user); vm.prank(user); erc4626Upgradeable.approve(address(this), shares); @@ -269,7 +292,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { function test_redeem(uint256 assets, address receiver) external { _validateReceiver(receiver); TestEnv memory env = _setupTestEnv(assets); - uint256 shares = _fund4626(env.amount, user); + uint256 shares = _fund4626(env.underlyingBalance, user); vm.prank(user); uint256 redeemedAssets = erc4626Upgradeable.redeem(shares, receiver, user); @@ -281,55 +304,55 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { function test_withdraw(uint256 assets, address receiver) public { _validateReceiver(receiver); TestEnv memory env = _setupTestEnv(assets); - uint256 shares = _fund4626(env.amount, user); + uint256 shares = _fund4626(env.underlyingBalance, user); vm.prank(user); - uint256 withdrawnShares = erc4626Upgradeable.withdraw(env.amount, receiver, user); + uint256 withdrawnShares = erc4626Upgradeable.withdraw(env.underlyingBalance, receiver, user); assertEq(withdrawnShares, shares); assertEq(erc4626Upgradeable.balanceOf(user), 0); - assertLe(IERC20(underlying).balanceOf(receiver), env.amount); - assertApproxEqAbs(IERC20(underlying).balanceOf(receiver), env.amount, 1); + assertLe(IERC20(underlying).balanceOf(receiver), env.underlyingBalance); + assertApproxEqAbs(IERC20(underlying).balanceOf(receiver), env.underlyingBalance, 1); } function test_withdraw_shouldRevert_moreThenAvailable(uint256 assets, address receiver) public { _validateReceiver(receiver); TestEnv memory env = _setupTestEnv(assets); - _fund4626(env.amount, user); + _fund4626(env.underlyingBalance, user); vm.prank(user); vm.expectRevert( abi.encodeWithSelector( ERC4626Upgradeable.ERC4626ExceededMaxWithdraw.selector, address(user), - env.amount + 1, - env.amount + env.underlyingBalance + 1, + env.underlyingBalance ) ); - erc4626Upgradeable.withdraw(env.amount + 1, receiver, user); + erc4626Upgradeable.withdraw(env.underlyingBalance + 1, receiver, user); } // ### mint TESTS ### function test_mint(uint256 assets, address receiver) public { _validateReceiver(receiver); TestEnv memory env = _setupTestEnv(assets); - _fundUnderlying(env.amount, user); + _fundUnderlying(env.underlyingBalance, user); vm.startPrank(user); - IERC20(underlying).approve(address(erc4626Upgradeable), env.amount); - uint256 shares = erc4626Upgradeable.previewDeposit(env.amount); + IERC20(underlying).approve(address(erc4626Upgradeable), env.underlyingBalance); + uint256 shares = erc4626Upgradeable.previewDeposit(env.underlyingBalance); uint256 assetsUsedForMinting = erc4626Upgradeable.mint(shares, receiver); - assertEq(assetsUsedForMinting, env.amount); + assertEq(assetsUsedForMinting, env.underlyingBalance); assertEq(erc4626Upgradeable.balanceOf(receiver), shares); } function test_mint_shouldRevert_mintMoreThenBalance(uint256 assets, address receiver) public { _validateReceiver(receiver); TestEnv memory env = _setupTestEnv(assets); - _fundUnderlying(env.amount, user); + _fundUnderlying(env.underlyingBalance, user); vm.startPrank(user); IERC20(underlying).approve(address(erc4626Upgradeable), type(uint256).max); - uint256 shares = erc4626Upgradeable.previewDeposit(env.amount); + uint256 shares = erc4626Upgradeable.previewDeposit(env.underlyingBalance); vm.expectRevert(); erc4626Upgradeable.mint(shares + 1, receiver); @@ -379,7 +402,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { // ### maxRedeem TESTS ### function test_maxRedeem_paused(uint128 assets) public { TestEnv memory env = _setupTestEnv(assets); - _fund4626(env.amount, user); + _fund4626(env.underlyingBalance, user); vm.prank(address(roleList.marketOwner)); contracts.poolConfiguratorProxy.setReservePause(underlying, true); @@ -391,7 +414,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { function test_maxRedeem_sufficientAvailableLiquidity(uint128 assets) public { TestEnv memory env = _setupTestEnv(assets); - uint256 shares = _fund4626(env.amount, user); + uint256 shares = _fund4626(env.underlyingBalance, user); uint256 max = erc4626Upgradeable.maxRedeem(address(user)); @@ -446,19 +469,32 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { } struct TestEnv { - uint256 amount; + uint256 underlyingBalance; + uint256 amountToDeposit; + uint256 actualAmountToDeposit; } function _validateReceiver(address receiver) internal view { vm.assume(receiver != address(0) && receiver != address(aToken)); } - function _setupTestEnv(uint256 amount) internal pure returns (TestEnv memory) { + function _setupTestEnv( + uint256 underlyingBalance, + uint256 amountToDeposit + ) internal pure returns (TestEnv memory) { TestEnv memory env; - env.amount = bound(amount, 1, type(uint96).max); + env.underlyingBalance = bound(underlyingBalance, 1, type(uint96).max); + env.amountToDeposit = bound(amountToDeposit, 1, type(uint256).max); + env.actualAmountToDeposit = env.amountToDeposit > env.underlyingBalance + ? env.underlyingBalance + : env.amountToDeposit; return env; } + function _setupTestEnv(uint256 underlyingBalance) internal pure returns (TestEnv memory) { + return _setupTestEnv(underlyingBalance, underlyingBalance); + } + function _fundUnderlying(uint256 assets, address receiver) internal { deal(underlying, receiver, assets); } diff --git a/tests/extensions/static-a-token/StataTokenV2Rescuable.sol b/tests/extensions/static-a-token/StataTokenV2Rescuable.sol index 8034fd52..b494a933 100644 --- a/tests/extensions/static-a-token/StataTokenV2Rescuable.sol +++ b/tests/extensions/static-a-token/StataTokenV2Rescuable.sol @@ -16,7 +16,7 @@ contract StataTokenV2RescuableTest is BaseTest { function test_rescuable_shouldRevertForInvalidCaller() external { deal(tokenList.usdx, address(stataTokenV2), 1 ether); - vm.expectRevert('ONLY_RESCUE_GUARDIAN'); + vm.expectRevert(abi.encodeWithSelector(IRescuable.OnlyRescueGuardian.selector)); IRescuable(address(stataTokenV2)).emergencyTokenTransfer( tokenList.usdx, address(this),