diff --git a/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.sol b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.sol new file mode 100644 index 000000000..d0fb54f43 --- /dev/null +++ b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {AaveSafetyModule} from 'aave-address-book/AaveSafetyModule.sol'; +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; + +import {IStakeToken} from 'aave-address-book/common/IStakeToken.sol'; + +/** + * @title Safety Module stkAAVE - Re-enable Rewards + * @author @karpatkey_TokenLogic + * - Snapshot: Direct-to-AIP + * - Discussion: https://governance.aave.com/t/arfc-amend-safety-module-emissions/16640/13 + */ +contract AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106 is IProposalGenericExecutor { + uint256 public constant DISTRIBUTION_DURATION = 180 days; + uint128 public constant AAVE_EMISSION_PER_SECOND_STK_AAVE = uint128(360e18) / 1 days; // 360 AAVE per day + + function execute() external { + uint256 remainingAllowance = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).allowance( + address(MiscEthereum.ECOSYSTEM_RESERVE), + AaveSafetyModule.STK_AAVE + ); + + // Approval needs to be reset in order to increase it + MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.approve( + MiscEthereum.ECOSYSTEM_RESERVE, + AaveV3EthereumAssets.AAVE_UNDERLYING, + AaveSafetyModule.STK_AAVE, + 0 + ); + + MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.approve( + MiscEthereum.ECOSYSTEM_RESERVE, + AaveV3EthereumAssets.AAVE_UNDERLYING, + AaveSafetyModule.STK_AAVE, + remainingAllowance + (AAVE_EMISSION_PER_SECOND_STK_AAVE * DISTRIBUTION_DURATION) + ); + } +} diff --git a/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.t.sol b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.t.sol new file mode 100644 index 000000000..474616977 --- /dev/null +++ b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.t.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {AaveSafetyModule} from 'aave-address-book/AaveSafetyModule.sol'; +import {IStakeToken} from 'aave-address-book/common/IStakeToken.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; + +import {AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106} from './AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.sol'; + +/** + * @dev Test for AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.t.sol -vv + */ +contract AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106_Test is ProtocolV3TestBase { + AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21128772); + proposal = new AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106(); + } + + function test_checkConfig() public { + (uint128 emissionPerSecondBeforeStkAAVE, , ) = IStakeToken(AaveSafetyModule.STK_AAVE).assets( + AaveSafetyModule.STK_AAVE + ); + + assertEq( + emissionPerSecondBeforeStkAAVE, + proposal.AAVE_EMISSION_PER_SECOND_STK_AAVE(), + 'emissions before not equal stkAAVE' + ); + + executePayload(vm, address(proposal)); + + (uint128 emissionPerSecondAfterStkAAVE, , ) = IStakeToken(AaveSafetyModule.STK_AAVE).assets( + AaveSafetyModule.STK_AAVE + ); + + assertEq( + emissionPerSecondAfterStkAAVE, + proposal.AAVE_EMISSION_PER_SECOND_STK_AAVE(), + 'emissions after not equal stkAAVE' + ); + assertApproxEqAbs( + emissionPerSecondAfterStkAAVE, + emissionPerSecondBeforeStkAAVE, + 1, + 'stkAAVE emissions not same as previous' + ); + } + + function test_checkRewards_stkAAVE() public { + address staker = 0x5a801a9418D036fD453078c3ADCB761fdc5Ae695; + uint256 rewardsPerDay = proposal.AAVE_EMISSION_PER_SECOND_STK_AAVE(); + + executePayload(vm, address(proposal)); + + vm.startPrank(staker); + IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).approve(AaveSafetyModule.STK_AAVE, 1e18); + + IStakeToken(AaveSafetyModule.STK_AAVE).stake(staker, 1e18); + + vm.warp(block.timestamp + 1 days); + + uint256 rewardsBalance = IStakeToken(AaveSafetyModule.STK_AAVE).getTotalRewardsBalance(staker); + + assertTrue(rewardsBalance > 0 && rewardsBalance <= rewardsPerDay); + + vm.stopPrank(); + } +} diff --git a/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards.md b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards.md new file mode 100644 index 000000000..adb7606ab --- /dev/null +++ b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards.md @@ -0,0 +1,35 @@ +--- +title: "Safety Module stkAAVE - Re-enable Rewards" +author: "@karpatkey_TokenLogic" +discussions: "https://governance.aave.com/t/arfc-amend-safety-module-emissions/16640/13" +snapshot: "Direct-to-AIP" +--- + +## Simple Summary + +This publication proposes renewing the AAVE emissions to stkAAVE holders for the following 180 days. + +## Motivation + +With the conclusion of the recent reward cycle, this proposal aims to renew $AAVE emissions for stkAAVE holders. The stkAAVE module has proven to be a crucial mechanism as an AAVE supply sink, currently holding a TVL of $480M and accounting for 18.8% of the supply. It is recommended to maintain these emissions in anticipation of upcoming Umbrella developments, with adjustments to be made following the outcomes of the Umbrella upgrade. + +More information on stkAAVE can be found in these dashboards, [here](https://dune.com/xmc2/aave-safety-module) and [here](https://dune.com/KARTOD/AAVE-Staking). + +## Specification + +This proposal will implement the following changes to the AAVE emissions across the three SM categories: + +| Asset | Current AAVE/Day | Proposed AAVE/Day | +| ------- | ---------------- | ----------------- | +| stkAAVE | 360 | 360 | + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.t.sol) +- [Snapshot]: Direct-to-AIP +- [Discussion](https://governance.aave.com/t/arfc-amend-safety-module-emissions/16640/13) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards_20241106.s.sol b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards_20241106.s.sol new file mode 100644 index 000000000..3594948c4 --- /dev/null +++ b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards_20241106.s.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers, IPayloadsControllerCore, PayloadsControllerUtils} from 'aave-helpers/src/GovV3Helpers.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {EthereumScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; +import {AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106} from './AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards_20241106.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/SafetyModuleStkAAVEReEnableRewards_20241106.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Create Proposal + * command: make deploy-ledger contract=src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards_20241106.s.sol:CreateProposal chain=mainnet + */ +contract CreateProposal is EthereumScript { + function run() external { + // create payloads + PayloadsControllerUtils.Payload[] memory payloads = new PayloadsControllerUtils.Payload[](1); + + // compose actions for validation + IPayloadsControllerCore.ExecutionAction[] + memory actionsEthereum = new IPayloadsControllerCore.ExecutionAction[](1); + actionsEthereum[0] = GovV3Helpers.buildAction( + type(AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards_20241106).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/SafetyModuleStkAAVEReEnableRewards.md' + ) + ); + } +} diff --git a/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/config.ts b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/config.ts new file mode 100644 index 000000000..84ba8e736 --- /dev/null +++ b/src/20241106_AaveV3Ethereum_SafetyModuleStkAAVEReEnableRewards/config.ts @@ -0,0 +1,14 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3Ethereum'], + title: 'Safety Module stkAAVE - Re-enable Rewards', + shortName: 'SafetyModuleStkAAVEReEnableRewards', + date: '20241106', + author: '@karpatkey_TokenLogic', + discussion: '', + snapshot: 'Direct-to-AIP', + votingNetwork: 'POLYGON', + }, + poolOptions: {AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 21128772}}}, +};