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

Add governance proposal for GHO Stability Mechanism Launch #11

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7b657ec
feat: Add payload
miguelmtzinf Jan 17, 2024
ba41201
feat: bnb activation payload (#84)
brotherlymite Jan 18, 2024
f5f2e3d
fix(cache): automated cache update [skip ci]
Jan 18, 2024
30656b9
test: Add tests
miguelmtzinf Jan 18, 2024
6200cf4
feat: Add script and tests with final addresses
miguelmtzinf Jan 19, 2024
db29fcf
fix: update aave helpers (#171)
sakulstra Jan 19, 2024
87121b9
fix: Fix link and gasLimit amounts
miguelmtzinf Jan 19, 2024
fd6aa81
fix: Update dependencies
miguelmtzinf Jan 19, 2024
698dc29
fix: Fix oracle swap freezer addresses
miguelmtzinf Jan 19, 2024
9d1c452
Merge branch 'bgd/main' into feat/gsm-launch
miguelmtzinf Jan 19, 2024
dab9aa1
fix: Bump block number for tests
miguelmtzinf Jan 19, 2024
e1c7f56
fix: Reduce LINK amount so its enough in treasury
miguelmtzinf Jan 19, 2024
f3ccff9
add test for OracleSwapFreezer
parth-15 Jan 19, 2024
8ce41b3
fix: Clean up in tests
miguelmtzinf Jan 19, 2024
0ffb368
docs: Add AIP text
miguelmtzinf Jan 19, 2024
13b98e0
Polygon V2 Reserve Factor Updates (#162)
Jan 19, 2024
95d84cd
fix(cache): automated cache update [skip ci]
Jan 19, 2024
1982811
StkGHO Initialization proposal (#170)
The-3D Jan 20, 2024
b44cb96
fix(cache): automated cache update [skip ci]
Jan 20, 2024
168d0c8
docs: Fix typo in natspec docs
miguelmtzinf Jan 23, 2024
74f037f
fix: Fix typo on natspec docs
miguelmtzinf Jan 23, 2024
da11f18
docs: Fix typo in natspec docs
miguelmtzinf Jan 24, 2024
ecf22dc
fix: Add DAO as swap freezer
miguelmtzinf Jan 24, 2024
9ab28a9
chore(deps): bump lib/aave-helpers from `fae3da0` to `85e73cc` (#180)
dependabot[bot] Jan 24, 2024
b5521d3
Merge branch 'main' of https://github.com/bgd-labs/aave-proposals-v3 …
miguelmtzinf Jan 24, 2024
2458f31
Merge branch 'main' into feat/gsm-launch
miguelmtzinf Jan 24, 2024
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,135 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol';
import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol';

interface IGhoToken {
function addFacilitator(
address facilitatorAddress,
string calldata facilitatorLabel,
uint128 bucketCapacity
) external;
}

interface IGsm {
function updateFeeStrategy(address feeStrategy) external;

function SWAP_FREEZER_ROLE() external pure returns (bytes32);

function grantRole(bytes32 role, address account) external;
}

interface IGsmRegistry {
function addGsm(address gsmAddress) external;
}

interface IAaveCLRobotOperator {
/**
* @notice method called by owner to register the automation robot keeper.
* @param name - name of keeper.
* @param upkeepContract - upkeepContract of the keeper.
* @param gasLimit - max gasLimit which the chainlink automation node can execute for the automation.
* @param amountToFund - amount of link to fund the keeper with.
* @return chainlink id for the registered keeper.
**/
function register(
string memory name,
address upkeepContract,
uint32 gasLimit,
uint96 amountToFund
) external returns (uint256);
}

/**
* @title GHO Stability Module
* @author Aave labs (@aave)
* @dev This proposal enables 2 GHO Stability Modules (USDC, USDT):
* - Addition of USDC and USDT GSMs as GHO Facilitators
* - Give Swap Freezer permissions to OracleSwapFreezers, one per module
* - Install a 2% fee strategy into both modules
* - Register both GSMs in the GsmRegistry
* - Activate OracleSwapFreezer contracts as AaveRobot Keepers
* Relevant governance links:
* 1. GHO Stability Module
* - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x98bdd30f645b2981320f82c671ae9fee31ee771766c13cd2627b66a22f0d438e
* - Discussion: https://governance.aave.com/t/temp-check-gho-stability-module/13927
* 2. GHO Stability Module Update
* - Discussion: https://governance.aave.com/t/gho-stability-module-update/14442
* 3. GHO Stability Module Launch
* - Snapshot: https://snapshot.org/#/aave.eth/proposal/0xe9b62e197a98832da7d1231442b5960588747f184415fba4699b6325d7618842
*/
contract Gho_GhoStabilityModule {
using SafeERC20 for IERC20;

address public constant GSM_USDC = 0x55027d3dBBcEA0327eF73eFd74ba0Af42A13A966; // TODO
address public constant GSM_USDC_ORACLE_SWAP_FREEZER = 0x1Dbbf529D78d6507B0dd71F6c02f41138d828990; // TODO
address public constant GSM_USDT = 0x9eb52339B52e71B1EFD5537947e75D23b3a7719B; // TODO
address public constant GSM_USDT_ORACLE_SWAP_FREEZER = 0xf18774574148852771c2631d7d06E2A6c8b44fCA; // TODO
address public constant GSM_REGISTRY = 0x9f62EE65a8395824Ee0821eF2Dc4C947a23F0f25; // TODO

address public constant GSM_FIXED_FEE_STRATEGY = 0xF6d2cE02a0647dd10F3f4263e29f2167DC6542cC; // TODO

string public constant GSM_USDC_FACILITATOR_LABEL = 'GSM USDC';
uint128 public constant GSM_USDC_BUCKET_CAPACITY = 500_000e18;
string public constant GSM_USDT_FACILITATOR_LABEL = 'GSM USDT';
uint128 public constant GSM_USDT_BUCKET_CAPACITY = 500_000e18;

//
address public constant ROBOT_OPERATOR = 0x020E452b463568f55BAc6Dc5aFC8F0B62Ea5f0f3;
uint96 public constant LINK_AMOUNT_ORACLE_FREEZER_KEEPER = 10 ether;
uint96 public constant TOTAL_LINK_AMOUNT_KEEPERS = LINK_AMOUNT_ORACLE_FREEZER_KEEPER * 2; // 2 GSMs

//

function execute() external {
// 1. Enroll GSMs as GHO Facilitators
IGhoToken(AaveV3Ethereum.GHO_TOKEN).addFacilitator(
GSM_USDC,
GSM_USDC_FACILITATOR_LABEL,
GSM_USDC_BUCKET_CAPACITY
);
IGhoToken(AaveV3Ethereum.GHO_TOKEN).addFacilitator(
GSM_USDT,
GSM_USDT_FACILITATOR_LABEL,
GSM_USDT_BUCKET_CAPACITY
);

// 2. Add GSM Swap Freezer role to OracleSwapFreezers
IGsm(GSM_USDC).grantRole(IGsm(GSM_USDC).SWAP_FREEZER_ROLE(), GSM_USDC_ORACLE_SWAP_FREEZER);
IGsm(GSM_USDT).grantRole(IGsm(GSM_USDT).SWAP_FREEZER_ROLE(), GSM_USDT_ORACLE_SWAP_FREEZER);

// 3. Update Fee Strategy
IGsm(GSM_USDC).updateFeeStrategy(GSM_FIXED_FEE_STRATEGY);
IGsm(GSM_USDT).updateFeeStrategy(GSM_FIXED_FEE_STRATEGY);

// 4. Add GSMs to GSM Registry
IGsmRegistry(GSM_REGISTRY).addGsm(GSM_USDC);
IGsmRegistry(GSM_REGISTRY).addGsm(GSM_USDT);

// 5. Register OracleSwapFreezer as keepers
AaveV3Ethereum.COLLECTOR.transfer(
AaveV3EthereumAssets.LINK_UNDERLYING,
address(this),
TOTAL_LINK_AMOUNT_KEEPERS
);
IERC20(AaveV3EthereumAssets.LINK_UNDERLYING).forceApprove(
ROBOT_OPERATOR,
TOTAL_LINK_AMOUNT_KEEPERS
);

IAaveCLRobotOperator(ROBOT_OPERATOR).register(
'GSM USDC OracleSwapFreezer',
GSM_USDC_ORACLE_SWAP_FREEZER,
5000000, // gasLimit
miguelmtzinf marked this conversation as resolved.
Show resolved Hide resolved
LINK_AMOUNT_ORACLE_FREEZER_KEEPER
);
IAaveCLRobotOperator(ROBOT_OPERATOR).register(
'GSM USDT OracleSwapFreezer',
GSM_USDT_ORACLE_SWAP_FREEZER,
5000000, // gasLimit
LINK_AMOUNT_ORACLE_FREEZER_KEEPER
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import 'forge-std/Test.sol';
import {AaveV3EthereumAssets, AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol';
import {ProtocolV3TestBase} from 'aave-helpers/ProtocolV3TestBase.sol';
import {IPoolConfigurator} from 'aave-address-book/AaveV3.sol';
import {Gho_GhoStabilityModule} from './20240119_Gho_GhoStabilityModule.sol';

interface IGhoToken {
struct Facilitator {
uint128 bucketCapacity;
uint128 bucketLevel;
string label;
}

function getFacilitator(address facilitator) external view returns (Facilitator memory);

function getFacilitatorsList() external view returns (address[] memory);
}

interface IGsm {
function getFeeStrategy() external view returns (address);

function getAvailableUnderlyingExposure() external view returns (uint256);

function getIsFrozen() external view returns (bool);

function getIsSeized() external view returns (bool);

function UNDERLYING_ASSET() external view returns (address);
}

interface IFeeStrategy {
function getBuyFee(uint256 grossAmount) external view returns (uint256);

function getSellFee(uint256 grossAmount) external view returns (uint256);
}

interface IOracleSwapFreezer {
function getCanUnfreeze() external view returns (bool);

function getFreezeBound() external view returns (uint128, uint128);

function getUnfreezeBound() external view returns (uint128, uint128);
}

/**
* @dev Test for Gho_GhoStabilityModule
* command: make test-contract filter=Gho_GhoStabilityModule
*/
contract Gho_GhoStabilityModule_Test is ProtocolV3TestBase {
address constant NEW_VGHO_IMPL = 0x20Cb2f303EDe313e2Cc44549Ad8653a5E8c0050e;

Gho_GhoStabilityModule internal proposal;

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

function test_defaultProposalExecution() public {
// // increase GHO borrow cap so test borrows can succeed
// vm.prank(AaveV3Ethereum.CAPS_PLUS_RISK_STEWARD);
// AaveV3Ethereum.POOL_CONFIGURATOR.setBorrowCap(AaveV3Ethereum.GHO_TOKEN, 36_000_000);
defaultTest('Gho_GhoStabilityModule', AaveV3Ethereum.POOL, address(proposal));
}

function test_checkConfig() public {
uint256 facilitatorListLengthBefore = IGhoToken(AaveV3Ethereum.GHO_TOKEN)
.getFacilitatorsList()
.length;

executePayload(vm, address(proposal));

assertTrue(
IGhoToken(AaveV3Ethereum.GHO_TOKEN).getFacilitatorsList().length ==
facilitatorListLengthBefore + 2
);
IGhoToken.Facilitator memory gsmUsdc = IGhoToken(AaveV3Ethereum.GHO_TOKEN).getFacilitator(
proposal.GSM_USDC()
);
assertEq(gsmUsdc.label, proposal.GSM_USDC_FACILITATOR_LABEL());
assertEq(gsmUsdc.bucketCapacity, proposal.GSM_USDC_BUCKET_CAPACITY());
assertEq(gsmUsdc.bucketLevel, 0);

IGhoToken.Facilitator memory gsmUsdt = IGhoToken(AaveV3Ethereum.GHO_TOKEN).getFacilitator(
proposal.GSM_USDT()
);
assertEq(gsmUsdt.label, proposal.GSM_USDT_FACILITATOR_LABEL());
assertEq(gsmUsdt.bucketCapacity, proposal.GSM_USDT_BUCKET_CAPACITY());
assertEq(gsmUsdt.bucketLevel, 0);

// GSM USDC
GsmConfig memory gsmUsdcConfig = GsmConfig({
sellFee: 200,
buyFee: 200,
exposureCap: 500_000e6,
isFrozen: false,
isSeized: false,
freezerCanUnfreeze: true,
freezeLowerBound: 0.99e8,
freezeUpperBound: 1.01e8,
unfreezeLowerBound: 0.995e8,
unfreezeUpperBound: 1.005e8
});
_checkGsmConfig(
IGsm(proposal.GSM_USDC()),
AaveV3EthereumAssets.USDC_UNDERLYING,
IOracleSwapFreezer(proposal.GSM_USDC_ORACLE_SWAP_FREEZER()),
gsmUsdcConfig
);

// GSM USDT
GsmConfig memory gsmUsdtConfig = GsmConfig({
sellFee: 200,
buyFee: 200,
exposureCap: 500_000e6,
isFrozen: false,
isSeized: false,
freezerCanUnfreeze: true,
freezeLowerBound: 0.99e8,
freezeUpperBound: 1.01e8,
unfreezeLowerBound: 0.995e8,
unfreezeUpperBound: 1.005e8
});
_checkGsmConfig(
IGsm(proposal.GSM_USDT()),
AaveV3EthereumAssets.USDT_UNDERLYING,
IOracleSwapFreezer(proposal.GSM_USDT_ORACLE_SWAP_FREEZER()),
gsmUsdtConfig
);
}

struct GsmConfig {
uint256 sellFee;
uint256 buyFee;
uint256 exposureCap;
bool isFrozen;
bool isSeized;
bool freezerCanUnfreeze;
uint256 freezeLowerBound;
uint256 freezeUpperBound;
uint256 unfreezeLowerBound;
uint256 unfreezeUpperBound;
}

function _checkGsmConfig(
IGsm gsm,
address underlying,
IOracleSwapFreezer freezer,
GsmConfig memory config
) internal {
assertEq(gsm.UNDERLYING_ASSET(), underlying, 'wrong underlying asset');
assertEq(gsm.getAvailableUnderlyingExposure(), config.exposureCap, 'wrong exposure cap');
assertEq(gsm.getIsFrozen(), config.isFrozen, 'wrong freeze state');
assertEq(gsm.getIsSeized(), config.isSeized, 'wrong seized state');

IFeeStrategy feeStrategy = IFeeStrategy(gsm.getFeeStrategy());
assertEq(feeStrategy.getSellFee(10000), config.sellFee, 'wrong sell fee');
assertEq(feeStrategy.getBuyFee(10000), config.buyFee, 'wrong buy fee');

// Oracle freezer
assertEq(freezer.getCanUnfreeze(), config.freezerCanUnfreeze, 'wrong freezer config');
(uint256 lowerBound, uint256 upperBound) = freezer.getFreezeBound();
assertEq(lowerBound, config.freezeLowerBound, 'wrong freeze lower bound');
assertEq(upperBound, config.freezeUpperBound, 'wrong freeze upper bound');
(lowerBound, upperBound) = freezer.getUnfreezeBound();
assertEq(lowerBound, config.unfreezeLowerBound, 'wrong unfreeze lower bound');
assertEq(upperBound, config.unfreezeUpperBound, 'wrong unfreeze upper bound');
}
}
Loading