-
Notifications
You must be signed in to change notification settings - Fork 39
Migrate Mainnet GSMs to RemoteGSM #897
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,286 @@ | ||||||||||
| // SPDX-License-Identifier: MIT | ||||||||||
| pragma solidity ^0.8.0; | ||||||||||
|
|
||||||||||
| import {IERC20} from 'openzeppelin-contracts/contracts/token/ERC20/IERC20.sol'; | ||||||||||
| import {SafeERC20} from 'openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol'; | ||||||||||
| import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; | ||||||||||
| import {GhoEthereum} from 'aave-address-book/GhoEthereum.sol'; | ||||||||||
| import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; | ||||||||||
| import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; | ||||||||||
| import {CollectorUtils, ICollector} from 'aave-helpers/src/CollectorUtils.sol'; | ||||||||||
| import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; | ||||||||||
|
|
||||||||||
| import {IGhoBucketSteward} from 'src/interfaces/IGhoBucketSteward.sol'; | ||||||||||
| import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; | ||||||||||
| import {IGsm} from 'src/interfaces/IGsm.sol'; | ||||||||||
| import {IGsmRegistry} from 'src/interfaces/IGsmRegistry.sol'; | ||||||||||
| import {IAaveCLRobotOperator} from 'src/interfaces/IAaveCLRobotOperator.sol'; | ||||||||||
| import {IOwnableFacilitator} from 'src/interfaces/IOwnableFacilitator.sol'; | ||||||||||
| import {IGhoReserve} from 'src/interfaces/IGhoReserve.sol'; | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * @title GSM Migration | ||||||||||
| * @author @TokenLogic | ||||||||||
| * - Snapshot: https://snapshot.box/#/s:aavedao.eth/proposal/0xeb3572580924976867073ad9c8012cb9e52093c76dafebd7d3aebf318f2576fb | ||||||||||
| * - Discussion: https://governance.aave.com/t/arfc-launch-gho-on-plasma-set-aci-as-emissions-manager-for-rewards/22994/8 | ||||||||||
| */ | ||||||||||
| contract AaveV3Ethereum_GSMMigration_20251113 is IProposalGenericExecutor { | ||||||||||
| using SafeERC20 for IERC20; | ||||||||||
| using CollectorUtils for ICollector; | ||||||||||
|
|
||||||||||
| // OwnableFacilitator Constants | ||||||||||
| address public constant OWNABLE_FACILITATOR = 0x616AEe98F73C79FE59548Cfe7631c0baDBdA3165; | ||||||||||
| string public constant OWNABLE_FACILITATOR_NAME = 'OwnableFacilitator Gho GSMs Mainnet'; | ||||||||||
| uint128 public constant OWNABLE_FACILITATOR_CAPACITY = 85_000_000 ether; | ||||||||||
|
|
||||||||||
| // GhoReserve | ||||||||||
| // https://etherscan.io/address/0x0b0C0d8346F69EE94D29405f5630fc883A1052ab | ||||||||||
| address public constant GHO_RESERVE = 0x0b0C0d8346F69EE94D29405f5630fc883A1052ab; | ||||||||||
|
|
||||||||||
| // GSM Draw Limits | ||||||||||
| uint128 public constant USDC_GSM_RESERVE_LIMIT = 55_000_000 ether; | ||||||||||
| uint128 public constant USDT_GSM_RESERVE_LIMIT = 30_000_000 ether; | ||||||||||
|
Comment on lines
+41
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| // https://etherscan.io/address/0x3a3868898305f04bec7fea77becff04c13444112 | ||||||||||
| address public constant NEW_GSM_USDC = 0x3A3868898305f04beC7FEa77BecFf04C13444112; | ||||||||||
|
|
||||||||||
| // https://etherscan.io/address/0x6e51936e0ED4256f9dA4794B536B619c88Ff0047 | ||||||||||
| address public constant USDC_ORACLE_SWAP_FREEZER = 0x6e51936e0ED4256f9dA4794B536B619c88Ff0047; | ||||||||||
|
|
||||||||||
| // https://etherscan.io/address/0x882285e62656b9623af136ce3078c6bdcc33f5e3 | ||||||||||
| address public constant NEW_GSM_USDT = 0x882285E62656b9623AF136Ce3078c6BdCc33F5E3; | ||||||||||
|
|
||||||||||
| // https://etherscan.io/address/0x733AB16005c39d07FD3D9d1A350AA6768D10125b | ||||||||||
| address public constant USDT_ORACLE_SWAP_FREEZER = 0x733AB16005c39d07FD3D9d1A350AA6768D10125b; | ||||||||||
|
|
||||||||||
| // https://etherscan.io/address/0xE5025A7c15a44283A0616567181587eE6A646D64 | ||||||||||
| address public constant FEE_STRATEGY_USDC = 0xE5025A7c15a44283A0616567181587eE6A646D64; | ||||||||||
|
|
||||||||||
| // https://etherscan.io/address/0xA4346AEa575fCf5777D32F419E5850E5c68B2329 | ||||||||||
| address public constant FEE_STRATEGY_USDT = 0xA4346AEa575fCf5777D32F419E5850E5c68B2329; | ||||||||||
|
|
||||||||||
| uint96 public constant LINK_AMOUNT_ORACLE_FREEZER_KEEPER = 80 ether; | ||||||||||
| uint96 public constant TOTAL_LINK_AMOUNT_KEEPERS = LINK_AMOUNT_ORACLE_FREEZER_KEEPER * 2; // 2 GSMs | ||||||||||
| uint32 public constant KEEPER_GAS_LIMIT = 150_000; | ||||||||||
|
|
||||||||||
| uint256 public constant EXISTING_ORACLE_SWAP_FREEZER_USDC = | ||||||||||
| 85153843967789017760384794934034524869526055173666527804449435339462659418687; | ||||||||||
| uint256 public constant EXISTING_ORACLE_SWAP_FREEZER_USDT = | ||||||||||
| 113117985912495124427864354142901529291134634735835568280477108198234580494999; | ||||||||||
|
|
||||||||||
| bytes32 public immutable LIQUIDATOR_ROLE = IGsm(GhoEthereum.GSM_USDC).LIQUIDATOR_ROLE(); | ||||||||||
| bytes32 public immutable SWAP_FREEZER_ROLE = IGsm(GhoEthereum.GSM_USDC).SWAP_FREEZER_ROLE(); | ||||||||||
|
|
||||||||||
| function execute() external { | ||||||||||
| uint256 balanceUsdc = IERC20(AaveV3EthereumAssets.USDC_STATA_TOKEN).balanceOf( | ||||||||||
| GhoEthereum.GSM_USDC | ||||||||||
| ); | ||||||||||
| uint256 balanceUsdt = IERC20(AaveV3EthereumAssets.USDT_STATA_TOKEN).balanceOf( | ||||||||||
| GhoEthereum.GSM_USDT | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).addFacilitator( | ||||||||||
| OWNABLE_FACILITATOR, | ||||||||||
| OWNABLE_FACILITATOR_NAME, | ||||||||||
| OWNABLE_FACILITATOR_CAPACITY | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| _seize(); | ||||||||||
| _grantAccess(); | ||||||||||
| _updateFeeStrategy(); | ||||||||||
| _registerOracles(); | ||||||||||
| _fund(balanceUsdc, balanceUsdt); | ||||||||||
| _revokeAccess(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function _seize() internal { | ||||||||||
| IGsm(GhoEthereum.GSM_USDC).grantRole(LIQUIDATOR_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).grantRole(LIQUIDATOR_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
|
|
||||||||||
| IGsm(GhoEthereum.GSM_USDC).seize(); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).seize(); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function _grantAccess() internal { | ||||||||||
| address[] memory vaults = new address[](1); | ||||||||||
| vaults[0] = OWNABLE_FACILITATOR; | ||||||||||
| IGhoBucketSteward(GhoEthereum.GHO_BUCKET_STEWARD).setControlledFacilitator(vaults, true); | ||||||||||
|
|
||||||||||
| // Enroll GSMs as entities and set limit | ||||||||||
| IGhoReserve(GHO_RESERVE).addEntity(NEW_GSM_USDC); | ||||||||||
| IGhoReserve(GHO_RESERVE).addEntity(NEW_GSM_USDT); | ||||||||||
|
|
||||||||||
| IGhoReserve(GHO_RESERVE).setLimit(NEW_GSM_USDC, USDC_GSM_RESERVE_LIMIT); | ||||||||||
| IGhoReserve(GHO_RESERVE).setLimit(NEW_GSM_USDT, USDT_GSM_RESERVE_LIMIT); | ||||||||||
|
|
||||||||||
| // Add GSM Swap Freezer role to OracleSwapFreezers | ||||||||||
| IGsm(NEW_GSM_USDC).grantRole(SWAP_FREEZER_ROLE, USDC_ORACLE_SWAP_FREEZER); | ||||||||||
| IGsm(NEW_GSM_USDT).grantRole(SWAP_FREEZER_ROLE, USDT_ORACLE_SWAP_FREEZER); | ||||||||||
| IGsm(NEW_GSM_USDC).grantRole(SWAP_FREEZER_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
| IGsm(NEW_GSM_USDT).grantRole(SWAP_FREEZER_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
|
|
||||||||||
| // Add GSMs to GSM Registry | ||||||||||
| IGsmRegistry(GhoEthereum.GSM_REGISTRY).addGsm(NEW_GSM_USDC); | ||||||||||
| IGsmRegistry(GhoEthereum.GSM_REGISTRY).addGsm(NEW_GSM_USDT); | ||||||||||
|
|
||||||||||
| // GHO GSM Steward | ||||||||||
| IGsm(NEW_GSM_USDC).grantRole( | ||||||||||
| IGsm(NEW_GSM_USDC).CONFIGURATOR_ROLE(), | ||||||||||
| GhoEthereum.GHO_GSM_STEWARD | ||||||||||
| ); | ||||||||||
| IGsm(NEW_GSM_USDT).grantRole( | ||||||||||
| IGsm(NEW_GSM_USDT).CONFIGURATOR_ROLE(), | ||||||||||
| GhoEthereum.GHO_GSM_STEWARD | ||||||||||
| ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function _updateFeeStrategy() internal { | ||||||||||
| IGsm(NEW_GSM_USDC).updateFeeStrategy(FEE_STRATEGY_USDC); | ||||||||||
| IGsm(NEW_GSM_USDT).updateFeeStrategy(FEE_STRATEGY_USDT); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function _registerOracles() internal { | ||||||||||
| uint256 withdrawnBalance = AaveV3Ethereum.COLLECTOR.withdrawFromV3( | ||||||||||
| CollectorUtils.IOInput({ | ||||||||||
| pool: address(AaveV3Ethereum.POOL), | ||||||||||
| underlying: AaveV3EthereumAssets.LINK_UNDERLYING, | ||||||||||
| amount: TOTAL_LINK_AMOUNT_KEEPERS | ||||||||||
| }), | ||||||||||
| address(this) | ||||||||||
| ); | ||||||||||
| IERC20(AaveV3EthereumAssets.LINK_UNDERLYING).forceApprove( | ||||||||||
| MiscEthereum.AAVE_CL_ROBOT_OPERATOR, | ||||||||||
| withdrawnBalance | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| IAaveCLRobotOperator(MiscEthereum.AAVE_CL_ROBOT_OPERATOR).register( | ||||||||||
| 'GHO GSM 4626 stataUSDC OracleSwapFreezer', | ||||||||||
| USDC_ORACLE_SWAP_FREEZER, | ||||||||||
| '', | ||||||||||
| KEEPER_GAS_LIMIT, | ||||||||||
| LINK_AMOUNT_ORACLE_FREEZER_KEEPER, | ||||||||||
| 0, | ||||||||||
| '' | ||||||||||
| ); | ||||||||||
| IAaveCLRobotOperator(MiscEthereum.AAVE_CL_ROBOT_OPERATOR).register( | ||||||||||
| 'GHO GSM 4626 stataUSDT OracleSwapFreezer', | ||||||||||
| USDT_ORACLE_SWAP_FREEZER, | ||||||||||
| '', | ||||||||||
| KEEPER_GAS_LIMIT, | ||||||||||
| uint96(withdrawnBalance) - LINK_AMOUNT_ORACLE_FREEZER_KEEPER, | ||||||||||
| 0, | ||||||||||
| '' | ||||||||||
| ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function _fund(uint256 balanceUsdc, uint256 balanceUsdt) internal { | ||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. given that these balances are from stataUsdc and stataUsdt, are all the following operations precise? or we can incur in 1 wei diff. Also, are we ending up with 0 values in old GSM instances? can we have tests for this? |
||||||||||
| IGsm(GhoEthereum.GSM_USDC).distributeFeesToTreasury(); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).distributeFeesToTreasury(); | ||||||||||
|
|
||||||||||
| IOwnableFacilitator(OWNABLE_FACILITATOR).mint( | ||||||||||
| GHO_RESERVE, | ||||||||||
| USDC_GSM_RESERVE_LIMIT + USDT_GSM_RESERVE_LIMIT | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| AaveV3Ethereum.COLLECTOR.transfer( | ||||||||||
| IERC20(AaveV3EthereumAssets.USDC_STATA_TOKEN), | ||||||||||
| address(this), | ||||||||||
| balanceUsdc | ||||||||||
| ); | ||||||||||
| AaveV3Ethereum.COLLECTOR.transfer( | ||||||||||
| IERC20(AaveV3EthereumAssets.USDT_STATA_TOKEN), | ||||||||||
| address(this), | ||||||||||
| balanceUsdt | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| IERC20(AaveV3EthereumAssets.USDC_STATA_TOKEN).forceApprove(NEW_GSM_USDC, balanceUsdc); | ||||||||||
| IERC20(AaveV3EthereumAssets.USDT_STATA_TOKEN).forceApprove(NEW_GSM_USDT, balanceUsdt); | ||||||||||
|
|
||||||||||
| (, uint256 amountGhoUsdc) = IGsm(NEW_GSM_USDC).sellAsset(balanceUsdc, address(this)); | ||||||||||
| (, uint256 amountGhoUsdt) = IGsm(NEW_GSM_USDT).sellAsset(balanceUsdt, address(this)); | ||||||||||
|
|
||||||||||
| (, uint256 ghoUsdcNeeded) = IGhoToken(GhoEthereum.GHO_TOKEN).getFacilitatorBucket( | ||||||||||
| GhoEthereum.GSM_USDC | ||||||||||
| ); | ||||||||||
| (, uint256 ghoUsdtNeeded) = IGhoToken(GhoEthereum.GHO_TOKEN).getFacilitatorBucket( | ||||||||||
| GhoEthereum.GSM_USDT | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| uint256 acquiredGho = amountGhoUsdc + amountGhoUsdt; | ||||||||||
| uint256 mintedGho = ghoUsdcNeeded + ghoUsdtNeeded; | ||||||||||
|
|
||||||||||
| if (mintedGho > acquiredGho) { | ||||||||||
| AaveV3Ethereum.COLLECTOR.transfer( | ||||||||||
| IERC20(GhoEthereum.GHO_TOKEN), | ||||||||||
| address(this), | ||||||||||
| mintedGho - acquiredGho | ||||||||||
| ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| IERC20(GhoEthereum.GHO_TOKEN).forceApprove(GhoEthereum.GSM_USDC, ghoUsdcNeeded); | ||||||||||
| IERC20(GhoEthereum.GHO_TOKEN).forceApprove(GhoEthereum.GSM_USDT, ghoUsdtNeeded); | ||||||||||
|
|
||||||||||
| IGsm(GhoEthereum.GSM_USDC).burnAfterSeize(ghoUsdcNeeded); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).burnAfterSeize(ghoUsdtNeeded); | ||||||||||
|
|
||||||||||
| // Send to collector any positive difference | ||||||||||
| IERC20(GhoEthereum.GHO_TOKEN).transfer( | ||||||||||
| address(AaveV3Ethereum.COLLECTOR), | ||||||||||
| IERC20(GhoEthereum.GHO_TOKEN).balanceOf(address(this)) | ||||||||||
| ); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function _revokeAccess() internal { | ||||||||||
| // Remove existing GSMs as GHO Facilitators | ||||||||||
| IGhoToken(GhoEthereum.GHO_TOKEN).removeFacilitator(GhoEthereum.GSM_USDC); | ||||||||||
| IGhoToken(GhoEthereum.GHO_TOKEN).removeFacilitator(GhoEthereum.GSM_USDT); | ||||||||||
|
|
||||||||||
| // Revoke existing GSMs | ||||||||||
| address[] memory revokedVaults = new address[](2); | ||||||||||
| revokedVaults[0] = GhoEthereum.GSM_USDC; | ||||||||||
| revokedVaults[1] = GhoEthereum.GSM_USDT; | ||||||||||
| IGhoBucketSteward(GhoEthereum.GHO_BUCKET_STEWARD).setControlledFacilitator( | ||||||||||
| revokedVaults, | ||||||||||
| false | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| // Remove existing GSMs from Registry | ||||||||||
| IGsmRegistry(GhoEthereum.GSM_REGISTRY).removeGsm(GhoEthereum.GSM_USDC); | ||||||||||
| IGsmRegistry(GhoEthereum.GSM_REGISTRY).removeGsm(GhoEthereum.GSM_USDT); | ||||||||||
|
|
||||||||||
| // Revoke Roles | ||||||||||
| IGsm(GhoEthereum.GSM_USDC).revokeRole( | ||||||||||
| SWAP_FREEZER_ROLE, | ||||||||||
| GhoEthereum.GSM_USDC_ORACLE_SWAP_FREEZER | ||||||||||
| ); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).revokeRole( | ||||||||||
| SWAP_FREEZER_ROLE, | ||||||||||
| GhoEthereum.GSM_USDT_ORACLE_SWAP_FREEZER | ||||||||||
| ); | ||||||||||
| IGsm(GhoEthereum.GSM_USDC).revokeRole(SWAP_FREEZER_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).revokeRole(SWAP_FREEZER_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
|
|
||||||||||
| IGsm(GhoEthereum.GSM_USDC).revokeRole(LIQUIDATOR_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).revokeRole(LIQUIDATOR_ROLE, GovernanceV3Ethereum.EXECUTOR_LVL_1); | ||||||||||
|
|
||||||||||
| // GHO GSM Steward | ||||||||||
| IGsm(GhoEthereum.GSM_USDC).revokeRole( | ||||||||||
| IGsm(GhoEthereum.GSM_USDC).CONFIGURATOR_ROLE(), | ||||||||||
| GhoEthereum.GHO_GSM_STEWARD | ||||||||||
| ); | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).revokeRole( | ||||||||||
| IGsm(GhoEthereum.GSM_USDT).CONFIGURATOR_ROLE(), | ||||||||||
| GhoEthereum.GHO_GSM_STEWARD | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| // Cancel existing keepers | ||||||||||
| IAaveCLRobotOperator(MiscEthereum.AAVE_CL_ROBOT_OPERATOR).cancel( | ||||||||||
| EXISTING_ORACLE_SWAP_FREEZER_USDC | ||||||||||
| ); | ||||||||||
| IAaveCLRobotOperator(MiscEthereum.AAVE_CL_ROBOT_OPERATOR).cancel( | ||||||||||
| EXISTING_ORACLE_SWAP_FREEZER_USDT | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| // Manually withdraw LINK from existing keepers permissionesly ~20 blocks after cancel | ||||||||||
| } | ||||||||||
| } | ||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably enough. We can also use
EthereumGSM.For reference, other facilitator names:
CoreGhoDirectMinter,LidoGhoDirectMinter,HorizonGhoDirectMinter