diff --git a/tests/periphery/static-a-token/ERC4626StataTokenUpgradeable.t.sol b/tests/periphery/static-a-token/ERC4626StataTokenUpgradeable.t.sol index 62e4382b..d9e0ce7c 100644 --- a/tests/periphery/static-a-token/ERC4626StataTokenUpgradeable.t.sol +++ b/tests/periphery/static-a-token/ERC4626StataTokenUpgradeable.t.sol @@ -5,7 +5,7 @@ import {IERC20Errors} from 'openzeppelin-contracts/contracts/interfaces/draft-IE import {IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol'; import {IPool} from '../../../src/core/contracts/interfaces/IPool.sol'; import {TestnetProcedures, TestnetERC20} from '../../utils/TestnetProcedures.sol'; -import {ERC4626StataTokenUpgradeable, IStata4626} from '../../../src/periphery/contracts/static-a-token/ERC4626StataTokenUpgradeable.sol'; +import {ERC4626StataTokenUpgradeable, IERC4626StataToken} from '../../../src/periphery/contracts/static-a-token/ERC4626StataTokenUpgradeable.sol'; import {IRewardsController} from '../../../src/periphery/contracts/rewards/interfaces/IRewardsController.sol'; import {PullRewardsTransferStrategy, ITransferStrategyBase} from '../../../src/periphery/contracts/rewards/transfer-strategies/PullRewardsTransferStrategy.sol'; import {RewardsDataTypes} from '../../../src/periphery/contracts/rewards/libraries/RewardsDataTypes.sol'; @@ -17,7 +17,7 @@ contract MockERC4626StataTokenUpgradeable is ERC4626StataTokenUpgradeable { constructor(IPool pool) ERC4626StataTokenUpgradeable(pool) {} function mockInit(address aToken) external initializer { - __Stata4626_init(aToken); + __ERC4626StataToken_init(aToken); } } @@ -44,6 +44,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { erc4626Upgradeable.mockInit(address(reserveDataWETH.aTokenAddress)); } + // ### DEPOSIT TESTS ### function test_depositATokens(uint128 assets, address receiver) public { vm.assume(receiver != address(0)); TestEnv memory env = _setupTestEnv(assets); @@ -63,6 +64,16 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { test_depositATokens(1 ether, user); } + function test_deposit_shouldRevert_insufficientAllowance(uint128 assets) external { + TestEnv memory env = _setupTestEnv(assets); + _fundAToken(env.amount, user); + + vm.expectRevert(); // underflows + vm.prank(user); + uint256 shares = erc4626Upgradeable.depositATokens(env.amount, user); + } + + // ### REDEEM TESTS ### function test_redeemATokens(uint256 assets, address receiver) public { vm.assume(receiver != address(0)); TestEnv memory env = _setupTestEnv(assets); @@ -106,6 +117,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { erc4626Upgradeable.redeemATokens(env.amount, address(this), user); } + // ### maxDeposit TESTS ### function test_maxDeposit_freeze() public { vm.prank(roleList.marketOwner); contracts.poolConfiguratorProxy.setReserveFreeze(underlying, true); @@ -144,8 +156,9 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { assertEq(max, cap * 10 ** erc4626Upgradeable.decimals()); } - //TODO: perhaps makes sense to add maxDeposit test with accruedToTreasury etc + // TODO: perhaps makes sense to add maxDeposit test with accruedToTreasury etc + // ### maxRedeem TESTS ### function test_maxRedeem_paused(uint128 assets) public { TestEnv memory env = _setupTestEnv(assets); uint256 shares = _fund4626(env.amount, user); @@ -185,7 +198,7 @@ contract ERC4626StataTokenUpgradeableTest is TestnetProcedures { assertEq(max, erc4626Upgradeable.previewRedeem(assets - amountToBorrow)); } - // ### tests for the token internal oracle + // ### lastestAnswer TESTS ### function test_latestAnswer_priceShouldBeEqualOnDefaultIndex() public { vm.mockCall( address(contracts.poolProxy), diff --git a/tests/periphery/static-a-token/Pausable.t.sol b/tests/periphery/static-a-token/Pausable.t.sol deleted file mode 100644 index 709106c1..00000000 --- a/tests/periphery/static-a-token/Pausable.t.sol +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.10; - -import {UpgradableOwnableWithGuardian} from 'solidity-utils/contracts/access-control/UpgradableOwnableWithGuardian.sol'; -import {PausableUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol'; -import {IERC20Metadata, IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol'; -import {AToken} from '../../../src/core/contracts/protocol/tokenization/AToken.sol'; -import {DataTypes} from '../../../src/core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol'; -import {PullRewardsTransferStrategy} from '../../../src/periphery/contracts/rewards/transfer-strategies/PullRewardsTransferStrategy.sol'; -import {RewardsDataTypes} from '../../../src/periphery/contracts/rewards/libraries/RewardsDataTypes.sol'; -import {ITransferStrategyBase} from '../../../src/periphery/contracts/rewards/interfaces/ITransferStrategyBase.sol'; -import {IEACAggregatorProxy} from '../../../src/periphery/contracts/misc/interfaces/IEACAggregatorProxy.sol'; -import {IERC4626StataToken} from '../../../src/periphery/contracts/static-a-token/interfaces/IERC4626StataToken.sol'; -import {SigUtils} from '../../utils/SigUtils.sol'; -import {BaseTest, TestnetERC20} from './TestBase.sol'; - -contract StataPausableTest is BaseTest { - function test_setPaused_shouldRevertForInvalidCaller(address actor) external { - vm.assume(actor != poolAdmin && actor != proxyAdmin); - vm.expectRevert(abi.encodeWithSelector(IERC4626StataToken.OnlyPauseGuardian.selector, actor)); - _setPaused(actor, true); - } - - function test_setPaused_shouldSucceedForOwner() external { - assertEq(PausableUpgradeable(address(staticATokenLM)).paused(), false); - _setPaused(poolAdmin, true); - assertEq(PausableUpgradeable(address(staticATokenLM)).paused(), true); - } - - function test_deposit_shouldRevert() external { - vm.startPrank(user); - uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - IERC20(UNDERLYING).approve(address(staticATokenLM), amountToDeposit); - vm.stopPrank(); - - _setPausedAsAclAdmin(true); - vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); - vm.prank(user); - staticATokenLM.deposit(amountToDeposit, user); - } - // TODO: add depositATokens - - function test_mint_shouldRevert() external { - vm.startPrank(user); - uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - IERC20(UNDERLYING).approve(address(staticATokenLM), amountToDeposit); - vm.stopPrank(); - - uint256 sharesToMint = staticATokenLM.previewDeposit(amountToDeposit); - _setPausedAsAclAdmin(true); - vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); - vm.prank(user); - staticATokenLM.mint(sharesToMint, user); - } - - function test_redeem_shouldRevert() external { - uint128 amountToDeposit = 5 ether; - vm.startPrank(user); - _fundUser(amountToDeposit, user); - _depositAToken(amountToDeposit, user); - vm.stopPrank(); - - assertEq(staticATokenLM.maxRedeem(user), staticATokenLM.balanceOf(user)); - - _setPausedAsAclAdmin(true); - uint256 maxRedeem = staticATokenLM.maxRedeem(user); - vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); - vm.prank(user); - staticATokenLM.redeem(maxRedeem, user, user); - } - - function test_withdraw_shouldRevert() external { - uint128 amountToDeposit = 5 ether; - vm.startPrank(user); - _fundUser(amountToDeposit, user); - _depositAToken(amountToDeposit, user); - vm.stopPrank(); - - uint256 maxWithdraw = staticATokenLM.maxWithdraw(user); - _setPausedAsAclAdmin(true); - vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); - vm.prank(user); - staticATokenLM.withdraw(maxWithdraw, user, user); - } - - function test_transfer_shouldRevert() external { - uint128 amountToDeposit = 10 ether; - vm.startPrank(user); - _fundUser(amountToDeposit, user); - _depositAToken(amountToDeposit, user); - vm.stopPrank(); - - _setPausedAsAclAdmin(true); - vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); - vm.prank(user); - staticATokenLM.transfer(user1, amountToDeposit); - } - - function test_claimingRewards_shouldRevert() external { - _configureLM(); - uint128 amountToDeposit = 10 ether; - vm.startPrank(user); - _fundUser(amountToDeposit, user); - _depositAToken(amountToDeposit, user); - vm.stopPrank(); - - _setPausedAsAclAdmin(true); - vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); - vm.prank(user); - staticATokenLM.claimRewardsToSelf(rewardTokens); - } - - function _setPausedAsAclAdmin(bool paused) internal { - _setPaused(poolAdmin, paused); - } - - function _setPaused(address actor, bool paused) internal { - vm.prank(actor); - staticATokenLM.setPaused(paused); - } -} diff --git a/tests/periphery/static-a-token/Stata4626LM.t.sol b/tests/periphery/static-a-token/Stata4626LM.t.sol index be279b5b..987657ba 100644 --- a/tests/periphery/static-a-token/Stata4626LM.t.sol +++ b/tests/periphery/static-a-token/Stata4626LM.t.sol @@ -15,140 +15,87 @@ import {BaseTest, TestnetERC20} from './TestBase.sol'; import {IPool} from '../../../src/core/contracts/interfaces/IPool.sol'; contract Stata4626LMTest is BaseTest { - function setUp() public override { - super.setUp(); - - _configureLM(); - _openSupplyAndBorrowPositions(); - - vm.startPrank(user); - } - function test_initializeShouldRevert() public { address impl = factory.STATIC_A_TOKEN_IMPL(); vm.expectRevert(Initializable.InvalidInitialization.selector); - StataTokenV2(impl).initialize(A_TOKEN, 'hey', 'ho'); + StataTokenV2(impl).initialize(aToken, 'hey', 'ho'); } function test_getters() public view { - assertEq(staticATokenLM.name(), 'Static Aave Local WETH'); - assertEq(staticATokenLM.symbol(), 'stataLocWETH'); + assertEq(stataTokenV2.name(), 'Static Aave Local WETH'); + assertEq(stataTokenV2.symbol(), 'stataLocWETH'); - address referenceAsset = staticATokenLM.getReferenceAsset(); - assertEq(referenceAsset, A_TOKEN); + address referenceAsset = stataTokenV2.getReferenceAsset(); + assertEq(referenceAsset, aToken); - address underlyingAddress = address(staticATokenLM.asset()); - assertEq(underlyingAddress, UNDERLYING); + address underlyingAddress = address(stataTokenV2.asset()); + assertEq(underlyingAddress, underlying); IERC20Metadata underlying = IERC20Metadata(underlyingAddress); - assertEq(staticATokenLM.decimals(), underlying.decimals()); + assertEq(stataTokenV2.decimals(), underlying.decimals()); assertEq( - address(staticATokenLM.INCENTIVES_CONTROLLER()), - address(AToken(A_TOKEN).getIncentivesController()) + address(stataTokenV2.INCENTIVES_CONTROLLER()), + address(AToken(aToken).getIncentivesController()) ); } function test_convertersAndPreviews() public view { uint128 amount = 5 ether; - uint256 shares = staticATokenLM.convertToShares(amount); + uint256 shares = stataTokenV2.convertToShares(amount); assertLe(shares, amount, 'SHARES LOWER'); - assertEq(shares, staticATokenLM.previewDeposit(amount), 'PREVIEW_DEPOSIT'); - assertLe(shares, staticATokenLM.previewWithdraw(amount), 'PREVIEW_WITHDRAW'); - uint256 assets = staticATokenLM.convertToAssets(amount); + assertEq(shares, stataTokenV2.previewDeposit(amount), 'PREVIEW_DEPOSIT'); + assertLe(shares, stataTokenV2.previewWithdraw(amount), 'PREVIEW_WITHDRAW'); + uint256 assets = stataTokenV2.convertToAssets(amount); assertGe(assets, shares, 'ASSETS GREATER'); - assertLe(assets, staticATokenLM.previewMint(amount), 'PREVIEW_MINT'); - assertEq(assets, staticATokenLM.previewRedeem(amount), 'PREVIEW_REDEEM'); + assertLe(assets, stataTokenV2.previewMint(amount), 'PREVIEW_MINT'); + assertEq(assets, stataTokenV2.previewRedeem(amount), 'PREVIEW_REDEEM'); } // Redeem tests function test_redeem() public { uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - _depositAToken(amountToDeposit, user); + _fund4626(amountToDeposit, user); - assertEq(staticATokenLM.maxRedeem(user), staticATokenLM.balanceOf(user)); - staticATokenLM.redeem(staticATokenLM.maxRedeem(user), user, user); - assertEq(staticATokenLM.balanceOf(user), 0); - assertLe(IERC20(UNDERLYING).balanceOf(user), amountToDeposit); - assertApproxEqAbs(IERC20(UNDERLYING).balanceOf(user), amountToDeposit, 1); + assertEq(stataTokenV2.maxRedeem(user), stataTokenV2.balanceOf(user)); + stataTokenV2.redeem(stataTokenV2.maxRedeem(user), user, user); + assertEq(stataTokenV2.balanceOf(user), 0); + assertLe(IERC20(underlying).balanceOf(user), amountToDeposit); + assertApproxEqAbs(IERC20(underlying).balanceOf(user), amountToDeposit, 1); } function test_redeemAToken() public { uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - - _depositAToken(amountToDeposit, user); - - assertEq(staticATokenLM.maxRedeem(user), staticATokenLM.balanceOf(user)); - staticATokenLM.redeemATokens(staticATokenLM.maxRedeem(user), user, user); - assertEq(staticATokenLM.balanceOf(user), 0); - assertLe(IERC20(A_TOKEN).balanceOf(user), amountToDeposit); - assertApproxEqAbs(IERC20(A_TOKEN).balanceOf(user), amountToDeposit, 1); - } - - function test_redeemAllowance() public { - uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - - _depositAToken(amountToDeposit, user); + _fund4626(amountToDeposit, user); - staticATokenLM.approve(user1, staticATokenLM.maxRedeem(user)); - vm.stopPrank(); - vm.startPrank(user1); - staticATokenLM.redeem(staticATokenLM.maxRedeem(user), user1, user); - assertEq(staticATokenLM.balanceOf(user), 0); - assertLe(IERC20(UNDERLYING).balanceOf(user1), amountToDeposit); - assertApproxEqAbs(IERC20(UNDERLYING).balanceOf(user1), amountToDeposit, 1); - } - - function testFail_redeemOverflowAllowance() public { - uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - - _depositAToken(amountToDeposit, user); - - staticATokenLM.approve(user1, staticATokenLM.maxRedeem(user) / 2); - vm.stopPrank(); - vm.startPrank(user1); - staticATokenLM.redeem(staticATokenLM.maxRedeem(user), user1, user); - assertEq(staticATokenLM.balanceOf(user), 0); - assertEq(IERC20(A_TOKEN).balanceOf(user1), amountToDeposit); - } - - function testFail_redeemAboveBalance() public { - uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - - _depositAToken(amountToDeposit, user); - staticATokenLM.redeem(staticATokenLM.maxRedeem(user) + 1, user, user); + assertEq(stataTokenV2.maxRedeem(user), stataTokenV2.balanceOf(user)); + stataTokenV2.redeemATokens(stataTokenV2.maxRedeem(user), user, user); + assertEq(stataTokenV2.balanceOf(user), 0); + assertLe(IERC20(aToken).balanceOf(user), amountToDeposit); + assertApproxEqAbs(IERC20(aToken).balanceOf(user), amountToDeposit, 1); } // Withdraw tests function test_withdraw() public { uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - - _depositAToken(amountToDeposit, user); + _fund4626(amountToDeposit, user); - assertLe(staticATokenLM.maxWithdraw(user), amountToDeposit); - staticATokenLM.withdraw(staticATokenLM.maxWithdraw(user), user, user); - assertEq(staticATokenLM.balanceOf(user), 0); - assertLe(IERC20(UNDERLYING).balanceOf(user), amountToDeposit); - assertApproxEqAbs(IERC20(UNDERLYING).balanceOf(user), amountToDeposit, 1); + assertLe(stataTokenV2.maxWithdraw(user), amountToDeposit); + stataTokenV2.withdraw(stataTokenV2.maxWithdraw(user), user, user); + assertEq(stataTokenV2.balanceOf(user), 0); + assertLe(IERC20(underlying).balanceOf(user), amountToDeposit); + assertApproxEqAbs(IERC20(underlying).balanceOf(user), amountToDeposit, 1); } function testFail_withdrawAboveBalance() public { uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - _fundUser(amountToDeposit, user1); - _depositAToken(amountToDeposit, user); - _depositAToken(amountToDeposit, user1); + _fundAToken(amountToDeposit, user); + _fundAToken(amountToDeposit, user1); - assertEq(staticATokenLM.maxWithdraw(user), amountToDeposit); - staticATokenLM.withdraw(staticATokenLM.maxWithdraw(user) + 1, user, user); + assertEq(stataTokenV2.maxWithdraw(user), amountToDeposit); + stataTokenV2.withdraw(stataTokenV2.maxWithdraw(user) + 1, user, user); } // mint @@ -157,134 +104,25 @@ contract Stata4626LMTest is BaseTest { // set supply cap to non-zero vm.startPrank(poolAdmin); - contracts.poolConfiguratorProxy.setSupplyCap(UNDERLYING, 15_000); + contracts.poolConfiguratorProxy.setSupplyCap(underlying, 15_000); vm.stopPrank(); vm.startPrank(user); uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); + _fundUnderlying(amountToDeposit, user); - IERC20(UNDERLYING).approve(address(staticATokenLM), amountToDeposit); + IERC20(underlying).approve(address(stataTokenV2), amountToDeposit); uint256 shares = 1 ether; - staticATokenLM.mint(shares, user); - assertEq(shares, staticATokenLM.balanceOf(user)); + stataTokenV2.mint(shares, user); + assertEq(shares, stataTokenV2.balanceOf(user)); } function testFail_mintAboveBalance() public { uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); - - _underlyingToAToken(amountToDeposit, user); - IERC20(A_TOKEN).approve(address(staticATokenLM), amountToDeposit); - staticATokenLM.mint(amountToDeposit, user); - } - - function test_permit() public { - SigUtils.Permit memory permit = SigUtils.Permit({ - owner: user, - spender: spender, - value: 1 ether, - nonce: staticATokenLM.nonces(user), - deadline: block.timestamp + 1 days - }); - - bytes32 permitDigest = SigUtils.getTypedDataHash( - permit, - PERMIT_TYPEHASH, - staticATokenLM.DOMAIN_SEPARATOR() - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitDigest); - - staticATokenLM.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s); - - assertEq(staticATokenLM.allowance(permit.owner, spender), permit.value); - } - - function test_permit_expired() public { - // as the default timestamp is 0, we move ahead in time a bit - vm.warp(10 days); - - SigUtils.Permit memory permit = SigUtils.Permit({ - owner: user, - spender: spender, - value: 1 ether, - nonce: staticATokenLM.nonces(user), - deadline: block.timestamp - 1 days - }); - - bytes32 permitDigest = SigUtils.getTypedDataHash( - permit, - PERMIT_TYPEHASH, - staticATokenLM.DOMAIN_SEPARATOR() - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitDigest); - - vm.expectRevert( - abi.encodeWithSelector( - ERC20PermitUpgradeable.ERC2612ExpiredSignature.selector, - permit.deadline - ) - ); - staticATokenLM.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s); - } - - function test_permit_invalidSigner() public { - SigUtils.Permit memory permit = SigUtils.Permit({ - owner: address(424242), - spender: spender, - value: 1 ether, - nonce: staticATokenLM.nonces(user), - deadline: block.timestamp + 1 days - }); - - bytes32 permitDigest = SigUtils.getTypedDataHash( - permit, - PERMIT_TYPEHASH, - staticATokenLM.DOMAIN_SEPARATOR() - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitDigest); + _fund4626(amountToDeposit, user); - vm.expectRevert( - abi.encodeWithSelector( - ERC20PermitUpgradeable.ERC2612InvalidSigner.selector, - user, - permit.owner - ) - ); - staticATokenLM.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s); - } - - function test_rescuable_shouldRevertForInvalidCaller() external { - deal(tokenList.usdx, address(staticATokenLM), 1 ether); - vm.expectRevert('ONLY_RESCUE_GUARDIAN'); - IRescuable(address(staticATokenLM)).emergencyTokenTransfer( - tokenList.usdx, - address(this), - 1 ether - ); - } - - function test_rescuable_shouldSuceedForOwner() external { - deal(tokenList.usdx, address(staticATokenLM), 1 ether); - vm.startPrank(poolAdmin); - IRescuable(address(staticATokenLM)).emergencyTokenTransfer( - tokenList.usdx, - address(this), - 1 ether - ); - } - - function _openSupplyAndBorrowPositions() internal { - // this is to open borrow positions so that the aToken balance increases - address whale = address(79); - vm.startPrank(whale); - _fundUser(5_000 ether, whale); - - weth.approve(address(POOL), 5_000 ether); - POOL.deposit(address(weth), 5_000 ether, whale, 0); - - POOL.borrow(address(weth), 1_000 ether, 2, 0, whale); - vm.stopPrank(); + IERC20(aToken).approve(address(stataTokenV2), amountToDeposit); + stataTokenV2.mint(amountToDeposit, user); } } diff --git a/tests/periphery/static-a-token/StataTokenV2Pausable.t.sol b/tests/periphery/static-a-token/StataTokenV2Pausable.t.sol new file mode 100644 index 00000000..1e714717 --- /dev/null +++ b/tests/periphery/static-a-token/StataTokenV2Pausable.t.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.10; + +import {PausableUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol'; +import {IERC20Metadata, IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol'; +import {IERC4626StataToken} from '../../../src/periphery/contracts/static-a-token/interfaces/IERC4626StataToken.sol'; +import {BaseTest} from './TestBase.sol'; + +contract StataTokenV2PausableTest is BaseTest { + function test_setPaused_shouldRevertForInvalidCaller(address actor) external { + vm.assume(actor != poolAdmin && actor != proxyAdmin); + vm.expectRevert(abi.encodeWithSelector(IERC4626StataToken.OnlyPauseGuardian.selector, actor)); + _setPaused(actor, true); + } + + function test_setPaused_shouldSucceedForOwner() external { + assertEq(PausableUpgradeable(address(stataTokenV2)).paused(), false); + _setPaused(poolAdmin, true); + assertEq(PausableUpgradeable(address(stataTokenV2)).paused(), true); + } + + function test_deposit_shouldRevert() external { + uint128 amountToDeposit = 5 ether; + _fundUnderlying(amountToDeposit, user); + vm.prank(user); + IERC20(underlying).approve(address(stataTokenV2), amountToDeposit); + + _setPausedAsAclAdmin(true); + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + vm.prank(user); + stataTokenV2.deposit(amountToDeposit, user); + } + + function test_mint_shouldRevert() external { + uint128 amountToDeposit = 5 ether; + _fundUnderlying(amountToDeposit, user); + vm.prank(user); + IERC20(underlying).approve(address(stataTokenV2), amountToDeposit); + + uint256 sharesToMint = stataTokenV2.previewDeposit(amountToDeposit); + _setPausedAsAclAdmin(true); + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + vm.prank(user); + stataTokenV2.mint(sharesToMint, user); + } + + function test_redeem_shouldRevert() external { + uint128 amountToDeposit = 5 ether; + _fund4626(amountToDeposit, user); + + assertEq(stataTokenV2.maxRedeem(user), stataTokenV2.balanceOf(user)); + + _setPausedAsAclAdmin(true); + uint256 maxRedeem = stataTokenV2.maxRedeem(user); + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + vm.prank(user); + stataTokenV2.redeem(maxRedeem, user, user); + } + + function test_withdraw_shouldRevert() external { + uint128 amountToDeposit = 5 ether; + _fund4626(amountToDeposit, user); + + uint256 maxWithdraw = stataTokenV2.maxWithdraw(user); + _setPausedAsAclAdmin(true); + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + vm.prank(user); + stataTokenV2.withdraw(maxWithdraw, user, user); + } + + function test_transfer_shouldRevert() external { + uint128 amountToDeposit = 10 ether; + _fund4626(amountToDeposit, user); + + _setPausedAsAclAdmin(true); + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + vm.prank(user); + stataTokenV2.transfer(user1, amountToDeposit); + } + + function test_claimingRewards_shouldRevert() external { + uint128 amountToDeposit = 10 ether; + _fund4626(amountToDeposit, user); + + _setPausedAsAclAdmin(true); + vm.expectRevert(PausableUpgradeable.EnforcedPause.selector); + vm.prank(user); + stataTokenV2.claimRewardsToSelf(rewardTokens); + } + + function _setPausedAsAclAdmin(bool paused) internal { + _setPaused(poolAdmin, paused); + } + + function _setPaused(address actor, bool paused) internal { + vm.prank(actor); + stataTokenV2.setPaused(paused); + } +} diff --git a/tests/periphery/static-a-token/StataTokenV2Permit.sol b/tests/periphery/static-a-token/StataTokenV2Permit.sol new file mode 100644 index 00000000..47829867 --- /dev/null +++ b/tests/periphery/static-a-token/StataTokenV2Permit.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.10; + +import {IRescuable} from 'solidity-utils/contracts/utils/Rescuable.sol'; +import {BaseTest} from './TestBase.sol'; + +contract StataTokenV2RescuableTest is BaseTest { + function test_rescuable_shouldRevertForInvalidCaller() external { + deal(tokenList.usdx, address(stataTokenV2), 1 ether); + vm.expectRevert('ONLY_RESCUE_GUARDIAN'); + IRescuable(address(stataTokenV2)).emergencyTokenTransfer( + tokenList.usdx, + address(this), + 1 ether + ); + } + + function test_rescuable_shouldSuceedForOwner() external { + deal(tokenList.usdx, address(stataTokenV2), 1 ether); + vm.startPrank(poolAdmin); + IRescuable(address(stataTokenV2)).emergencyTokenTransfer( + tokenList.usdx, + address(this), + 1 ether + ); + } +} diff --git a/tests/periphery/static-a-token/StataTokenV2Rescuable.sol b/tests/periphery/static-a-token/StataTokenV2Rescuable.sol new file mode 100644 index 00000000..ebbe22e8 --- /dev/null +++ b/tests/periphery/static-a-token/StataTokenV2Rescuable.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.10; + +import {ERC20PermitUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol'; +import {SigUtils} from '../../utils/SigUtils.sol'; +import {BaseTest} from './TestBase.sol'; + +contract StataTokenV2PermitTest is BaseTest { + function test_permit() public { + SigUtils.Permit memory permit = SigUtils.Permit({ + owner: user, + spender: spender, + value: 1 ether, + nonce: stataTokenV2.nonces(user), + deadline: block.timestamp + 1 days + }); + + bytes32 permitDigest = SigUtils.getTypedDataHash( + permit, + PERMIT_TYPEHASH, + stataTokenV2.DOMAIN_SEPARATOR() + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitDigest); + + stataTokenV2.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s); + + assertEq(stataTokenV2.allowance(permit.owner, spender), permit.value); + } + + function test_permit_expired() public { + // as the default timestamp is 0, we move ahead in time a bit + vm.warp(10 days); + + SigUtils.Permit memory permit = SigUtils.Permit({ + owner: user, + spender: spender, + value: 1 ether, + nonce: stataTokenV2.nonces(user), + deadline: block.timestamp - 1 days + }); + + bytes32 permitDigest = SigUtils.getTypedDataHash( + permit, + PERMIT_TYPEHASH, + stataTokenV2.DOMAIN_SEPARATOR() + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitDigest); + + vm.expectRevert( + abi.encodeWithSelector( + ERC20PermitUpgradeable.ERC2612ExpiredSignature.selector, + permit.deadline + ) + ); + stataTokenV2.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s); + } + + function test_permit_invalidSigner() public { + SigUtils.Permit memory permit = SigUtils.Permit({ + owner: address(424242), + spender: spender, + value: 1 ether, + nonce: stataTokenV2.nonces(user), + deadline: block.timestamp + 1 days + }); + + bytes32 permitDigest = SigUtils.getTypedDataHash( + permit, + PERMIT_TYPEHASH, + stataTokenV2.DOMAIN_SEPARATOR() + ); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitDigest); + + vm.expectRevert( + abi.encodeWithSelector( + ERC20PermitUpgradeable.ERC2612InvalidSigner.selector, + user, + permit.owner + ) + ); + stataTokenV2.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s); + } +} diff --git a/tests/periphery/static-a-token/StaticATokenNoLM.t.sol b/tests/periphery/static-a-token/StaticATokenNoLM.t.sol index 66db80ac..6f7bd5b2 100644 --- a/tests/periphery/static-a-token/StaticATokenNoLM.t.sol +++ b/tests/periphery/static-a-token/StaticATokenNoLM.t.sol @@ -1,51 +1,51 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.10; +// // SPDX-License-Identifier: BUSL-1.1 +// pragma solidity ^0.8.10; -import {BaseTest, IERC20, IERC20AaveLM} from './TestBase.sol'; +// import {BaseTest, IERC20, IERC20AaveLM} from './TestBase.sol'; -/** - * Testing the static token wrapper on a pool that never had LM enabled - * This is a slightly different assumption than a pool that doesn't have LM enabled any more as incentivesController.rewardTokens() will have length=0 - */ -contract StaticATokenNoLMTest is BaseTest { - function setUp() public override { - super.setUp(); +// /** +// * Testing the static token wrapper on a pool that never had LM enabled +// * This is a slightly different assumption than a pool that doesn't have LM enabled any more as incentivesController.rewardTokens() will have length=0 +// */ +// contract StaticATokenNoLMTest is BaseTest { +// function setUp() public override { +// super.setUp(); - vm.startPrank(user); - } +// vm.startPrank(user); +// } - // test rewards - function test_collectAndUpdateRewardsWithLMDisabled() public { - uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); +// // test rewards +// function test_collectAndUpdateRewardsWithLMDisabled() public { +// uint128 amountToDeposit = 5 ether; +// _fundUser(amountToDeposit, user); - _depositAToken(amountToDeposit, user); +// _depositAToken(amountToDeposit, user); - _skipBlocks(60); - assertEq(IERC20(REWARD_TOKEN).balanceOf(address(staticATokenLM)), 0); - assertEq(staticATokenLM.getTotalClaimableRewards(REWARD_TOKEN), 0); - assertEq(staticATokenLM.collectAndUpdateRewards(REWARD_TOKEN), 0); - assertEq(IERC20(REWARD_TOKEN).balanceOf(address(staticATokenLM)), 0); - } +// _skipBlocks(60); +// assertEq(IERC20(REWARD_TOKEN).balanceOf(address(stataTokenV2)), 0); +// assertEq(stataTokenV2.getTotalClaimableRewards(REWARD_TOKEN), 0); +// assertEq(stataTokenV2.collectAndUpdateRewards(REWARD_TOKEN), 0); +// assertEq(IERC20(REWARD_TOKEN).balanceOf(address(stataTokenV2)), 0); +// } - function test_claimRewardsToSelfWithLMDisabled() public { - uint128 amountToDeposit = 5 ether; - _fundUser(amountToDeposit, user); +// function test_claimRewardsToSelfWithLMDisabled() public { +// uint128 amountToDeposit = 5 ether; +// _fundUser(amountToDeposit, user); - _depositAToken(amountToDeposit, user); +// _depositAToken(amountToDeposit, user); - _skipBlocks(60); +// _skipBlocks(60); - vm.expectRevert( - abi.encodeWithSelector(IERC20AaveLM.RewardNotInitialized.selector, REWARD_TOKEN) - ); - staticATokenLM.getClaimableRewards(user, REWARD_TOKEN); +// vm.expectRevert( +// abi.encodeWithSelector(IERC20AaveLM.RewardNotInitialized.selector, REWARD_TOKEN) +// ); +// stataTokenV2.getClaimableRewards(user, REWARD_TOKEN); - vm.expectRevert( - abi.encodeWithSelector(IERC20AaveLM.RewardNotInitialized.selector, REWARD_TOKEN) - ); - staticATokenLM.claimRewardsToSelf(rewardTokens); +// vm.expectRevert( +// abi.encodeWithSelector(IERC20AaveLM.RewardNotInitialized.selector, REWARD_TOKEN) +// ); +// stataTokenV2.claimRewardsToSelf(rewardTokens); - assertEq(IERC20(REWARD_TOKEN).balanceOf(user), 0); - } -} +// assertEq(IERC20(REWARD_TOKEN).balanceOf(user), 0); +// } +// } diff --git a/tests/periphery/static-a-token/TestBase.sol b/tests/periphery/static-a-token/TestBase.sol index d5bbe659..f5ca5d25 100644 --- a/tests/periphery/static-a-token/TestBase.sol +++ b/tests/periphery/static-a-token/TestBase.sol @@ -31,17 +31,16 @@ abstract contract BaseTest is TestnetProcedures { uint256 internal userPrivateKey; uint256 internal spenderPrivateKey; - StataTokenV2 public staticATokenLM; + StataTokenV2 public stataTokenV2; address public proxyAdmin; ITransparentProxyFactory public proxyFactory; StaticATokenFactory public factory; address[] rewardTokens; - address public UNDERLYING; - address public A_TOKEN; - address public REWARD_TOKEN; - IPool public POOL; + address public underlying; + address public aToken; + address public rewardToken; function setUp() public virtual { userPrivateKey = 0xA11CE; @@ -55,62 +54,19 @@ abstract contract BaseTest is TestnetProcedures { tokenList.weth ); - UNDERLYING = address(weth); - REWARD_TOKEN = address(new TestnetERC20('LM Reward ERC20', 'RWD', 18, OWNER)); - A_TOKEN = reserveDataWETH.aTokenAddress; - POOL = contracts.poolProxy; + underlying = address(weth); + rewardToken = address(new TestnetERC20('LM Reward ERC20', 'RWD', 18, OWNER)); + aToken = reserveDataWETH.aTokenAddress; - rewardTokens.push(REWARD_TOKEN); + rewardTokens.push(rewardToken); proxyFactory = ITransparentProxyFactory(report.transparentProxyFactory); proxyAdmin = report.proxyAdmin; factory = StaticATokenFactory(report.staticATokenFactoryProxy); - factory.createStaticATokens(POOL.getReservesList()); + factory.createStaticATokens(contracts.poolProxy.getReservesList()); - staticATokenLM = StataTokenV2(factory.getStaticAToken(UNDERLYING)); - } - - function _configureLM() internal { - PullRewardsTransferStrategy strat = new PullRewardsTransferStrategy( - report.rewardsControllerProxy, - EMISSION_ADMIN, - EMISSION_ADMIN - ); - - vm.startPrank(poolAdmin); - contracts.emissionManager.setEmissionAdmin(REWARD_TOKEN, EMISSION_ADMIN); - vm.stopPrank(); - - vm.startPrank(EMISSION_ADMIN); - IERC20(REWARD_TOKEN).approve(address(strat), 10_000 ether); - vm.stopPrank(); - - vm.startPrank(OWNER); - TestnetERC20(REWARD_TOKEN).mint(EMISSION_ADMIN, 10_000 ether); - vm.stopPrank(); - - RewardsDataTypes.RewardsConfigInput[] memory config = new RewardsDataTypes.RewardsConfigInput[]( - 1 - ); - config[0] = RewardsDataTypes.RewardsConfigInput( - 0.00385 ether, - 10_000 ether, - uint32(block.timestamp + 30 days), - A_TOKEN, - REWARD_TOKEN, - ITransferStrategyBase(strat), - IEACAggregatorProxy(address(2)) - ); - - vm.prank(EMISSION_ADMIN); - contracts.emissionManager.configureAssets(config); - - staticATokenLM.refreshRewardTokens(); - } - - function _fundUser(uint128 amountToDeposit, address targetUser) internal { - deal(UNDERLYING, targetUser, amountToDeposit); + stataTokenV2 = StataTokenV2(factory.getStaticAToken(underlying)); } function _skipBlocks(uint128 blocks) internal { @@ -118,22 +74,32 @@ abstract contract BaseTest is TestnetProcedures { vm.warp(block.timestamp + blocks * 12); // assuming a block is around 12seconds } - function _underlyingToAToken(uint256 amountToDeposit, address targetUser) internal { - IERC20(UNDERLYING).approve(address(POOL), amountToDeposit); - POOL.deposit(UNDERLYING, amountToDeposit, targetUser, 0); + function testAdmin() public { + vm.stopPrank(); + vm.startPrank(proxyAdmin); + assertEq(TransparentUpgradeableProxy(payable(address(stataTokenV2))).admin(), proxyAdmin); + assertEq(TransparentUpgradeableProxy(payable(address(factory))).admin(), proxyAdmin); + vm.stopPrank(); } - function _depositAToken(uint256 amountToDeposit, address targetUser) internal returns (uint256) { - _underlyingToAToken(amountToDeposit, targetUser); - IERC20(A_TOKEN).approve(address(staticATokenLM), amountToDeposit); - return staticATokenLM.depositATokens(amountToDeposit, targetUser); + function _fundUnderlying(uint256 assets, address user) internal { + deal(underlying, user, assets); } - function testAdmin() public { + function _fundAToken(uint256 assets, address user) internal { + _fundUnderlying(assets, user); + vm.startPrank(user); + IERC20(underlying).approve(address(contracts.poolProxy), assets); + contracts.poolProxy.deposit(underlying, assets, user, 0); vm.stopPrank(); - vm.startPrank(proxyAdmin); - assertEq(TransparentUpgradeableProxy(payable(address(staticATokenLM))).admin(), proxyAdmin); - assertEq(TransparentUpgradeableProxy(payable(address(factory))).admin(), proxyAdmin); + } + + function _fund4626(uint256 assets, address user) internal returns (uint256) { + _fundAToken(assets, user); + vm.startPrank(user); + IERC20(aToken).approve(address(stataTokenV2), assets); + uint256 shares = stataTokenV2.depositATokens(assets, user); vm.stopPrank(); + return shares; } }