diff --git a/foundry.toml b/foundry.toml index 61253e0b..0d5dd4c3 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,14 +4,17 @@ test = 'tests' script = 'scripts' optimizer = true optimizer_runs = 200 -solc='0.8.19' +solc = '0.8.20' evm_version = 'paris' bytecode_hash = 'none' out = 'out' libs = ['lib'] -remappings = [ +remappings = [] +fs_permissions = [ + { access = "write", path = "./reports" }, + { access = "read", path = "./out" }, + { access = "read", path = "./config" }, ] -fs_permissions = [{access = "write", path = "./reports"}, {access = "read", path = "./out" }, {access = "read", path = "./config"}] ffi = true [fuzz] @@ -25,7 +28,7 @@ avalanche = "${RPC_AVALANCHE}" polygon = "${RPC_POLYGON}" arbitrum = "${RPC_ARBITRUM}" fantom = "${RPC_FANTOM}" -scroll= "${RPC_SCROLL}" +scroll = "${RPC_SCROLL}" celo = "${RPC_CELO}" fantom_testnet = "${RPC_FANTOM_TESTNET}" harmony = "${RPC_HARMONY}" @@ -38,19 +41,19 @@ gnosis = "${RPC_GNOSIS}" base = "${RPC_BASE}" [etherscan] -mainnet={key="${ETHERSCAN_API_KEY_MAINNET}",chainId=1} -optimism={key="${ETHERSCAN_API_KEY_OPTIMISM}",chainId=10} -avalanche={key="${ETHERSCAN_API_KEY_AVALANCHE}",chainId=43114} -polygon={key="${ETHERSCAN_API_KEY_POLYGON}",chainId=137} -arbitrum={key="${ETHERSCAN_API_KEY_ARBITRUM}",chainId=42161} -fantom={key="${ETHERSCAN_API_KEY_FANTOM}",chainId=250} -scroll={key="${ETHERSCAN_API_KEY_SCROLL}",chainId=534352, url='https://api.scrollscan.com/api\?'} -celo={key="${ETHERSCAN_API_KEY_CELO}",chainId=42220} -sepolia={key="${ETHERSCAN_API_KEY_MAINNET}",chainId=11155111} -mumbai={key="${ETHERSCAN_API_KEY_POLYGON}",chainId=80001} -amoy={key="${ETHERSCAN_API_KEY_POLYGON}",chainId=80002} -bnb_testnet={key="${ETHERSCAN_API_KEY_BNB}",chainId=97,url='https://api-testnet.bscscan.com/api'} -bnb={key="${ETHERSCAN_API_KEY_BNB}",chainId=56,url='https://api.bscscan.com/api'} -base={key="${ETHERSCAN_API_KEY_BASE}",chain=8453} -gnosis={key="${ETHERSCAN_API_KEY_GNOSIS}",chainId=100} +mainnet = { key = "${ETHERSCAN_API_KEY_MAINNET}", chainId = 1 } +optimism = { key = "${ETHERSCAN_API_KEY_OPTIMISM}", chainId = 10 } +avalanche = { key = "${ETHERSCAN_API_KEY_AVALANCHE}", chainId = 43114 } +polygon = { key = "${ETHERSCAN_API_KEY_POLYGON}", chainId = 137 } +arbitrum = { key = "${ETHERSCAN_API_KEY_ARBITRUM}", chainId = 42161 } +fantom = { key = "${ETHERSCAN_API_KEY_FANTOM}", chainId = 250 } +scroll = { key = "${ETHERSCAN_API_KEY_SCROLL}", chainId = 534352, url = 'https://api.scrollscan.com/api\?' } +celo = { key = "${ETHERSCAN_API_KEY_CELO}", chainId = 42220 } +sepolia = { key = "${ETHERSCAN_API_KEY_MAINNET}", chainId = 11155111 } +mumbai = { key = "${ETHERSCAN_API_KEY_POLYGON}", chainId = 80001 } +amoy = { key = "${ETHERSCAN_API_KEY_POLYGON}", chainId = 80002 } +bnb_testnet = { key = "${ETHERSCAN_API_KEY_BNB}", chainId = 97, url = 'https://api-testnet.bscscan.com/api' } +bnb = { key = "${ETHERSCAN_API_KEY_BNB}", chainId = 56, url = 'https://api.bscscan.com/api' } +base = { key = "${ETHERSCAN_API_KEY_BASE}", chain = 8453 } +gnosis = { key = "${ETHERSCAN_API_KEY_GNOSIS}", chainId = 100 } # See more config options https://github.com/gakonst/foundry/tree/master/config diff --git a/src/periphery/contracts/static-a-token/DeprecationGap.sol b/src/periphery/contracts/static-a-token/DeprecationGap.sol new file mode 100644 index 00000000..c3b4b47b --- /dev/null +++ b/src/periphery/contracts/static-a-token/DeprecationGap.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.10; + +/** + * This contract adds a single slot gap + * The slot is required to account for the now deprecated Initializable. + * The new version of Initializable uses erc7201, so it no longer occupies the first slot. + * https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/proxy/utils/Initializable.sol#L60 + */ +contract DeprecationGap { + uint256 internal __deprecated_initializable_gap; +} diff --git a/src/periphery/contracts/static-a-token/StaticATokenFactory.sol b/src/periphery/contracts/static-a-token/StaticATokenFactory.sol index c48e339a..4e0f8bd0 100644 --- a/src/periphery/contracts/static-a-token/StaticATokenFactory.sol +++ b/src/periphery/contracts/static-a-token/StaticATokenFactory.sol @@ -17,7 +17,7 @@ import {IStaticATokenFactory} from './interfaces/IStaticATokenFactory.sol'; */ contract StaticATokenFactory is Initializable, IStaticATokenFactory { IPool public immutable POOL; - address public immutable ADMIN; + address public immutable PROXY_ADMIN; ITransparentProxyFactory public immutable TRANSPARENT_PROXY_FACTORY; address public immutable STATIC_A_TOKEN_IMPL; @@ -33,7 +33,7 @@ contract StaticATokenFactory is Initializable, IStaticATokenFactory { address staticATokenImpl ) { POOL = pool; - ADMIN = proxyAdmin; + PROXY_ADMIN = proxyAdmin; TRANSPARENT_PROXY_FACTORY = transparentProxyFactory; STATIC_A_TOKEN_IMPL = staticATokenImpl; } @@ -54,7 +54,7 @@ contract StaticATokenFactory is Initializable, IStaticATokenFactory { ); address staticAToken = TRANSPARENT_PROXY_FACTORY.createDeterministic( STATIC_A_TOKEN_IMPL, - ADMIN, + PROXY_ADMIN, abi.encodeWithSelector( StaticATokenLM.initialize.selector, reserveData.aTokenAddress, @@ -63,6 +63,7 @@ contract StaticATokenFactory is Initializable, IStaticATokenFactory { ), bytes32(uint256(uint160(underlyings[i]))) ); + _underlyingToStaticAToken[underlyings[i]] = staticAToken; staticATokens[i] = staticAToken; _staticATokens.push(staticAToken); diff --git a/src/periphery/contracts/static-a-token/StaticATokenLM.sol b/src/periphery/contracts/static-a-token/StaticATokenLM.sol index 508d5a2e..5ca0fb29 100644 --- a/src/periphery/contracts/static-a-token/StaticATokenLM.sol +++ b/src/periphery/contracts/static-a-token/StaticATokenLM.sol @@ -3,11 +3,11 @@ pragma solidity ^0.8.10; import {IPool} from '../../../core/contracts/interfaces/IPool.sol'; import {DataTypes, ReserveConfiguration} from '../../../core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol'; -import {IRewardsController} from '../rewards/interfaces/IRewardsController.sol'; import {WadRayMath} from '../../../core/contracts/protocol/libraries/math/WadRayMath.sol'; import {MathUtils} from '../../../core/contracts/protocol/libraries/math/MathUtils.sol'; +import {IACLManager} from '../../../core/contracts/interfaces/IACLManager.sol'; +import {IRewardsController} from '../rewards/interfaces/IRewardsController.sol'; import {SafeCast} from 'solidity-utils/contracts/oz-common/SafeCast.sol'; -import {Initializable} from 'solidity-utils/contracts/transparent-proxy/Initializable.sol'; import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; import {IERC20Metadata} from 'solidity-utils/contracts/oz-common/interfaces/IERC20Metadata.sol'; import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; @@ -21,6 +21,8 @@ import {IInitializableStaticATokenLM} from './interfaces/IInitializableStaticATo import {StaticATokenErrors} from './StaticATokenErrors.sol'; import {RayMathExplicitRounding, Rounding} from '../libraries/RayMathExplicitRounding.sol'; import {IERC4626} from './interfaces/IERC4626.sol'; +import {PausableUpgradeable} from 'openzeppelin-contracts-upgradeable/contracts/utils/PausableUpgradeable.sol'; +import {DeprecationGap} from './DeprecationGap.sol'; /** * @title StaticATokenLM @@ -30,10 +32,11 @@ import {IERC4626} from './interfaces/IERC4626.sol'; * @author BGD labs */ contract StaticATokenLM is - Initializable, + DeprecationGap, ERC20('STATIC__aToken_IMPL', 'STATIC__aToken_IMPL', 18), IStaticATokenLM, - Rescuable + Rescuable, + PausableUpgradeable { using SafeERC20 for IERC20; using SafeCast for uint256; @@ -61,10 +64,20 @@ contract StaticATokenLM is mapping(address => mapping(address => UserRewardsData)) internal _userRewardsData; constructor(IPool pool, IRewardsController rewardsController) { + _disableInitializers(); POOL = pool; INCENTIVES_CONTROLLER = rewardsController; } + modifier onlyPauseGuardian() { + if (!canPause(msg.sender)) revert OnlyPauseGuardian(msg.sender); + _; + } + + function canPause(address actor) public view returns (bool) { + return IACLManager(POOL.ADDRESSES_PROVIDER().getACLManager()).isEmergencyAdmin(actor); + } + ///@inheritdoc IInitializableStaticATokenLM function initialize( address newAToken, @@ -93,6 +106,12 @@ contract StaticATokenLM is return POOL.ADDRESSES_PROVIDER().getACLAdmin(); } + ///@inheritdoc IStaticATokenLM + function setPaused(bool paused) external onlyPauseGuardian { + if (paused) _pause(); + else _unpause(); + } + ///@inheritdoc IStaticATokenLM function refreshRewardTokens() public override { address[] memory rewards = INCENTIVES_CONTROLLER.getRewardsByAsset(address(_aToken)); @@ -540,7 +559,7 @@ contract StaticATokenLM is * @param from The address of the sender of tokens * @param to The address of the receiver of tokens */ - function _beforeTokenTransfer(address from, address to, uint256) internal override { + function _beforeTokenTransfer(address from, address to, uint256) internal override whenNotPaused { for (uint256 i = 0; i < _rewardTokens.length; i++) { address rewardToken = address(_rewardTokens[i]); uint256 rewardsIndex = getCurrentRewardsIndex(rewardToken); @@ -633,7 +652,7 @@ contract StaticATokenLM is address onBehalfOf, address receiver, address[] memory rewards - ) internal { + ) internal whenNotPaused { for (uint256 i = 0; i < rewards.length; i++) { if (address(rewards[i]) == address(0)) { continue; diff --git a/src/periphery/contracts/static-a-token/interfaces/IStaticATokenLM.sol b/src/periphery/contracts/static-a-token/interfaces/IStaticATokenLM.sol index a5afeace..18edaeb7 100644 --- a/src/periphery/contracts/static-a-token/interfaces/IStaticATokenLM.sol +++ b/src/periphery/contracts/static-a-token/interfaces/IStaticATokenLM.sol @@ -30,6 +30,8 @@ interface IStaticATokenLM is IInitializableStaticATokenLM, IERC4626 { uint248 lastUpdatedIndex; } + error OnlyPauseGuardian(address caller); + event RewardTokenRegistered(address indexed reward, uint256 startIndex); /** @@ -207,7 +209,14 @@ interface IStaticATokenLM is IInitializableStaticATokenLM, IERC4626 { /** * @notice Checks if the passed token is a registered reward. + * @param reward The reward to claim * @return bool signaling if token is a registered reward. */ function isRegisteredRewardToken(address reward) external view returns (bool); + + /** + * @notice Pauses/unpauses all system's operations + * @param paused boolean determining if the token should be paused or unpaused + */ + function setPaused(bool paused) external; } diff --git a/tests/periphery/static-a-token/Pausable.t.sol b/tests/periphery/static-a-token/Pausable.t.sol new file mode 100644 index 00000000..966a33ac --- /dev/null +++ b/tests/periphery/static-a-token/Pausable.t.sol @@ -0,0 +1,125 @@ +// 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 {AToken} from '../../../src/core/contracts/protocol/tokenization/AToken.sol'; +import {DataTypes} from '../../../src/core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol'; +import {IERC20, IERC20Metadata} from '../../../src/periphery/contracts/static-a-token/StaticATokenLM.sol'; +import {RayMathExplicitRounding} from '../../../src/periphery/contracts/libraries/RayMathExplicitRounding.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 {IStaticATokenLM} from '../../../src/periphery/contracts/static-a-token/interfaces/IStaticATokenLM.sol'; +import {SigUtils} from '../../utils/SigUtils.sol'; +import {BaseTest, TestnetERC20} from './TestBase.sol'; + +contract Pausable is BaseTest { + using RayMathExplicitRounding for uint256; + + function test_setPaused_shouldRevertForInvalidCaller(address actor) external { + vm.assume(actor != poolAdmin && actor != proxyAdmin); + vm.expectRevert(abi.encodeWithSelector(IStaticATokenLM.OnlyPauseGuardian.selector, actor)); + _setPaused(actor, true); + } + + function test_setPaused_shouldSuceedForOwner() 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, 0, true); + } + + 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/StaticATokenLM.t.sol b/tests/periphery/static-a-token/StaticATokenLM.t.sol index 543a0c21..6f2ddaf4 100644 --- a/tests/periphery/static-a-token/StaticATokenLM.t.sol +++ b/tests/periphery/static-a-token/StaticATokenLM.t.sol @@ -1,14 +1,12 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.10; +import {IRescuable} from 'solidity-utils/contracts/utils/Rescuable.sol'; +import {Initializable} from 'openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol'; import {AToken} from '../../../src/core/contracts/protocol/tokenization/AToken.sol'; import {DataTypes} from '../../../src/core/contracts/protocol/libraries/configuration/ReserveConfiguration.sol'; import {IERC20, IERC20Metadata} from '../../../src/periphery/contracts/static-a-token/StaticATokenLM.sol'; import {RayMathExplicitRounding} from '../../../src/periphery/contracts/libraries/RayMathExplicitRounding.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 {IStaticATokenLM} from '../../../src/periphery/contracts/static-a-token/interfaces/IStaticATokenLM.sol'; import {SigUtils} from '../../utils/SigUtils.sol'; import {BaseTest, TestnetERC20} from './TestBase.sol'; @@ -16,8 +14,6 @@ import {BaseTest, TestnetERC20} from './TestBase.sol'; contract StaticATokenLMTest is BaseTest { using RayMathExplicitRounding for uint256; - address public constant EMISSION_ADMIN = address(25); - function setUp() public override { super.setUp(); @@ -29,8 +25,8 @@ contract StaticATokenLMTest is BaseTest { function test_initializeShouldRevert() public { address impl = factory.STATIC_A_TOKEN_IMPL(); - vm.expectRevert(); - IStaticATokenLM(impl).initialize(0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8, 'hey', 'ho'); + vm.expectRevert(Initializable.InvalidInitialization.selector); + IStaticATokenLM(impl).initialize(A_TOKEN, 'hey', 'ho'); } function test_getters() public view { @@ -556,42 +552,24 @@ contract StaticATokenLMTest is BaseTest { staticATokenLM.permit(permit.owner, permit.spender, permit.value, permit.deadline, v, r, s); } - function _configureLM() internal { - PullRewardsTransferStrategy strat = new PullRewardsTransferStrategy( - report.rewardsControllerProxy, - EMISSION_ADMIN, - EMISSION_ADMIN + 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); - 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)) + IRescuable(address(staticATokenLM)).emergencyTokenTransfer( + tokenList.usdx, + address(this), + 1 ether ); - - vm.prank(EMISSION_ADMIN); - contracts.emissionManager.configureAssets(config); - - staticATokenLM.refreshRewardTokens(); } function _openSupplyAndBorrowPositions() internal { diff --git a/tests/periphery/static-a-token/TestBase.sol b/tests/periphery/static-a-token/TestBase.sol index 058ad713..b20aab9c 100644 --- a/tests/periphery/static-a-token/TestBase.sol +++ b/tests/periphery/static-a-token/TestBase.sol @@ -2,6 +2,10 @@ pragma solidity ^0.8.10; import {IRewardsController} from '../../../src/periphery/contracts/rewards/interfaces/IRewardsController.sol'; +import {RewardsDataTypes} from '../../../src/periphery/contracts/rewards/libraries/RewardsDataTypes.sol'; +import {PullRewardsTransferStrategy} from '../../../src/periphery/contracts/rewards/transfer-strategies/PullRewardsTransferStrategy.sol'; +import {ITransferStrategyBase} from '../../../src/periphery/contracts/rewards/interfaces/ITransferStrategyBase.sol'; +import {IEACAggregatorProxy} from '../../../src/periphery/contracts/misc/interfaces/IEACAggregatorProxy.sol'; import {TransparentUpgradeableProxy} from 'solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol'; import {ITransparentProxyFactory} from 'solidity-utils/contracts/transparent-proxy/TransparentProxyFactory.sol'; import {IPool} from '../../../src/core/contracts/interfaces/IPool.sol'; @@ -13,6 +17,7 @@ import {DataTypes} from '../../../src/core/contracts/protocol/libraries/configur abstract contract BaseTest is TestnetProcedures { address constant OWNER = address(1234); + address public constant EMISSION_ADMIN = address(25); address public user; address public user1; @@ -61,6 +66,44 @@ abstract contract BaseTest is TestnetProcedures { staticATokenLM = StaticATokenLM(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); }