Skip to content

Commit 13c0567

Browse files
Joafaiefecarranza
andauthored
stkABPT Emission Update (#930)
* Emissions update * Emission update fixes * Fixed md file, added allowances updates, fixed tests * fixed allowance update and tests * fixed tests * fixed unused import * fixed naming and md file * 'increment distribution end' * feat: clean up tests --------- Co-authored-by: efecarranza <[email protected]>
1 parent 4e35e81 commit 13c0567

File tree

6 files changed

+346
-0
lines changed

6 files changed

+346
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
## Raw diff
2+
3+
```json
4+
{
5+
"raw": {
6+
"0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9": {
7+
"label": "AaveV2Ethereum.ASSETS.AAVE.UNDERLYING, AaveV2EthereumArc.ASSETS.AAVE.UNDERLYING, AaveV3Ethereum.ASSETS.AAVE.UNDERLYING",
8+
"contract": "lib/aave-umbrella/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy",
9+
"balanceDiff": null,
10+
"nonceDiff": null,
11+
"stateDiff": {
12+
"0x495a957a79652a5e0fd2a854635df04aeb86fb136127eba37ff9c7b5a260662d": {
13+
"previousValue": "0x0000000000000000000000000000000000000000000004f79c41084d5297e515",
14+
"newValue": "0x0000000000000000000000000000000000000000000006131d622ea76acf3ed5"
15+
}
16+
}
17+
},
18+
"0x9eda81c21c273a82be9bbc19b6a6182212068101": {
19+
"label": "AaveSafetyModule.STK_AAVE_WSTETH_BPTV2",
20+
"contract": "lib/aave-umbrella/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy",
21+
"balanceDiff": null,
22+
"nonceDiff": null,
23+
"stateDiff": {
24+
"0x0000000000000000000000000000000000000000000000000000000000000009": {
25+
"previousValue": "0x000000000000000000000000000000000000000000000000000000006989e13f",
26+
"newValue": "0x000000000000000000000000000000000000000000000000000000006a00883f"
27+
},
28+
"0xfc8aee8416b692e3581ecb0e469dfb43f6eb58b5c6dba1f8235e29609b3cb8a0": {
29+
"previousValue": "0x0000000000000000000000006953fc03000000000000000000055873e297b4bd",
30+
"newValue": "0x00000000000000000000000069542a5f00000000000000000001a50ff6f39a12"
31+
},
32+
"0xfc8aee8416b692e3581ecb0e469dfb43f6eb58b5c6dba1f8235e29609b3cb8a1": {
33+
"previousValue": "0x00000000000000000000000000000000000000000000000004e4c159b1db552a",
34+
"newValue": "0x00000000000000000000000000000000000000000000000004e4f9cee86a41d8"
35+
}
36+
}
37+
},
38+
"0xdabad81af85554e9ae636395611c58f7ec1aaec5": {
39+
"label": "GovernanceV3Ethereum.PAYLOADS_CONTROLLER",
40+
"contract": "lib/aave-umbrella/lib/aave-v3-origin/lib/solidity-utils/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy",
41+
"balanceDiff": null,
42+
"nonceDiff": null,
43+
"stateDiff": {
44+
"0x60969e1cbcb55c22c415d8d4f95d877c6b7c4a416eeb50448d660a445fb3775f": {
45+
"previousValue": "0x0069542a5e000000000002000000000000000000000000000000000000000000",
46+
"newValue": "0x0069542a5e000000000003000000000000000000000000000000000000000000"
47+
},
48+
"0x60969e1cbcb55c22c415d8d4f95d877c6b7c4a416eeb50448d660a445fb37760": {
49+
"previousValue": "0x000000000000000000093a8000000000000069824edf00000000000000000000",
50+
"newValue": "0x000000000000000000093a8000000000000069824edf00000000000069542a5f"
51+
}
52+
}
53+
}
54+
}
55+
}
56+
```
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol';
5+
import {IStakeToken} from 'aave-helpers/lib/aave-address-book/src/common/IStakeToken.sol';
6+
import {AaveSafetyModule} from 'aave-address-book/AaveSafetyModule.sol';
7+
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
8+
import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol';
9+
import {IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';
10+
11+
/**
12+
* @title stkABPT Emission Update
13+
* @author @TokenLogic
14+
* - Snapshot: https://snapshot.box/#/s:aavedao.eth/proposal/0x6712b1677068d2d316af699757057a0c8c03e0ff0693c12aacc381d294c419a4
15+
* - Discussion: https://governance.aave.com/t/arfc-safety-module-umbrella-emission-update/23103/9
16+
*/
17+
contract AaveV3Ethereum_EmissionUpdate_20251219 is IProposalGenericExecutor {
18+
uint128 public constant AAVE_EMISSION_PER_SECOND_STK_BPT = uint128(40 ether) / 1 days;
19+
20+
function execute() external override {
21+
IStakeToken.AssetConfigInput[] memory config = new IStakeToken.AssetConfigInput[](1);
22+
config[0] = IStakeToken.AssetConfigInput({
23+
emissionPerSecond: AAVE_EMISSION_PER_SECOND_STK_BPT,
24+
totalStaked: 0,
25+
underlyingAsset: AaveSafetyModule.STK_AAVE_WSTETH_BPTV2
26+
});
27+
28+
IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2).configureAssets(config);
29+
30+
uint256 newDistributionEnd = IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2)
31+
.distributionEnd() + 90 days;
32+
IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2).setDistributionEnd(newDistributionEnd);
33+
34+
uint256 currentAllowance = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).allowance(
35+
MiscEthereum.ECOSYSTEM_RESERVE,
36+
AaveSafetyModule.STK_AAVE_WSTETH_BPTV2
37+
);
38+
39+
uint256 updatedAllowance = currentAllowance +
40+
(uint256(AAVE_EMISSION_PER_SECOND_STK_BPT) * (newDistributionEnd - block.timestamp));
41+
42+
MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.approve(
43+
MiscEthereum.ECOSYSTEM_RESERVE,
44+
AaveV3EthereumAssets.AAVE_UNDERLYING,
45+
AaveSafetyModule.STK_AAVE_WSTETH_BPTV2,
46+
0
47+
);
48+
49+
MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.approve(
50+
MiscEthereum.ECOSYSTEM_RESERVE,
51+
AaveV3EthereumAssets.AAVE_UNDERLYING,
52+
AaveSafetyModule.STK_AAVE_WSTETH_BPTV2,
53+
updatedAllowance
54+
);
55+
}
56+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol';
5+
import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol';
6+
import {AaveV3Ethereum_EmissionUpdate_20251219} from './AaveV3Ethereum_EmissionUpdate_20251219.sol';
7+
import {IStakeToken} from 'aave-address-book/common/IStakeToken.sol';
8+
import {AaveSafetyModule} from 'aave-address-book/AaveSafetyModule.sol';
9+
import {IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';
10+
import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol';
11+
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
12+
13+
/**
14+
* @dev Test for AaveV3Ethereum_EmissionUpdate_20251219
15+
* command:
16+
* FOUNDRY_PROFILE=test forge test --match-path=src/20251219_AaveV3Ethereum_EmissionUpdate/AaveV3Ethereum_EmissionUpdate_20251219.t.sol -vv
17+
*/
18+
contract AaveV3Ethereum_EmissionUpdate_20251219_Test is ProtocolV3TestBase {
19+
AaveV3Ethereum_EmissionUpdate_20251219 internal proposal;
20+
21+
function setUp() public {
22+
vm.createSelectFork(vm.rpcUrl('mainnet'), 24127590);
23+
proposal = new AaveV3Ethereum_EmissionUpdate_20251219();
24+
}
25+
26+
function test_defaultProposalExecution() public {
27+
defaultTest('AaveV3Ethereum_EmissionUpdate_20251219', AaveV3Ethereum.POOL, address(proposal));
28+
}
29+
30+
function test_checkConfig() public {
31+
(uint128 emissionPerSecondBefore, , ) = IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2)
32+
.assets(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2);
33+
34+
assertEq(
35+
emissionPerSecondBefore,
36+
uint128(130 ether) / 1 days,
37+
'unexpected stkABPT emission rate before'
38+
);
39+
40+
executePayload(vm, address(proposal));
41+
42+
(uint128 emissionPerSecondAfter, , ) = IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2)
43+
.assets(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2);
44+
45+
assertEq(
46+
emissionPerSecondAfter,
47+
proposal.AAVE_EMISSION_PER_SECOND_STK_BPT(),
48+
'unexpected stkABPT emission rate after'
49+
);
50+
}
51+
52+
function test_distributionEnd() public {
53+
uint256 endTimestampBefore = IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2)
54+
.distributionEnd();
55+
56+
assertGt(
57+
endTimestampBefore,
58+
block.timestamp,
59+
'New distribution duration is lower than current timestamp'
60+
);
61+
62+
executePayload(vm, address(proposal));
63+
64+
uint256 endTimestampAfter = IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2)
65+
.distributionEnd();
66+
67+
assertEq(endTimestampBefore + 90 days, endTimestampAfter, 'New distribution duration differs');
68+
}
69+
70+
function test_checkAllowance() public {
71+
uint256 allowanceBefore = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).allowance(
72+
MiscEthereum.ECOSYSTEM_RESERVE,
73+
AaveSafetyModule.STK_AAVE_WSTETH_BPTV2
74+
);
75+
76+
uint256 newDistributionEnd = IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2)
77+
.distributionEnd() + 90 days;
78+
79+
uint256 secondsRemaining = newDistributionEnd - block.timestamp;
80+
81+
uint256 expectedAllowance = allowanceBefore +
82+
(uint256(proposal.AAVE_EMISSION_PER_SECOND_STK_BPT()) * secondsRemaining);
83+
84+
executePayload(vm, address(proposal));
85+
86+
assertEq(
87+
IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2).distributionEnd(),
88+
newDistributionEnd,
89+
'unexpected distributionEnd after'
90+
);
91+
92+
uint256 allowanceAfter = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).allowance(
93+
MiscEthereum.ECOSYSTEM_RESERVE,
94+
AaveSafetyModule.STK_AAVE_WSTETH_BPTV2
95+
);
96+
97+
assertEq(allowanceAfter, expectedAllowance, 'unexpected allowance after');
98+
assertGe(allowanceAfter, allowanceBefore, 'allowance after should be >= allowance before');
99+
}
100+
101+
function test_checkRewards_stkBPT() public {
102+
address stakedToken = 0x3de27EFa2F1AA663Ae5D458857e731c129069F29;
103+
address staker = 0xce88686553686DA562CE7Cea497CE749DA109f9F;
104+
uint256 rewardsPerDay = 40e18;
105+
106+
executePayload(vm, address(proposal));
107+
108+
vm.startPrank(staker);
109+
IERC20(stakedToken).approve(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2, 1 ether);
110+
IERC20(stakedToken).balanceOf(staker);
111+
IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2).stake(staker, 1 ether);
112+
vm.stopPrank();
113+
114+
vm.warp(block.timestamp + 1 days);
115+
116+
uint256 rewardsBalance = IStakeToken(AaveSafetyModule.STK_AAVE_WSTETH_BPTV2)
117+
.getTotalRewardsBalance(staker);
118+
119+
assertTrue(rewardsBalance > 0 && rewardsBalance <= rewardsPerDay);
120+
121+
vm.stopPrank();
122+
}
123+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: "stkABPT Emission Update"
3+
author: "@TokenLogic"
4+
discussions: "https://governance.aave.com/t/arfc-safety-module-umbrella-emission-update/23103/9"
5+
---
6+
7+
## Simple Summary
8+
9+
Reduce AAVE emissions for the legacy Safety Module **stkABPT** (stkAAVE/wstETH BPTv2) from **130 AAVE/day** to **40 AAVE/day**.
10+
11+
## Motivation
12+
13+
This change follows the proposal’s direction to **phase out stkABPT incentives** (as emissions will trend to **0**) by reducing incentives on the legacy Safety Module. Reducing stkABPT emissions lowers ongoing incentive spend while supporting the transition towards the Umbrella Safety Modules and improved capital efficiency.
14+
15+
**Note:** **stkAAVE is not being phased out**; it will be **repurposed** into a **no-risk staking (dividend yield) contract**.
16+
17+
## Specification
18+
19+
- **Target module:** Legacy Safety Module stake token **stkABPT** (`AaveSafetyModule.STK_AAVE_WSTETH_BPTV2`).
20+
- **Change:** Update AAVE rewards emission rate from **130 AAVE/day → 40 AAVE/day** by setting:
21+
- `emissionPerSecond = 40 AAVE / day` (expressed as `uint128(40 ether) / 1 days`)
22+
- **Distribution end:** extend rewards distribution by **90 days**: `newDistributionEnd = distributionEnd + 90 days`.
23+
- **Allowance adjustment (funding):** Reset and set the AAVE allowance from the **Ecosystem Reserve** to the stake token to cover **already accrued** + **future emissions**:
24+
- `allowance = currentAllowance + emissionPerSecond * (newDistributionEnd - block.timestamp)`
25+
- **No other changes:** This payload does **not** modify stkAAVE emissions, cooldown parameters, slashing parameters, or any Umbrella module emissions.
26+
27+
## References
28+
29+
- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20251219_AaveV3Ethereum_EmissionUpdate/AaveV3Ethereum_EmissionUpdate_20251219.sol)
30+
- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20251219_AaveV3Ethereum_EmissionUpdate/AaveV3Ethereum_EmissionUpdate_20251219.t.sol)
31+
- [Snapshot](https://snapshot.box/#/s:aavedao.eth/proposal/0x6712b1677068d2d316af699757057a0c8c03e0ff0693c12aacc381d294c419a4)
32+
- [Discussion](https://governance.aave.com/t/arfc-safety-module-umbrella-emission-update/23103/9)
33+
34+
## Copyright
35+
36+
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import {GovV3Helpers, IPayloadsControllerCore, PayloadsControllerUtils} from 'aave-helpers/src/GovV3Helpers.sol';
5+
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
6+
7+
import {EthereumScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol';
8+
import {AaveV3Ethereum_EmissionUpdate_20251219} from './AaveV3Ethereum_EmissionUpdate_20251219.sol';
9+
10+
/**
11+
* @dev Deploy Ethereum
12+
* deploy-command: make deploy-ledger contract=src/20251219_AaveV3Ethereum_EmissionUpdate/EmissionUpdate_20251219.s.sol:DeployEthereum chain=mainnet
13+
* verify-command: FOUNDRY_PROFILE=deploy npx catapulta-verify -b broadcast/EmissionUpdate_20251219.s.sol/1/run-latest.json
14+
*/
15+
contract DeployEthereum is EthereumScript {
16+
function run() external broadcast {
17+
// deploy payloads
18+
address payload0 = GovV3Helpers.deployDeterministic(
19+
type(AaveV3Ethereum_EmissionUpdate_20251219).creationCode
20+
);
21+
22+
// compose action
23+
IPayloadsControllerCore.ExecutionAction[]
24+
memory actions = new IPayloadsControllerCore.ExecutionAction[](1);
25+
actions[0] = GovV3Helpers.buildAction(payload0);
26+
27+
// register action at payloadsController
28+
GovV3Helpers.createPayload(actions);
29+
}
30+
}
31+
32+
/**
33+
* @dev Create Proposal
34+
* command: make deploy-ledger contract=src/20251219_AaveV3Ethereum_EmissionUpdate/EmissionUpdate_20251219.s.sol:CreateProposal chain=mainnet
35+
*/
36+
contract CreateProposal is EthereumScript {
37+
function run() external {
38+
// create payloads
39+
PayloadsControllerUtils.Payload[] memory payloads = new PayloadsControllerUtils.Payload[](1);
40+
41+
// compose actions for validation
42+
{
43+
IPayloadsControllerCore.ExecutionAction[]
44+
memory actionsEthereum = new IPayloadsControllerCore.ExecutionAction[](1);
45+
actionsEthereum[0] = GovV3Helpers.buildAction(
46+
type(AaveV3Ethereum_EmissionUpdate_20251219).creationCode
47+
);
48+
payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum);
49+
}
50+
51+
// create proposal
52+
vm.startBroadcast();
53+
GovV3Helpers.createProposal(
54+
vm,
55+
payloads,
56+
GovernanceV3Ethereum.VOTING_PORTAL_ETH_AVAX,
57+
GovV3Helpers.ipfsHashFile(vm, 'src/20251219_AaveV3Ethereum_EmissionUpdate/EmissionUpdate.md')
58+
);
59+
}
60+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {ConfigFile} from '../../generator/types';
2+
export const config: ConfigFile = {
3+
rootOptions: {
4+
pools: ['AaveV3Ethereum'],
5+
title: 'stkABPT Emission Update',
6+
shortName: 'EmissionUpdate',
7+
date: '20251219',
8+
author: '@TokenLogic',
9+
discussion: 'https://governance.aave.com/t/arfc-safety-module-umbrella-emission-update/23103/9',
10+
snapshot:
11+
'https://snapshot.box/#/s:aavedao.eth/proposal/0x6712b1677068d2d316af699757057a0c8c03e0ff0693c12aacc381d294c419a4',
12+
votingNetwork: 'AVALANCHE',
13+
},
14+
poolOptions: {AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 24044429}}},
15+
};

0 commit comments

Comments
 (0)