Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: gho stewards update to v3.1 #433

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Raw diff

```json
{}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol';
import {IGhoStewardV2} from './interfaces/IGhoStewardV2.sol';
import {IGhoToken} from './interfaces/IGho.sol';
import {IGsm} from './interfaces/IGSM.sol';
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol';
import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol';

/**
* @title GHO Steward Update
* @author BGD Labs (@bgdlabs)
* - Snapshot: Direct To AIP
* - Discussion: TODO
*/
contract AaveV3Ethereum_GHOStewardUpdate_20240826 is IProposalGenericExecutor {
address public constant GHO_STEWARD = 0x1180eE41eC15Dd0accC13a1e646B3152bECFf8F6;
address public constant OLD_GHO_STEWARD = 0xb9481a29f0f996BCAc759aB201FB3844c81866c4;

function execute() external {
// Remove Risk admin role to the old steward
AaveV3Ethereum.ACL_MANAGER.removeRiskAdmin(OLD_GHO_STEWARD);

// Revoke old steward's bucket manager role
IGhoToken(MiscEthereum.GHO_TOKEN).revokeRole(
IGhoToken(MiscEthereum.GHO_TOKEN).BUCKET_MANAGER_ROLE(),
OLD_GHO_STEWARD
);

// Revoke old steward's configurator role on usdc, usdt gsm
IGsm(MiscEthereum.GSM_USDC).revokeRole(
IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(),
OLD_GHO_STEWARD
);
IGsm(MiscEthereum.GSM_USDT).revokeRole(
IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(),
OLD_GHO_STEWARD
);

// Give Risk admin role to the new steward
AaveV3Ethereum.ACL_MANAGER.addRiskAdmin(GHO_STEWARD);

// Give bucket manager role to the new steward
IGhoToken(MiscEthereum.GHO_TOKEN).grantRole(
IGhoToken(MiscEthereum.GHO_TOKEN).BUCKET_MANAGER_ROLE(),
GHO_STEWARD
);

// Give configurator role on usdc, usdt gsm to the new steward
IGsm(MiscEthereum.GSM_USDC).grantRole(
IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(),
GHO_STEWARD
);
IGsm(MiscEthereum.GSM_USDT).grantRole(
IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(),
GHO_STEWARD
);

// Whitelist all the facilitators on the stewards, including: GhoAToken, GhoFlashMinter, GSM USDC, GSM USDT
IGhoStewardV2(GHO_STEWARD).setControlledFacilitator(
IGhoToken(MiscEthereum.GHO_TOKEN).getFacilitatorsList(),
true
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IGhoStewardV2} from './interfaces/IGhoStewardV2.sol';
import {IGhoToken} from './interfaces/IGho.sol';
import {IGsmFeeStrategy} from './interfaces/IGsmFeeStrategy.sol';
import {IGsm} from './interfaces/IGSM.sol';
import {IDefaultInterestRateStrategyV2} from 'aave-v3-origin/core/contracts/interfaces/IDefaultInterestRateStrategyV2.sol';
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol';
import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol';
import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol';
import {AaveV3Ethereum_GHOStewardUpdate_20240826} from './AaveV3Ethereum_GHOStewardUpdate_20240826.sol';

/**
* @dev Test for AaveV3Ethereum_GHOStewardUpdate_20240826
* command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20240826_AaveV3Ethereum_GHOStewardUpdate/AaveV3Ethereum_GHOStewardUpdate_20240826.t.sol -vv
*/
contract AaveV3Ethereum_GHOStewardUpdate_20240826_Test is ProtocolV3TestBase {
AaveV3Ethereum_GHOStewardUpdate_20240826 internal proposal;
address public RISK_COUNCIL;

function setUp() public {
vm.createSelectFork(vm.rpcUrl('mainnet'), 20611314);
proposal = new AaveV3Ethereum_GHOStewardUpdate_20240826();
RISK_COUNCIL = IGhoStewardV2(proposal.GHO_STEWARD()).RISK_COUNCIL();
}

/**
* @dev executes the generic test suite including e2e and config snapshots
*/
function test_defaultProposalExecution() public {
defaultTest('AaveV3Ethereum_GHOStewardUpdate_20240826', AaveV3Ethereum.POOL, address(proposal));
}

function test_adminPermissions() public {
executePayload(vm, address(proposal));

// Check that the old steward has been removed from roles
assertFalse(AaveV3Ethereum.ACL_MANAGER.isRiskAdmin(proposal.OLD_GHO_STEWARD()));

assertFalse(
IGhoToken(MiscEthereum.GHO_TOKEN).hasRole(
IGhoToken(MiscEthereum.GHO_TOKEN).BUCKET_MANAGER_ROLE(),
proposal.OLD_GHO_STEWARD()
)
);

assertFalse(
IGsm(MiscEthereum.GSM_USDT).hasRole(
IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(),
proposal.OLD_GHO_STEWARD()
)
);

assertFalse(
IGsm(MiscEthereum.GSM_USDC).hasRole(
IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(),
proposal.OLD_GHO_STEWARD()
)
);

assertTrue(AaveV3Ethereum.ACL_MANAGER.isRiskAdmin(proposal.GHO_STEWARD()));
assertTrue(
IGhoToken(MiscEthereum.GHO_TOKEN).hasRole(
IGhoToken(MiscEthereum.GHO_TOKEN).BUCKET_MANAGER_ROLE(),
proposal.GHO_STEWARD()
)
);
assertTrue(
IGsm(MiscEthereum.GSM_USDT).hasRole(
IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(),
proposal.GHO_STEWARD()
)
);
assertTrue(
IGsm(MiscEthereum.GSM_USDC).hasRole(
IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(),
proposal.GHO_STEWARD()
)
);

address[] memory controlledFacilitatorsList = IGhoStewardV2(proposal.GHO_STEWARD())
.getControlledFacilitators();
address[] memory ghoFacilitatorList = IGhoToken(MiscEthereum.GHO_TOKEN).getFacilitatorsList();
assertEq(controlledFacilitatorsList.length, ghoFacilitatorList.length);

for (uint256 i = 0; i < controlledFacilitatorsList.length; i++) {
assertEq(controlledFacilitatorsList[i], ghoFacilitatorList[i]);
}
}

function testUpdateGhoBorrowRate() public {
executePayload(vm, address(proposal));

uint256 oldBorrowRate = _getGhoBorrowRate();
uint256 newBorrowRate = oldBorrowRate + 1;

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateGhoBorrowRate(newBorrowRate);
vm.stopPrank();

uint256 currentBorrowRate = _getGhoBorrowRate();
assertEq(currentBorrowRate, newBorrowRate);
}

function testLowerGhoBorrowRate() public {
executePayload(vm, address(proposal));

uint256 oldBorrowRate = _getGhoBorrowRate();
uint256 newBorrowRate = oldBorrowRate - 1;

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateGhoBorrowRate(newBorrowRate);
vm.stopPrank();

uint256 currentBorrowRate = _getGhoBorrowRate();
assertEq(currentBorrowRate, newBorrowRate);
}

function testUpdateGhoBorrowCap() public {
executePayload(vm, address(proposal));

(uint256 oldBorrowCap, ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveCaps(
MiscEthereum.GHO_TOKEN
);
uint256 newBorrowCap = oldBorrowCap + 1;

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateGhoBorrowCap(newBorrowCap);
vm.stopPrank();

(uint256 updatedBorrowCap, ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveCaps(
MiscEthereum.GHO_TOKEN
);
assertEq(newBorrowCap, updatedBorrowCap);
}

function testLowerGhoBorrowCap() public {
executePayload(vm, address(proposal));

(uint256 oldBorrowCap, ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveCaps(
MiscEthereum.GHO_TOKEN
);
uint256 newBorrowCap = oldBorrowCap - 1;

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateGhoBorrowCap(newBorrowCap);
vm.stopPrank();

(uint256 updatedBorrowCap, ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveCaps(
MiscEthereum.GHO_TOKEN
);
assertEq(newBorrowCap, updatedBorrowCap);
}

function testUpdateFacilitatorBucketCapacity() public {
executePayload(vm, address(proposal));
address[] memory ghoFacilitatorList = IGhoToken(MiscEthereum.GHO_TOKEN).getFacilitatorsList();

for (uint256 i = 0; i < ghoFacilitatorList.length; i++) {
address ghoFacilitator = ghoFacilitatorList[i];
(uint256 currentBucketCapacity, ) = IGhoToken(MiscEthereum.GHO_TOKEN).getFacilitatorBucket(
ghoFacilitator
);
uint128 newBucketCapacity = uint128(currentBucketCapacity) + 1;

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateFacilitatorBucketCapacity(
ghoFacilitator,
newBucketCapacity
);
vm.stopPrank();

(uint256 updatedCapacity, ) = IGhoToken(MiscEthereum.GHO_TOKEN).getFacilitatorBucket(
ghoFacilitator
);
assertEq(newBucketCapacity, updatedCapacity);
}
}

function testUpdateGsmExposureCap() public {
executePayload(vm, address(proposal));

address[2] memory gsmList;
gsmList[0] = MiscEthereum.GSM_USDC;
gsmList[1] = MiscEthereum.GSM_USDT;

for (uint256 i = 0; i < gsmList.length; i++) {
address gsm = gsmList[i];

uint128 oldExposureCap = IGsm(gsm).getExposureCap();
uint128 newExposureCap = oldExposureCap + 1;

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateGsmExposureCap(gsm, newExposureCap);
vm.stopPrank();

uint128 currentExposureCap = IGsm(gsm).getExposureCap();
assertEq(currentExposureCap, newExposureCap);
}
}

function testLowerGsmExposureCap() public {
executePayload(vm, address(proposal));

address[2] memory gsmList;
gsmList[0] = MiscEthereum.GSM_USDC;
gsmList[1] = MiscEthereum.GSM_USDT;

for (uint256 i = 0; i < gsmList.length; i++) {
address gsm = gsmList[i];

uint128 oldExposureCap = IGsm(gsm).getExposureCap();
uint128 newExposureCap = oldExposureCap - 1;

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateGsmExposureCap(gsm, newExposureCap);
vm.stopPrank();

uint128 currentExposureCap = IGsm(gsm).getExposureCap();
assertEq(currentExposureCap, newExposureCap);
}
}

function testUpdateGsmBuySellFeesBuyFee() public {
executePayload(vm, address(proposal));

address[2] memory gsmList;
gsmList[0] = MiscEthereum.GSM_USDC;
gsmList[1] = MiscEthereum.GSM_USDT;

for (uint256 i = 0; i < gsmList.length; i++) {
address gsm = gsmList[i];

address feeStrategy = IGsm(gsm).getFeeStrategy();
uint256 buyFee = IGsmFeeStrategy(feeStrategy).getBuyFee(1e4);
uint256 sellFee = IGsmFeeStrategy(feeStrategy).getSellFee(1e4);

vm.startPrank(RISK_COUNCIL);
IGhoStewardV2(proposal.GHO_STEWARD()).updateGsmBuySellFees(gsm, buyFee + 1, sellFee + 1);
vm.stopPrank();

address newStrategy = IGsm(gsm).getFeeStrategy();
uint256 newBuyFee = IGsmFeeStrategy(newStrategy).getBuyFee(1e4);
uint256 newSellFee = IGsmFeeStrategy(newStrategy).getSellFee(1e4);

assertEq(newBuyFee, buyFee + 1);
assertEq(newSellFee, sellFee + 1);
}
}

function _getGhoBorrowRate() internal view returns (uint256) {
address currentInterestRateStrategy = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getInterestRateStrategyAddress(MiscEthereum.GHO_TOKEN);
return
IDefaultInterestRateStrategyV2(currentInterestRateStrategy)
.getInterestRateDataBps(MiscEthereum.GHO_TOKEN)
.baseVariableBorrowRate;
}
}
46 changes: 46 additions & 0 deletions src/20240826_AaveV3Ethereum_GHOStewardUpdate/GHOStewardUpdate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "GHO Steward Update"
author: "BGD Labs (@bgdlabs)"
discussions: ""
---

## Simple Summary

This proposal aims to upgrade the GHO Steward contract to a new version that is compatible with Aave v3.1. The update ensures that the GHO Steward can continue to effectively manage GHO parameters on the latest Aave v3.1 version.

## Motivation

The Aave v3.1 upgrade introduced stateful interest rates, rendering the existing GHO Steward incompatible due to its reliance on fixed interest rate strategy contracts. During the v3.1 upgrade, the original GHO Steward was not deactivated to allow for adjustments to parameters other than the borrow rate. In the interim, GHO borrow rate updates were being managed through governance proposals (such as [Proposal 151](https://vote.onaave.com/proposal/?proposalId=151)).

The activation of this new GHO Steward will restore full functionality to the GHO Stewards, allowing to manage all GHO parameters effectively within the current Aave protocol version.

Same as earlier, the GHO Stewards will consist of members from Growth (ACI), Risk (ChaosLabs) and Finance (TokenLogic + Karpatkey) Service Providers and utilize a 3 of 4 [multi-sig](https://etherscan.io/address/0x8513e6F37dBc52De87b166980Fa3F50639694B60).

## Specification

The proposal gives / revokes the following admin roles:

- Grant new GHO Steward the Risk Admin role via the [ACL_MANAGER](https://etherscan.io/address/0xc2aaCf6553D20d1e9d78E365AAba8032af9c85b0) contract and revoke from the old Steward.
- Grant new GHO Steward the Bucket Manager role on the [GHO token](https://etherscan.io/address/0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f) and revoke from the old Steward.
- Grant new GHO Steward the Configurator role on [GSM_USDC](https://etherscan.io/address/0x0d8eFfC11dF3F229AA1EA0509BC9DFa632A13578) and [GSM_USDT](https://etherscan.io/address/0x686F8D21520f4ecEc7ba577be08354F4d1EB8262) and revoke from the old Steward.
- Whitelists all the facilitators on the new GHO Stewards, including: [GHO_AToken](https://etherscan.io/address/0x00907f9921424583e7ffBfEdf84F92B7B2Be4977), [GHO_FlashMinter](https://etherscan.io/address/0xb639D208Bcf0589D54FaC24E655C79EC529762B8), [GSM_USDC](https://etherscan.io/address/0x0d8eFfC11dF3F229AA1EA0509BC9DFa632A13578), [GSM_USDT](https://etherscan.io/address/0x686F8D21520f4ecEc7ba577be08354F4d1EB8262) - so that the steward has the permissions to update the bucket capacity.

Changes:

- GHO borrow rate change is now done via stateful interest rates, being compatible with Aave v3.1

_Note: The params for the GHO Stewards will be exactly the same as on the earlier version._

## References

- GHO Steward SAFE address: [0x8513e6F37dBc52De87b166980Fa3F50639694B60](https://etherscan.io/address/0x8513e6F37dBc52De87b166980Fa3F50639694B60)
- GHO Steward V2 address: [0x1180eE41eC15Dd0accC13a1e646B3152bECFf8F6](https://etherscan.io/address/0x1180ee41ec15dd0accc13a1e646b3152becff8f6)
- GHO Steward Repo: [GhoStewardV2.sol](TODO-ADD-AFTER-PR-MERGE)
- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240826_AaveV3Ethereum_GHOStewardUpdate/AaveV3Ethereum_GHOStewardUpdate_20240826.sol)
- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240826_AaveV3Ethereum_GHOStewardUpdate/AaveV3Ethereum_GHOStewardUpdate_20240826.t.sol)
- [Discussion](TODO)
- Snapshot: Direct To AIP

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Loading
Loading