Skip to content

Commit

Permalink
Add new gho-aave-steward on prime instance (#615)
Browse files Browse the repository at this point in the history
* added new gho-aave-steward on prime instance

* updated redme

* added some comments

* used constant for council address

* added more tests

* removed unused import

* added more tests

* BTC UOptiminal Rate update

* Revert "BTC UOptiminal Rate update"

This reverts commit d64400c.

* test script and md updated

* Update src/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/ExtendGHOStewardOnAavePrimeInstance.md

Co-authored-by: Harsh Pandey <[email protected]>

* restricted gho on risk steward

* import source bugfix

---------

Co-authored-by: Harsh Pandey <[email protected]>
  • Loading branch information
LucasWongC and brotherlymite authored Feb 25, 2025
1 parent 1dde9cc commit b3b4adb
Show file tree
Hide file tree
Showing 6 changed files with 426 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol';
import {AaveV3EthereumLido, AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol';
import {IRiskSteward} from './IRiskSteward.sol';

/**
* @title Extend GHO Steward on Aave Prime Instance
* @author TokenLogic
* - Snapshot: https://snapshot.org/#/s:aave.eth/proposal/0xf28190a683eff1dc246924f150a724dcf29b23dd40971df38d20fc6cf301fbe1
* - Discussion: https://governance.aave.com/t/arfc-extend-gho-steward-on-aave-prime-instance/20598
*/
contract AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129 is
IProposalGenericExecutor
{
// https://etherscan.io/address/0x5C905d62B22e4DAa4967E517C4a047Ff6026C731
address public constant NEW_GHO_AAVE_STEWARD = 0x5C905d62B22e4DAa4967E517C4a047Ff6026C731;
function execute() external {
// Gho Aave Steward
AaveV3EthereumLido.ACL_MANAGER.grantRole(
AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(),
NEW_GHO_AAVE_STEWARD
);

IRiskSteward(AaveV3EthereumLido.RISK_STEWARD).setAddressRestricted(
AaveV3EthereumLidoAssets.GHO_UNDERLYING,
true
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IGhoAaveSteward} from 'src/interfaces/IGhoAaveSteward.sol';
import {AaveV3EthereumLido, AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol';
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
import {IOwnable} from 'aave-address-book/common/IOwnable.sol';
import {IDefaultInterestRateStrategyV2} from 'aave-address-book/AaveV3.sol';

import {IRiskSteward} from './IRiskSteward.sol';
import {ReserveConfiguration} from 'aave-v3-origin/contracts/protocol/libraries/configuration/ReserveConfiguration.sol';
import {DataTypes} from 'aave-v3-origin/contracts/protocol/libraries/types/DataTypes.sol';

import 'forge-std/Test.sol';
import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol';
import {AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129} from './AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129.sol';

/**
* @dev Test for AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129
* command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129.t.sol -vv
*/
contract AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129_Test is
ProtocolV3TestBase
{
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129 internal proposal;
// https://etherscan.io/address/0x5C905d62B22e4DAa4967E517C4a047Ff6026C731
IGhoAaveSteward public constant NEW_GHO_AAVE_STEWARD =
IGhoAaveSteward(0x5C905d62B22e4DAa4967E517C4a047Ff6026C731);

// The address of council
// https://etherscan.io/address/0x8513e6F37dBc52De87b166980Fa3F50639694B60
address public constant RISK_COUNCIL = 0x8513e6F37dBc52De87b166980Fa3F50639694B60;

function setUp() public {
vm.createSelectFork(vm.rpcUrl('mainnet'), 21738505);
proposal = new AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129();
}

function testValidate() public {
assertFalse(
AaveV3EthereumLido.ACL_MANAGER.hasRole(
AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(),
address(NEW_GHO_AAVE_STEWARD)
)
);
assertFalse(
IRiskSteward(AaveV3EthereumLido.RISK_STEWARD).isAddressRestricted(
AaveV3EthereumLidoAssets.GHO_UNDERLYING
)
);
assertEq(address(proposal.NEW_GHO_AAVE_STEWARD()), address(NEW_GHO_AAVE_STEWARD));
assertEq(IOwnable(address(NEW_GHO_AAVE_STEWARD)).owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1);
assertEq(
NEW_GHO_AAVE_STEWARD.POOL_ADDRESSES_PROVIDER(),
address(AaveV3EthereumLido.POOL_ADDRESSES_PROVIDER)
);
assertEq(
NEW_GHO_AAVE_STEWARD.POOL_DATA_PROVIDER(),
address(AaveV3EthereumLido.AAVE_PROTOCOL_DATA_PROVIDER)
);
assertEq(NEW_GHO_AAVE_STEWARD.GHO_TOKEN(), AaveV3EthereumLidoAssets.GHO_UNDERLYING);
assertEq(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL(), RISK_COUNCIL);
assertEq(
NEW_GHO_AAVE_STEWARD.getBorrowRateConfig(),
IGhoAaveSteward.BorrowRateConfig({
optimalUsageRatioMaxChange: 5_00,
baseVariableBorrowRateMaxChange: 5_00,
variableRateSlope1MaxChange: 5_00,
variableRateSlope2MaxChange: 5_00
})
);

executePayload(vm, address(proposal));

assertTrue(
AaveV3EthereumLido.ACL_MANAGER.hasRole(
AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(),
address(NEW_GHO_AAVE_STEWARD)
)
);
assertTrue(
IRiskSteward(AaveV3EthereumLido.RISK_STEWARD).isAddressRestricted(
AaveV3EthereumLidoAssets.GHO_UNDERLYING
)
);
}

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

uint256 currentBorrowCap = _getGhoBorrowCap();
uint256 newBorrowCap = currentBorrowCap + 1;
vm.startPrank(RISK_COUNCIL);
IGhoAaveSteward(proposal.NEW_GHO_AAVE_STEWARD()).updateGhoBorrowCap(newBorrowCap);
assertEq(_getGhoBorrowCap(), newBorrowCap);
}

function test_ghoAaveSteward_updateGhoSupplyCap() public {
uint256 configValue = 830948836238514615306439858845646848;
vm.mockCall(
address(AaveV3EthereumLido.POOL),
abi.encodeWithSelector(AaveV3EthereumLido.POOL.getConfiguration.selector),
abi.encode(configValue)
);

uint256 currentSupplyCap = _getGhoSupplyCap();
assertEq(currentSupplyCap, 10);
uint256 newSupplyCap = currentSupplyCap + 1;

executePayload(vm, address(proposal));

IGhoAaveSteward steward = IGhoAaveSteward(proposal.NEW_GHO_AAVE_STEWARD());

vm.startPrank(RISK_COUNCIL);
steward.updateGhoSupplyCap(newSupplyCap);
vm.stopPrank();

vm.clearMockedCalls();

assertEq(_getGhoSupplyCap(), newSupplyCap);
}

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

uint256 currentSupplyCap = _getGhoSupplyCap();
assertEq(currentSupplyCap, 20000000);
uint256 newSupplyCap = 2 * currentSupplyCap + 1;

IGhoAaveSteward steward = IGhoAaveSteward(proposal.NEW_GHO_AAVE_STEWARD());

// Can't update supply cap even by 1 since it's 0, and 100% of 0 is 0
vm.expectRevert('INVALID_SUPPLY_CAP_UPDATE');
vm.startPrank(RISK_COUNCIL);
steward.updateGhoSupplyCap(newSupplyCap);
vm.stopPrank();
}

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

address rateStrategyAddress = AaveV3EthereumLido
.AAVE_PROTOCOL_DATA_PROVIDER
.getInterestRateStrategyAddress(AaveV3EthereumLidoAssets.GHO_UNDERLYING);

IDefaultInterestRateStrategyV2.InterestRateData
memory mockResponse = IDefaultInterestRateStrategyV2.InterestRateData({
optimalUsageRatio: 100,
baseVariableBorrowRate: 100,
variableRateSlope1: 100,
variableRateSlope2: 100
});
vm.mockCall(
rateStrategyAddress,
abi.encodeWithSelector(
IDefaultInterestRateStrategyV2(rateStrategyAddress).getInterestRateDataBps.selector,
AaveV3EthereumLidoAssets.GHO_UNDERLYING
),
abi.encode(mockResponse)
);

IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates();
uint16 newOptimalUsageRatio = currentRates.optimalUsageRatio + 1;
uint32 newBaseVariableBorrowRate = currentRates.baseVariableBorrowRate + 1;
uint32 newVariableRateSlope1 = currentRates.variableRateSlope1 - 1;
uint32 newVariableRateSlope2 = currentRates.variableRateSlope2 - 1;

vm.startPrank(RISK_COUNCIL);
IGhoAaveSteward(proposal.NEW_GHO_AAVE_STEWARD()).updateGhoBorrowRate(
newOptimalUsageRatio,
newBaseVariableBorrowRate,
newVariableRateSlope1,
newVariableRateSlope2
);
vm.stopPrank();

vm.clearMockedCalls();

assertEq(_getOptimalUsageRatio(), newOptimalUsageRatio);
assertEq(_getBaseVariableBorrowRate(), newBaseVariableBorrowRate);
assertEq(_getVariableRateSlope1(), newVariableRateSlope1);
assertEq(_getVariableRateSlope2(), newVariableRateSlope2);
}

// Helpers

function _getGhoBorrowCap() internal view returns (uint256) {
DataTypes.ReserveConfigurationMap memory configuration = AaveV3EthereumLido
.POOL
.getConfiguration(AaveV3EthereumLidoAssets.GHO_UNDERLYING);
return configuration.getBorrowCap();
}

function _getGhoSupplyCap() internal view returns (uint256) {
DataTypes.ReserveConfigurationMap memory configuration = AaveV3EthereumLido
.POOL
.getConfiguration(AaveV3EthereumLidoAssets.GHO_UNDERLYING);
return configuration.getSupplyCap();
}

function _getOptimalUsageRatio() internal view returns (uint16) {
IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates();
return currentRates.optimalUsageRatio;
}

function _getBaseVariableBorrowRate() internal view returns (uint32) {
IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates();
return currentRates.baseVariableBorrowRate;
}

function _getVariableRateSlope1() internal view returns (uint32) {
IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates();
return currentRates.variableRateSlope1;
}

function _getVariableRateSlope2() internal view returns (uint32) {
IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates();
return currentRates.variableRateSlope2;
}

function _getGhoBorrowRates()
internal
view
returns (IDefaultInterestRateStrategyV2.InterestRateData memory)
{
address rateStrategyAddress = AaveV3EthereumLido
.AAVE_PROTOCOL_DATA_PROVIDER
.getInterestRateStrategyAddress(AaveV3EthereumLidoAssets.GHO_UNDERLYING);
return
IDefaultInterestRateStrategyV2(rateStrategyAddress).getInterestRateDataBps(
AaveV3EthereumLidoAssets.GHO_UNDERLYING
);
}

function assertEq(
IGhoAaveSteward.BorrowRateConfig memory a,
IGhoAaveSteward.BorrowRateConfig memory b
) internal pure {
assertEq(a.optimalUsageRatioMaxChange, b.optimalUsageRatioMaxChange);
assertEq(a.baseVariableBorrowRateMaxChange, b.baseVariableBorrowRateMaxChange);
assertEq(a.variableRateSlope1MaxChange, b.variableRateSlope1MaxChange);
assertEq(a.variableRateSlope2MaxChange, b.variableRateSlope2MaxChange);
assertEq(abi.encode(a), abi.encode(b)); // sanity check
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: "Extend GHO Steward on Aave Prime Instance"
author: "TokenLogic"
discussions: "https://governance.aave.com/t/arfc-extend-gho-steward-on-aave-prime-instance/20598"
snapshot: "https://snapshot.org/#/s:aave.eth/proposal/0xf28190a683eff1dc246924f150a724dcf29b23dd40971df38d20fc6cf301fbe1"
---

## Simple Summary

This publication proposes extending the GHO Aave Steward to the GHO market on the Prime instance.

## Motivation

In response to the expanding GHO ecosystem and following the upgrade of the [GHO Stewards](https://governance.aave.com/t/arfc-gho-steward-v2-upgrade/19116) to a more modular approach at the end of 2024, this publication proposes extending the GHO Aave Steward role to the GHO Reserve on the Prime instance of Aave v3 on Ethereum.

Similar to how the GHO reserve on Arbitrum, which already has a GHO Aave Steward implementation, the role will be extended to the Prime instance of Aave v3.

## Specification

A new [GhoAaveSteward](https://etherscan.io/address/0x5C905d62B22e4DAa4967E517C4a047Ff6026C731) was deployed, with the Prime configuration:

Owner: [0x5300A1a15135EA4dc7aD5a167152C01EFc9b192A](https://etherscan.io/address/0x5300A1a15135EA4dc7aD5a167152C01EFc9b192A)

Addresses Provider: [0xcfBf336fe147D643B9Cb705648500e101504B16d](https://etherscan.io/address/0xcfBf336fe147D643B9Cb705648500e101504B16d)

Pool Data Provider: [0x08795CFE08C7a81dCDFf482BbAAF474B240f31cD](https://etherscan.io/address/0x08795CFE08C7a81dCDFf482BbAAF474B240f31cD)

Gho Token: [0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f](https://etherscan.io/address/0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f)

Risk Council: [0x8513e6F37dBc52De87b166980Fa3F50639694B60](https://etherscan.io/address/0x8513e6F37dBc52De87b166980Fa3F50639694B60)

BorrowRateConfig:

- UOptimal Max Change: 500
- Base Variable Rate Max Change: 500
- Slope 1 Max Change: 500
- Slope 2 Max Change: 500

Then, the GhoAaveSteward is to be granted the following permissions: `RiskAdmin` in Aave V3 Ethereum Prime Pool

## References

- Implementation: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129.sol)
- Tests: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129.t.sol)
- [Snapshot](https://snapshot.org/#/s:aave.eth/proposal/0xf28190a683eff1dc246924f150a724dcf29b23dd40971df38d20fc6cf301fbe1)
- [Discussion](https://governance.aave.com/t/arfc-extend-gho-steward-on-aave-prime-instance/20598)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Original file line number Diff line number Diff line change
@@ -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 {AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129} from './AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129.sol';

/**
* @dev Deploy Ethereum
* deploy-command: make deploy-ledger contract=src/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/ExtendGHOStewardOnAavePrimeInstance_20250129.s.sol:DeployEthereum chain=mainnet
* verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/ExtendGHOStewardOnAavePrimeInstance_20250129.s.sol/1/run-latest.json
*/
contract DeployEthereum is EthereumScript {
function run() external broadcast {
// deploy payloads
address payload0 = GovV3Helpers.deployDeterministic(
type(AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129).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/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/ExtendGHOStewardOnAavePrimeInstance_20250129.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(AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129).creationCode
);
payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum);

// create proposal
vm.startBroadcast();
GovV3Helpers.createProposal(
vm,
payloads,
GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL,
GovV3Helpers.ipfsHashFile(
vm,
'src/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/ExtendGHOStewardOnAavePrimeInstance.md'
)
);
}
}
Loading

1 comment on commit b3b4adb

@sakulstra
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌈Test Results Compiling 329 files with Solc 0.8.22 Solc 0.8.22 finished in 161.98s Compiler run successful!

Ran 5 tests for src/20250129_AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance/AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129.t.sol:AaveV3EthereumLido_ExtendGHOStewardOnAavePrimeInstance_20250129_Test
[PASS] testValidate() (gas: 163350)
[PASS] test_ghoAaveSteward_revertsChangeOverMax() (gas: 163345)
[PASS] test_ghoAaveSteward_updateGhoBorrowCap() (gas: 216332)
[PASS] test_ghoAaveSteward_updateGhoBorrowRate() (gas: 378004)
[PASS] test_ghoAaveSteward_updateGhoSupplyCap() (gas: 212101)
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 2.48s (6.60s CPU time)

Ran 1 test suite in 2.48s (2.48s CPU time): 5 tests passed, 0 failed, 0 skipped (5 total tests)

Please sign in to comment.