From a2ef35fac482381c5c83a277e0bea27a0ba9ed4b Mon Sep 17 00:00:00 2001 From: Harsh Pandey Date: Tue, 22 Oct 2024 16:58:26 +0530 Subject: [PATCH 01/15] feat: ezETH listing on lido (#495) * feat: ezETH listing on lido * feat: add more test and writeup * chore: add diff snapshots * chore: update helpers * fix: rename emode * fix: remove asset from flashloanable * feat: add capo ezETH config * fix: aci as author * fix: author * chore: update writeup for more info on capo * fix: wstETH removal from borrowable and cap increase * fix: update capo feed * chore: fix writeup * chore: cleanup tests * chore: update writeup --------- Co-authored-by: ianflexa --- ...b02c78bc8b99547929074a8d98940dc76ac4e3.svg | 1 + ...30aea0d7ed061d9d9b0eeecb2e4835c7844ba5.svg | 1 + ...7a3ee47355f466dc627787f8cd371573621162.svg | 1 + ...3d4c8affcd41504dffeb1e9750d8e5357e2775.svg | 1 + ...cd89f015c573a55ccf57593f88a74f7b8bc050.svg | 1 + ...boardEzETHToLidoInstance_20241021_after.md | 197 ++++++++++++++++++ lib/aave-helpers | 2 +- ...do_OnboardEzETHToLidoInstance_20241021.sol | 152 ++++++++++++++ ..._OnboardEzETHToLidoInstance_20241021.t.sol | 175 ++++++++++++++++ .../OnboardEzETHToLidoInstance.md | 108 ++++++++++ .../OnboardEzETHToLidoInstance_20241021.s.sol | 60 ++++++ .../config.ts | 49 +++++ 12 files changed, 747 insertions(+), 1 deletion(-) create mode 100644 .assets/20b02c78bc8b99547929074a8d98940dc76ac4e3.svg create mode 100644 .assets/3b30aea0d7ed061d9d9b0eeecb2e4835c7844ba5.svg create mode 100644 .assets/4f7a3ee47355f466dc627787f8cd371573621162.svg create mode 100644 .assets/873d4c8affcd41504dffeb1e9750d8e5357e2775.svg create mode 100644 .assets/92cd89f015c573a55ccf57593f88a74f7b8bc050.svg create mode 100644 diffs/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_before_AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_after.md create mode 100644 src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol create mode 100644 src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol create mode 100644 src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md create mode 100644 src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance_20241021.s.sol create mode 100644 src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/config.ts diff --git a/.assets/20b02c78bc8b99547929074a8d98940dc76ac4e3.svg b/.assets/20b02c78bc8b99547929074a8d98940dc76ac4e3.svg new file mode 100644 index 000000000..bb652b1a0 --- /dev/null +++ b/.assets/20b02c78bc8b99547929074a8d98940dc76ac4e3.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%50%100%Optimal utilization 45%Optimal utilization 45% \ No newline at end of file diff --git a/.assets/3b30aea0d7ed061d9d9b0eeecb2e4835c7844ba5.svg b/.assets/3b30aea0d7ed061d9d9b0eeecb2e4835c7844ba5.svg new file mode 100644 index 000000000..3c15ce1a6 --- /dev/null +++ b/.assets/3b30aea0d7ed061d9d9b0eeecb2e4835c7844ba5.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%100%200%300%Optimal utilization 45%Optimal utilization 45% \ No newline at end of file diff --git a/.assets/4f7a3ee47355f466dc627787f8cd371573621162.svg b/.assets/4f7a3ee47355f466dc627787f8cd371573621162.svg new file mode 100644 index 000000000..0bd7d00f7 --- /dev/null +++ b/.assets/4f7a3ee47355f466dc627787f8cd371573621162.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%50%100%Optimal utilization 92%Optimal utilization 92% \ No newline at end of file diff --git a/.assets/873d4c8affcd41504dffeb1e9750d8e5357e2775.svg b/.assets/873d4c8affcd41504dffeb1e9750d8e5357e2775.svg new file mode 100644 index 000000000..fc56a2cce --- /dev/null +++ b/.assets/873d4c8affcd41504dffeb1e9750d8e5357e2775.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%50%100%Optimal utilization 90%Optimal utilization 90% \ No newline at end of file diff --git a/.assets/92cd89f015c573a55ccf57593f88a74f7b8bc050.svg b/.assets/92cd89f015c573a55ccf57593f88a74f7b8bc050.svg new file mode 100644 index 000000000..e673fbc39 --- /dev/null +++ b/.assets/92cd89f015c573a55ccf57593f88a74f7b8bc050.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%20%40%60%80%Optimal utilization 92%Optimal utilization 92% \ No newline at end of file diff --git a/diffs/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_before_AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_after.md b/diffs/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_before_AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_after.md new file mode 100644 index 000000000..01f3b66ad --- /dev/null +++ b/diffs/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_before_AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_after.md @@ -0,0 +1,197 @@ +## Reserve changes + +### Reserves added + +#### ezETH ([0xbf5495Efe5DB9ce00f80364C8B423567e58d2110](https://etherscan.io/address/0xbf5495Efe5DB9ce00f80364C8B423567e58d2110)) + +| description | value | +| --- | --- | +| decimals | 18 | +| isActive | true | +| isFrozen | false | +| supplyCap | 15,000 ezETH | +| borrowCap | 100 ezETH | +| debtCeiling | 0 $ [0] | +| isSiloed | false | +| isFlashloanable | false | +| oracle | [0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee](https://etherscan.io/address/0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee) | +| oracleDecimals | 8 | +| oracleDescription | Capped ezETH / ezETH(ETH) / USD | +| oracleLatestAnswer | 2714.95678736 | +| usageAsCollateralEnabled | true | +| ltv | 0.05 % [5] | +| liquidationThreshold | 0.1 % [10] | +| liquidationBonus | 7.5 % | +| liquidationProtocolFee | 10 % [1000] | +| reserveFactor | 15 % [1500] | +| aToken | [0x74e5664394998f13B07aF42446380ACef637969f](https://etherscan.io/address/0x74e5664394998f13B07aF42446380ACef637969f) | +| aTokenImpl | [0x7F8Fc14D462bdF93c681c1f2Fd615389bF969Fb2](https://etherscan.io/address/0x7F8Fc14D462bdF93c681c1f2Fd615389bF969Fb2) | +| variableDebtToken | [0x08e1bba76D27841dD91FAb4b3a636A0D5CF8c3E9](https://etherscan.io/address/0x08e1bba76D27841dD91FAb4b3a636A0D5CF8c3E9) | +| variableDebtTokenImpl | [0x3E59212c34588a63350142EFad594a20C88C2CEd](https://etherscan.io/address/0x3E59212c34588a63350142EFad594a20C88C2CEd) | +| borrowingEnabled | false | +| isBorrowableInIsolation | false | +| interestRateStrategy | [0x8958b1C39269167527821f8c276Ef7504883f2fa](https://etherscan.io/address/0x8958b1C39269167527821f8c276Ef7504883f2fa) | +| aTokenName | Aave Ethereum Lido ezETH | +| aTokenSymbol | aEthLidoezETH | +| aTokenUnderlyingBalance | 0.1 ezETH [100000000000000000] | +| id | 4 | +| isPaused | false | +| variableDebtTokenName | Aave Ethereum Lido Variable Debt ezETH | +| variableDebtTokenSymbol | variableDebtEthLidoezETH | +| virtualAccountingActive | true | +| virtualBalance | 0.1 ezETH [100000000000000000] | +| optimalUsageRatio | 45 % | +| maxVariableBorrowRate | 307 % | +| baseVariableBorrowRate | 0 % | +| variableRateSlope1 | 7 % | +| variableRateSlope2 | 300 % | +| interestRate | ![ir](/.assets/3b30aea0d7ed061d9d9b0eeecb2e4835c7844ba5.svg) | + + +### Reserves altered + +#### wstETH ([0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0](https://etherscan.io/address/0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0)) + +| description | value before | value after | +| --- | --- | --- | +| borrowCap | 100 wstETH | 14,000 wstETH | + + +## Emodes changed + +### EMode: ETH correlated(id: 1) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label (unchanged) | ETH correlated | ETH correlated | +| eMode.ltv (unchanged) | 93.5 % | 93.5 % | +| eMode.liquidationThreshold (unchanged) | 95.5 % | 95.5 % | +| eMode.liquidationBonus (unchanged) | 1 % | 1 % | +| eMode.borrowableBitmap | wstETH, WETH | WETH | +| eMode.collateralBitmap (unchanged) | wstETH, WETH | wstETH, WETH | + + +### EMode: LRT Stablecoins main(id: 2) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | LRT Stablecoins main | +| eMode.ltv | - | 75 % | +| eMode.liquidationThreshold | - | 78 % | +| eMode.liquidationBonus | - | 7.5 % | +| eMode.borrowableBitmap | - | USDS | +| eMode.collateralBitmap | - | ezETH | + + +### EMode: LRT wstETH main(id: 3) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | LRT wstETH main | +| eMode.ltv | - | 93 % | +| eMode.liquidationThreshold | - | 95 % | +| eMode.liquidationBonus | - | 1 % | +| eMode.borrowableBitmap | - | wstETH | +| eMode.collateralBitmap | - | ezETH | + + +## Raw diff + +```json +{ + "eModes": { + "1": { + "borrowableBitmap": { + "from": "3", + "to": "2" + } + }, + "2": { + "from": null, + "to": { + "borrowableBitmap": "4", + "collateralBitmap": "16", + "eModeCategory": 2, + "label": "LRT Stablecoins main", + "liquidationBonus": 10750, + "liquidationThreshold": 7800, + "ltv": 7500 + } + }, + "3": { + "from": null, + "to": { + "borrowableBitmap": "1", + "collateralBitmap": "16", + "eModeCategory": 3, + "label": "LRT wstETH main", + "liquidationBonus": 10100, + "liquidationThreshold": 9500, + "ltv": 9300 + } + } + }, + "reserves": { + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0": { + "borrowCap": { + "from": 100, + "to": 14000 + } + }, + "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110": { + "from": null, + "to": { + "aToken": "0x74e5664394998f13B07aF42446380ACef637969f", + "aTokenImpl": "0x7F8Fc14D462bdF93c681c1f2Fd615389bF969Fb2", + "aTokenName": "Aave Ethereum Lido ezETH", + "aTokenSymbol": "aEthLidoezETH", + "aTokenUnderlyingBalance": "100000000000000000", + "borrowCap": 100, + "borrowingEnabled": false, + "debtCeiling": 0, + "decimals": 18, + "id": 4, + "interestRateStrategy": "0x8958b1C39269167527821f8c276Ef7504883f2fa", + "isActive": true, + "isBorrowableInIsolation": false, + "isFlashloanable": false, + "isFrozen": false, + "isPaused": false, + "isSiloed": false, + "liquidationBonus": 10750, + "liquidationProtocolFee": 1000, + "liquidationThreshold": 10, + "ltv": 5, + "oracle": "0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee", + "oracleDecimals": 8, + "oracleDescription": "Capped ezETH / ezETH(ETH) / USD", + "oracleLatestAnswer": "271495678736", + "reserveFactor": 1500, + "supplyCap": 15000, + "symbol": "ezETH", + "underlying": "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110", + "usageAsCollateralEnabled": true, + "variableDebtToken": "0x08e1bba76D27841dD91FAb4b3a636A0D5CF8c3E9", + "variableDebtTokenImpl": "0x3E59212c34588a63350142EFad594a20C88C2CEd", + "variableDebtTokenName": "Aave Ethereum Lido Variable Debt ezETH", + "variableDebtTokenSymbol": "variableDebtEthLidoezETH", + "virtualAccountingActive": true, + "virtualBalance": "100000000000000000" + } + } + }, + "strategies": { + "0xbf5495Efe5DB9ce00f80364C8B423567e58d2110": { + "from": null, + "to": { + "address": "0x8958b1C39269167527821f8c276Ef7504883f2fa", + "baseVariableBorrowRate": "0", + "maxVariableBorrowRate": "3070000000000000000000000000", + "optimalUsageRatio": "450000000000000000000000000", + "variableRateSlope1": "70000000000000000000000000", + "variableRateSlope2": "3000000000000000000000000000" + } + } + } +} +``` \ No newline at end of file diff --git a/lib/aave-helpers b/lib/aave-helpers index 7bbdb8bdd..2a943191c 160000 --- a/lib/aave-helpers +++ b/lib/aave-helpers @@ -1 +1 @@ -Subproject commit 7bbdb8bddf62d45bfb6a91f3ad10bba23a4b8f0a +Subproject commit 2a943191c1c4cadf88cb209c2ae622ce241ee912 diff --git a/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol new file mode 100644 index 000000000..d61358aa2 --- /dev/null +++ b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumLido, AaveV3EthereumLidoAssets, AaveV3EthereumLidoEModes} from 'aave-address-book/AaveV3EthereumLido.sol'; +import {AaveV3PayloadEthereumLido} from 'aave-helpers/src/v3-config-engine/AaveV3PayloadEthereumLido.sol'; +import {EngineFlags} from 'aave-v3-origin/contracts/extensions/v3-config-engine/EngineFlags.sol'; +import {IAaveV3ConfigEngine} from 'aave-v3-origin/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; + +/** + * @title Onboard ezETH to Lido Instance + * @author Aave Chan Initiative + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x7ef22a28cb6729ea4a978b02332ff1af8ed924a726915f9a6debf835d8bf8048 + * - Discussion: https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504/11 + */ +contract AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021 is AaveV3PayloadEthereumLido { + using SafeERC20 for IERC20; + + address public constant ezETH = 0xbf5495Efe5DB9ce00f80364C8B423567e58d2110; + uint256 public constant ezETH_SEED_AMOUNT = 0.1 ether; + address public constant ezETH_LM_ADMIN = 0xac140648435d03f784879cd789130F22Ef588Fcd; + + function _postExecute() internal override { + IERC20(ezETH).forceApprove(address(AaveV3EthereumLido.POOL), ezETH_SEED_AMOUNT); + AaveV3EthereumLido.POOL.supply( + ezETH, + ezETH_SEED_AMOUNT, + address(AaveV3EthereumLido.COLLECTOR), + 0 + ); + + (address aezETH, , ) = AaveV3EthereumLido.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + ezETH + ); + IEmissionManager(AaveV3EthereumLido.EMISSION_MANAGER).setEmissionAdmin(ezETH, ezETH_LM_ADMIN); + IEmissionManager(AaveV3EthereumLido.EMISSION_MANAGER).setEmissionAdmin(aezETH, ezETH_LM_ADMIN); + } + + function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) { + IAaveV3ConfigEngine.Listing[] memory listings = new IAaveV3ConfigEngine.Listing[](1); + + listings[0] = IAaveV3ConfigEngine.Listing({ + asset: ezETH, + assetSymbol: 'ezETH', + priceFeed: 0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee, + enabledToBorrow: EngineFlags.DISABLED, + borrowableInIsolation: EngineFlags.DISABLED, + withSiloedBorrowing: EngineFlags.DISABLED, + flashloanable: EngineFlags.DISABLED, + ltv: 5, + liqThreshold: 10, + liqBonus: 7_50, + reserveFactor: 15_00, + supplyCap: 15_000, + borrowCap: 100, + debtCeiling: 0, + liqProtocolFee: 10_00, + rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({ + optimalUsageRatio: 45_00, + baseVariableBorrowRate: 0, + variableRateSlope1: 7_00, + variableRateSlope2: 300_00 + }) + }); + + return listings; + } + + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](2); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 2, + ltv: 75_00, + liqThreshold: 78_00, + liqBonus: 7_50, + label: 'LRT Stablecoins main' + }); + eModeUpdates[1] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 3, + ltv: 93_00, + liqThreshold: 95_00, + liqBonus: 1_00, + label: 'LRT wstETH main' + }); + + return eModeUpdates; + } + + function assetsEModeUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.AssetEModeUpdate[] memory) + { + IAaveV3ConfigEngine.AssetEModeUpdate[] + memory assetEModeUpdates = new IAaveV3ConfigEngine.AssetEModeUpdate[](5); + + assetEModeUpdates[0] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: ezETH, + eModeCategory: 2, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3EthereumLidoAssets.USDS_UNDERLYING, + eModeCategory: 2, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + assetEModeUpdates[2] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: ezETH, + eModeCategory: 3, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + assetEModeUpdates[3] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3EthereumLidoAssets.wstETH_UNDERLYING, + eModeCategory: 3, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + assetEModeUpdates[4] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3EthereumLidoAssets.wstETH_UNDERLYING, + eModeCategory: AaveV3EthereumLidoEModes.ETH_CORRELATED, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.KEEP_CURRENT + }); + + return assetEModeUpdates; + } + + function capsUpdates() public pure override returns (IAaveV3ConfigEngine.CapsUpdate[] memory) { + IAaveV3ConfigEngine.CapsUpdate[] memory capsUpdate = new IAaveV3ConfigEngine.CapsUpdate[](1); + + capsUpdate[0] = IAaveV3ConfigEngine.CapsUpdate({ + asset: AaveV3EthereumLidoAssets.wstETH_UNDERLYING, + supplyCap: EngineFlags.KEEP_CURRENT, + borrowCap: 14_000 + }); + + return capsUpdate; + } +} diff --git a/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol new file mode 100644 index 000000000..73d377a4f --- /dev/null +++ b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3EthereumLido, AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {Errors} from 'aave-v3-origin/contracts/protocol/libraries/helpers/Errors.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021} from './AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol'; + +/** + * @dev Test for AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol -vv + */ +contract AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021_Test is ProtocolV3TestBase { + AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021 internal proposal; + address internal ezETH; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21019869); + proposal = new AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021(); + ezETH = proposal.ezETH(); + + deal(ezETH, GovernanceV3Ethereum.EXECUTOR_LVL_1, proposal.ezETH_SEED_AMOUNT()); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021', + AaveV3EthereumLido.POOL, + address(proposal) + ); + } + + function test_collectorHasezETHFunds() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aTokenAddress, , ) = AaveV3EthereumLido + .AAVE_PROTOCOL_DATA_PROVIDER + .getReserveTokensAddresses(ezETH); + assertGe( + IERC20(aTokenAddress).balanceOf(address(AaveV3EthereumLido.COLLECTOR)), + proposal.ezETH_SEED_AMOUNT() + ); + } + + function test_ezETH_USDS_emode() public { + GovV3Helpers.executePayload(vm, address(proposal)); + + address user = address(5); + deal(ezETH, user, 1 ether); + deal(AaveV3EthereumLidoAssets.USDS_UNDERLYING, user, 3000 ether); + + vm.startPrank(user); + AaveV3EthereumLido.POOL.setUserEMode(2); + + IERC20(ezETH).approve(address(AaveV3EthereumLido.POOL), 1 ether); + IERC20(AaveV3EthereumLidoAssets.USDS_UNDERLYING).approve( + address(AaveV3EthereumLido.POOL), + 3000 ether + ); + + // USDS as collateral and ezETH as borrowable - reverts + AaveV3EthereumLido.POOL.supply(AaveV3EthereumLidoAssets.USDS_UNDERLYING, 3000 ether, user, 0); + vm.expectRevert(bytes(Errors.BORROWING_NOT_ENABLED)); + AaveV3EthereumLido.POOL.borrow(ezETH, 0.1 ether, 2, 0, user); + + // ezETH as collateral and USDS as borrowable + AaveV3EthereumLido.POOL.supply(ezETH, 1 ether, user, 0); + AaveV3EthereumLido.POOL.borrow( + AaveV3EthereumLidoAssets.USDS_UNDERLYING, + 1500 ether, + 2, + 0, + user + ); + } + + function test_ezETH_wstETH_emode() public { + GovV3Helpers.executePayload(vm, address(proposal)); + + address user = address(8); + deal(ezETH, user, 1 ether); + deal(AaveV3EthereumLidoAssets.wstETH_UNDERLYING, user, 1 ether); + + vm.startPrank(user); + AaveV3EthereumLido.POOL.setUserEMode(3); + + IERC20(ezETH).approve(address(AaveV3EthereumLido.POOL), 1 ether); + IERC20(AaveV3EthereumLidoAssets.wstETH_UNDERLYING).approve( + address(AaveV3EthereumLido.POOL), + 1 ether + ); + + // wstETH as collateral and ezETH as borrowable - reverts + AaveV3EthereumLido.POOL.supply(AaveV3EthereumLidoAssets.wstETH_UNDERLYING, 1 ether, user, 0); + vm.expectRevert(bytes(Errors.BORROWING_NOT_ENABLED)); + AaveV3EthereumLido.POOL.borrow(ezETH, 0.9 ether, 2, 0, user); + + // ezETH as collateral and wstETH as borrowable + AaveV3EthereumLido.POOL.supply(ezETH, 1 ether, user, 0); + AaveV3EthereumLido.POOL.borrow( + AaveV3EthereumLidoAssets.wstETH_UNDERLYING, + 0.9 ether, + 2, + 0, + user + ); + } + + function test_wstETH_removalFromBorroableOnEmodeOne() public { + address user = address(8); + deal(user, 1 ether); + deal(AaveV3EthereumLidoAssets.wstETH_UNDERLYING, user, 2 ether); + + vm.prank(GovernanceV3Ethereum.EXECUTOR_LVL_1); + // as caps have been reached + AaveV3EthereumLido.POOL_CONFIGURATOR.setBorrowCap( + AaveV3EthereumLidoAssets.wstETH_UNDERLYING, + 350_000 + ); + + vm.startPrank(user); + AaveV3EthereumLido.POOL.setUserEMode(1); + + IERC20(AaveV3EthereumLidoAssets.wstETH_UNDERLYING).approve( + address(AaveV3EthereumLido.POOL), + 1 ether + ); + AaveV3EthereumLido.POOL.supply(AaveV3EthereumLidoAssets.wstETH_UNDERLYING, 1 ether, user, 0); + AaveV3EthereumLido.POOL.borrow( + AaveV3EthereumLidoAssets.wstETH_UNDERLYING, + 0.92 ether, + 2, + 0, + user + ); + + (, , , , , uint256 prevHealthFactor) = AaveV3EthereumLido.POOL.getUserAccountData(user); + + GovV3Helpers.executePayload(vm, address(proposal)); + + (, , , , , uint256 currentHealthFactor) = AaveV3EthereumLido.POOL.getUserAccountData(user); + assertEq(prevHealthFactor, currentHealthFactor); + + // wstETH cannot be borrowed in EModeId 1 + vm.expectRevert(bytes(Errors.NOT_BORROWABLE_IN_EMODE)); + AaveV3EthereumLido.POOL.borrow( + AaveV3EthereumLidoAssets.wstETH_UNDERLYING, + 0.01 ether, + 2, + 0, + user + ); + } + + function test_ezETHAdmin() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aezETH, , ) = AaveV3EthereumLido.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + ezETH + ); + assertEq( + IEmissionManager(AaveV3EthereumLido.EMISSION_MANAGER).getEmissionAdmin(ezETH), + proposal.ezETH_LM_ADMIN() + ); + assertEq( + IEmissionManager(AaveV3EthereumLido.EMISSION_MANAGER).getEmissionAdmin(aezETH), + proposal.ezETH_LM_ADMIN() + ); + } +} diff --git a/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md new file mode 100644 index 000000000..ded000441 --- /dev/null +++ b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md @@ -0,0 +1,108 @@ +--- +title: "Onboard ezETH to Lido Instance" +author: "Aave Chan Initiative" +discussions: "https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504/11" +snapshot: "https://snapshot.org/#/aave.eth/proposal/0x7ef22a28cb6729ea4a978b02332ff1af8ed924a726915f9a6debf835d8bf8048" +--- + +## Simple Summary + +The proposal aims to onboard Renzo Protocol's [ezETH](https://etherscan.io/address/0xbf5495Efe5DB9ce00f80364C8B423567e58d2110) and increase wstETH borrow caps on Aave v3 Lido Instance. + +## Motivation + +Liquid Restaking tokens (LRTs) have proven to be popular collateral assets on Aave. ezETH is an LRT reward-bearing token, exchange-rate based, where users can deposit native ETH and LSTs (currently stETH) in exchange for ezETH. + +Given LRTs high correlation to ETH, they are commonly used as collateral to borrow ETH and engage in yield leveraged staking, with several communities having built products that automate such strategies on top of Aave. The inclusion of ezETH in E-Mode enables these strategies to maximize the yield potential of the recursive strategy. The onboarding of ezETH will consequently create increased ezETH demand and increased revenues for both Aave and Renzo Protocol, whilst also bolstering the liquidity and peg stability of ezETH. + +With the listing of ezETH and the introduction of an LRT wstETH E-Mode, Chaos Labs anticipates that ezETH’s primary use case will be as collateral for borrowing wstETH to leverage ezETH’s yield. And with the borrow cap currently reached for wstETH, we aim to increase its borrow cap. + +The risk parameters have been provided by Chaos Labs, and positive technical evaluation of ezETH is done by BGD Labs, all of which including LlamaRisk's qualitative assessment, can be found on the [forum](https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504). + +## Specification + +The table below illustrates the configured risk parameters for **[ezETH](https://etherscan.io/address/0xbf5495Efe5DB9ce00f80364C8B423567e58d2110)** + +| Parameter | Value | +| ------------------------- | --------------------------------------------------------------------------------------------------------------------: | +| Collateral Enabled | true | +| Borrowable | false | +| Supply Cap (ezETH) | 15,000 | +| Borrow Cap (ezETH) | 100 | +| Debt Ceiling | USD 0 | +| LTV | 0.05 % | +| LT | 0.1 % | +| Liquidation Bonus | 7.5 % | +| Liquidation Protocol Fee | 10 % | +| Reserve Factor | 15 % | +| Base Variable Borrow Rate | 0 % | +| Variable Slope 1 | 7 % | +| Variable Slope 2 | 300 % | +| Uoptimal | 45 % | +| Flashloanable | false | +| Siloed Borrowing | false | +| Isolation Mode | false | +| Borrowable in Isolation | false | +| Oracle | [0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee](https://etherscan.io/address/0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee) | +| E-Modes | LRT Stablecoins main, LRT wstETH main | + +_Please Note: Low LTV, LT values have been configured to only allow for collateral use of ezETH on E-Modes. Additionally low borrow cap has been configured as a matter of extra safety as borrow cap of 0 represents no cap at all._ + +The borrow for wstETH on Lido Instance has been increased, more info on the [forum](https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504/14). + +| Chain | Asset | Current Borrow Cap | New Borrow Cap | +| -------------------- | ------ | ------------------ | -------------- | +| Ethereum Lido Market | wstETH | 100 | 14,000 | + +With liquid eModes live on all instances, we now configure two new E-Mode categories: LRT Stablecoins main, LRT wstETH main and add the respective assets to these categories. + +#### E-mode Category: LRT Stablecoins main + +| Parameter | Value | Value | +| --------------------- | ----: | ----: | +| Category Id | 2 | 2 | +| Asset | ezETH | USDS | +| Collateral | Yes | No | +| Borrowable | No | Yes | +| LTV | 75% | 75% | +| Liquidation Threshold | 78% | 78% | +| Liquidation Penalty | 7.5% | 7.5% | + +#### E-mode Category: LRT wstETH main + +| Parameter | Value | Value | +| --------------------- | ----: | -----: | +| Category Id | 3 | 3 | +| Asset | ezETH | wstETH | +| Collateral | Yes | No | +| Borrowable | No | Yes | +| LTV | 93% | 93% | +| Liquidation Threshold | 95% | 95% | +| Liquidation Penalty | 1% | 1% | + +#### CAPO parameters configuration: + +| Parameter | Value | +| --------------------- | -----------------------: | +| min snapshot delay | 14 days | +| snapshot ratio | 1019883708003361006 | +| snapshot timestamp | 1727172839 (Sep-24-2024) | +| max yearly growth (%) | 10.89% | + +The [price capo adapter](https://etherscan.io/address/0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee) has been verified by Certora (more info on [forum](https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504/12)), validating that the price cap adapter is robust against donation-based price manipulation and poses no risk to the protocol. + +wstETH on the Lido Instance has been disabled from being borrowable in the ETH Correlated E-Mode (categoryId: 1). Users current positions will remain intact, but borrow exposure of wstETH in ETH Correlated E-Mode cannot be increased. + +Additionally [0xac140648435d03f784879cd789130F22Ef588Fcd](https://etherscan.io/address/0xac140648435d03f784879cd789130F22Ef588Fcd) (ACI multi-sig) has been set as the emission admin for ezETH and the corresponding aToken. + +## References + +- Implementation: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol) +- Tests: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol) +- [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x7ef22a28cb6729ea4a978b02332ff1af8ed924a726915f9a6debf835d8bf8048) +- [Discussion ezETH Onboarding](https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504/11) +- [Discussion wstETH Cap Change](https://governance.aave.com/t/arfc-chaos-labs-risk-parameter-updates-increase-borrow-caps-for-wsteth-on-the-lido-market-10-20-24/19539) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance_20241021.s.sol b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance_20241021.s.sol new file mode 100644 index 000000000..bdf5562ea --- /dev/null +++ b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance_20241021.s.sol @@ -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_OnboardEzETHToLidoInstance_20241021} from './AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance_20241021.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/OnboardEzETHToLidoInstance_20241021.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021).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/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance_20241021.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_OnboardEzETHToLidoInstance_20241021).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md' + ) + ); + } +} diff --git a/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/config.ts b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/config.ts new file mode 100644 index 000000000..bcb4aefb6 --- /dev/null +++ b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/config.ts @@ -0,0 +1,49 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3EthereumLido'], + title: 'Onboard ezETH to Lido Instance', + shortName: 'OnboardEzETHToLidoInstance', + date: '20241021', + author: 'BGD Labs (@bgdlabs)', + discussion: + 'https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504/11', + snapshot: + 'https://snapshot.org/#/aave.eth/proposal/0x7ef22a28cb6729ea4a978b02332ff1af8ed924a726915f9a6debf835d8bf8048', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3EthereumLido: { + configs: { + ASSET_LISTING: [ + { + assetSymbol: 'ezETH', + decimals: 18, + priceFeed: '0x68C9c7Bf43DBd0EBab102116bc7C3C9f7d9297Ee', + ltv: '0.05', + liqThreshold: '0.100', + liqBonus: '7.5', + debtCeiling: '0', + liqProtocolFee: '10', + enabledToBorrow: 'DISABLED', + flashloanable: 'ENABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '15', + supplyCap: '15000', + borrowCap: '100', + rateStrategyParams: { + optimalUtilizationRate: '45', + baseVariableBorrowRate: '0', + variableRateSlope1: '7', + variableRateSlope2: '300', + }, + asset: '0xbf5495efe5db9ce00f80364c8b423567e58d2110', + admin: '0xac140648435d03f784879cd789130F22Ef588Fcd', + }, + ], + }, + cache: {blockNumber: 21011916}, + }, + }, +}; From df380336b3c64ebfa143084c25df4282f21b77de Mon Sep 17 00:00:00 2001 From: Cache bot Date: Tue, 22 Oct 2024 11:28:59 +0000 Subject: [PATCH 02/15] fix(cache): automated cache update [skip ci] --- .../OnboardEzETHToLidoInstance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md index ded000441..111133405 100644 --- a/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md +++ b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/OnboardEzETHToLidoInstance.md @@ -97,8 +97,8 @@ Additionally [0xac140648435d03f784879cd789130F22Ef588Fcd](https://etherscan.io/a ## References -- Implementation: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol) -- Tests: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol) +- Implementation: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/a2ef35fac482381c5c83a277e0bea27a0ba9ed4b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.sol) +- Tests: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/a2ef35fac482381c5c83a277e0bea27a0ba9ed4b/src/20241021_AaveV3EthereumLido_OnboardEzETHToLidoInstance/AaveV3EthereumLido_OnboardEzETHToLidoInstance_20241021.t.sol) - [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x7ef22a28cb6729ea4a978b02332ff1af8ed924a726915f9a6debf835d8bf8048) - [Discussion ezETH Onboarding](https://governance.aave.com/t/arfc-onboard-ezeth-to-aave-v3-lido-instance/18504/11) - [Discussion wstETH Cap Change](https://governance.aave.com/t/arfc-chaos-labs-risk-parameter-updates-increase-borrow-caps-for-wsteth-on-the-lido-market-10-20-24/19539) From e63fc7d6052ae90eac55d373b52c1cf343acb9e9 Mon Sep 17 00:00:00 2001 From: MartinGbz Date: Wed, 23 Oct 2024 11:12:33 +0200 Subject: [PATCH 03/15] =?UTF-8?q?=E2=9C=A8=20Fix=20USDS=20Slope=201=20(#49?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ add fix on USDS slope 1 * ✅ ran test * 🎨 refactor keep current `baseVariableBorrowRate` because it already at 6.25 * 📝 update readme + forum link * 💬 update motivation readme text Co-authored-by: sendra * 📝 use post thread link instead of specific post link Co-authored-by: sendra * 💬 put back the post instead of the whole thread Co-authored-by: Pavel <56404416+pavelvm5@users.noreply.github.com> --------- Co-authored-by: sendra Co-authored-by: Pavel <56404416+pavelvm5@users.noreply.github.com> --- ...7c21391c0b67eb1236e3875c882cd6115a3758.svg | 1 + ...ateToMatchSkySavingsRate_20241022_after.md | 32 ++++++++++ ...rrowRateToMatchSkySavingsRate_20241022.sol | 35 +++++++++++ ...owRateToMatchSkySavingsRate_20241022.t.sol | 32 ++++++++++ .../FixUSDSBorrowRateToMatchSkySavingsRate.md | 32 ++++++++++ ...owRateToMatchSkySavingsRate_20241022.s.sol | 60 +++++++++++++++++++ .../config.ts | 32 ++++++++++ 7 files changed, 224 insertions(+) create mode 100644 .assets/237c21391c0b67eb1236e3875c882cd6115a3758.svg create mode 100644 diffs/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_before_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_after.md create mode 100644 src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol create mode 100644 src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol create mode 100644 src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md create mode 100644 src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate_20241022.s.sol create mode 100644 src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/config.ts diff --git a/.assets/237c21391c0b67eb1236e3875c882cd6115a3758.svg b/.assets/237c21391c0b67eb1236e3875c882cd6115a3758.svg new file mode 100644 index 000000000..f1b68a17b --- /dev/null +++ b/.assets/237c21391c0b67eb1236e3875c882cd6115a3758.svg @@ -0,0 +1 @@ + Borrow APR, variableBorrow APR, stable0%25%50%75%100%0%50%100%Optimal utilization 92%Optimal utilization 92% \ No newline at end of file diff --git a/diffs/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_before_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_after.md b/diffs/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_before_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_after.md new file mode 100644 index 000000000..4cab64c25 --- /dev/null +++ b/diffs/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_before_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_after.md @@ -0,0 +1,32 @@ +## Reserve changes + +### Reserves altered + +#### USDS ([0xdC035D45d973E3EC169d2276DDab16f1e407384F](https://etherscan.io/address/0xdC035D45d973E3EC169d2276DDab16f1e407384F)) + +| description | value before | value after | +| --- | --- | --- | +| maxVariableBorrowRate | 87.5 % | 82 % | +| variableRateSlope1 | 6.25 % | 0.75 % | +| interestRate | ![before](/.assets/59f72276d32ba1eeba45953b23c84ea37048e299.svg) | ![after](/.assets/237c21391c0b67eb1236e3875c882cd6115a3758.svg) | + +## Emodes changes + +## Raw diff + +```json +{ + "strategies": { + "0xdC035D45d973E3EC169d2276DDab16f1e407384F": { + "maxVariableBorrowRate": { + "from": "875000000000000000000000000", + "to": "820000000000000000000000000" + }, + "variableRateSlope1": { + "from": "62500000000000000000000000", + "to": "7500000000000000000000000" + } + } + } +} +``` \ No newline at end of file diff --git a/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol new file mode 100644 index 000000000..818a855c2 --- /dev/null +++ b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3PayloadEthereum} from 'aave-helpers/src/v3-config-engine/AaveV3PayloadEthereum.sol'; +import {EngineFlags} from 'aave-v3-origin/contracts/extensions/v3-config-engine/EngineFlags.sol'; +import {IAaveV3ConfigEngine} from 'aave-v3-origin/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol'; +/** + * @title Fix USDS Borrow Rate to Match Sky Savings Rate + * @author ACI + * - Snapshot: Direct-to-AIP + * - Discussion: https://governance.aave.com/t/arfc-increase-usds-borrow-rate-to-match-sky-savings-rate/19494/2 + */ +contract AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022 is AaveV3PayloadEthereum { + function rateStrategiesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.RateStrategyUpdate[] memory) + { + IAaveV3ConfigEngine.RateStrategyUpdate[] + memory rateStrategies = new IAaveV3ConfigEngine.RateStrategyUpdate[](1); + rateStrategies[0] = IAaveV3ConfigEngine.RateStrategyUpdate({ + asset: AaveV3EthereumAssets.USDS_UNDERLYING, + params: IAaveV3ConfigEngine.InterestRateInputData({ + optimalUsageRatio: EngineFlags.KEEP_CURRENT, + baseVariableBorrowRate: EngineFlags.KEEP_CURRENT, + variableRateSlope1: 75, + variableRateSlope2: EngineFlags.KEEP_CURRENT + }) + }); + + return rateStrategies; + } +} diff --git a/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol new file mode 100644 index 000000000..1fef147cf --- /dev/null +++ b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022} from './AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol'; + +/** + * @dev Test for AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol -vv + */ +contract AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022_Test is ProtocolV3TestBase { + AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21021725); + proposal = new AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022', + AaveV3Ethereum.POOL, + address(proposal) + ); + } +} diff --git a/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md new file mode 100644 index 000000000..5b6c45b81 --- /dev/null +++ b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md @@ -0,0 +1,32 @@ +--- +title: "Fix USDS Borrow Rate to Match Sky Savings Rate" +author: "ACI" +discussions: "https://governance.aave.com/t/arfc-increase-usds-borrow-rate-to-match-sky-savings-rate/19494/2" +snapshot: "Direct-to-AIP" +--- + +## Simple Summary + +Proposal to update the USDS Slope1 to .75% + +## Motivation + +While the latest AIP for USDS borrow rate has the intended effect of disincentivizing USDS borrows and looping on Main instance, the current slope1 is too aggressive compared to other stables. A new AIP to address the unnecessarily high borrow rate for USDS has been created with an updated slope1 at 0.75%. + +## Specification + +USDS Slope1 will be adjusted as follows + +New Slope1 .75% +All other parameters remain unchanged. + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol) +- [Snapshot](Direct-to-AIP) +- [Discussion](https://governance.aave.com/t/arfc-increase-usds-borrow-rate-to-match-sky-savings-rate/19494/2) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate_20241022.s.sol b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate_20241022.s.sol new file mode 100644 index 000000000..0343c4891 --- /dev/null +++ b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate_20241022.s.sol @@ -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 {AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022} from './AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate_20241022.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/FixUSDSBorrowRateToMatchSkySavingsRate_20241022.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022).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/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate_20241022.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(AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md' + ) + ); + } +} diff --git a/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/config.ts b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/config.ts new file mode 100644 index 000000000..732534c8a --- /dev/null +++ b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/config.ts @@ -0,0 +1,32 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3Ethereum'], + title: 'Fix USDS Borrow Rate to Match Sky Savings Rate', + shortName: 'FixUSDSBorrowRateToMatchSkySavingsRate', + date: '20241022', + author: 'ACI', + discussion: + 'https://governance.aave.com/t/arfc-increase-usds-borrow-rate-to-match-sky-savings-rate/19494', + snapshot: 'Direct-to-AIP', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Ethereum: { + configs: { + RATE_UPDATE_V3: [ + { + asset: 'USDS', + params: { + optimalUtilizationRate: '', + baseVariableBorrowRate: '', + variableRateSlope1: '0.75', + variableRateSlope2: '', + }, + }, + ], + }, + cache: {blockNumber: 21021725}, + }, + }, +}; From dffdd5b35936c8e86628eef412635a47cab0ba0f Mon Sep 17 00:00:00 2001 From: Cache bot Date: Wed, 23 Oct 2024 09:13:01 +0000 Subject: [PATCH 04/15] fix(cache): automated cache update [skip ci] --- .../FixUSDSBorrowRateToMatchSkySavingsRate.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md index 5b6c45b81..8bf4829f1 100644 --- a/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md +++ b/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/FixUSDSBorrowRateToMatchSkySavingsRate.md @@ -22,8 +22,8 @@ All other parameters remain unchanged. ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/e63fc7d6052ae90eac55d373b52c1cf343acb9e9/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/e63fc7d6052ae90eac55d373b52c1cf343acb9e9/src/20241022_AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate/AaveV3Ethereum_FixUSDSBorrowRateToMatchSkySavingsRate_20241022.t.sol) - [Snapshot](Direct-to-AIP) - [Discussion](https://governance.aave.com/t/arfc-increase-usds-borrow-rate-to-match-sky-savings-rate/19494/2) From e4ab6eb39d25f4e2ac3469e1a7cac0f4b294d0e1 Mon Sep 17 00:00:00 2001 From: Harsh Pandey Date: Sun, 27 Oct 2024 16:56:51 +0530 Subject: [PATCH 05/15] feat: bgd phase 4 (#503) * feat: bgd phase 4 * Added BGD Phase 4 description --------- Co-authored-by: eboado --- ...V3Ethereum_AaveBGDPhase4_20241025_after.md | 19 +++ .../AaveBGDPhase4.md | 46 ++++++++ .../AaveBGDPhase4_20241025.s.sol | 57 +++++++++ .../AaveV3Ethereum_AaveBGDPhase4_20241025.sol | 61 ++++++++++ ...aveV3Ethereum_AaveBGDPhase4_20241025.t.sol | 109 ++++++++++++++++++ .../config.ts | 15 +++ 6 files changed, 307 insertions(+) create mode 100644 diffs/AaveV3Ethereum_AaveBGDPhase4_20241025_before_AaveV3Ethereum_AaveBGDPhase4_20241025_after.md create mode 100644 src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md create mode 100644 src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4_20241025.s.sol create mode 100644 src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.sol create mode 100644 src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol create mode 100644 src/20241025_AaveV3Ethereum_AaveBGDPhase4/config.ts diff --git a/diffs/AaveV3Ethereum_AaveBGDPhase4_20241025_before_AaveV3Ethereum_AaveBGDPhase4_20241025_after.md b/diffs/AaveV3Ethereum_AaveBGDPhase4_20241025_before_AaveV3Ethereum_AaveBGDPhase4_20241025_after.md new file mode 100644 index 000000000..e03f15bf7 --- /dev/null +++ b/diffs/AaveV3Ethereum_AaveBGDPhase4_20241025_before_AaveV3Ethereum_AaveBGDPhase4_20241025_after.md @@ -0,0 +1,19 @@ +## Emodes changed + +### EMode: ETH correlated(id: 1) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label (unchanged) | ETH correlated | ETH correlated | +| eMode.ltv (unchanged) | 93 % | 93 % | +| eMode.liquidationThreshold (unchanged) | 95 % | 95 % | +| eMode.liquidationBonus (unchanged) | 1 % | 1 % | +| eMode.borrowableBitmap (unchanged) | WETH, wstETH, cbETH, rETH, weETH, osETH, ETHx | WETH, wstETH, cbETH, rETH, weETH, osETH, ETHx | +| eMode.collateralBitmap (unchanged) | WETH, wstETH, cbETH, rETH, weETH, osETH, ETHx | WETH, wstETH, cbETH, rETH, weETH, osETH, ETHx | + + +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md new file mode 100644 index 000000000..83c5f9210 --- /dev/null +++ b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md @@ -0,0 +1,46 @@ +--- +title: "Aave BGD Phase 4" +author: "BGD Labs (@bgdlabs)" +discussions: "https://governance.aave.com/t/aave-bored-ghosts-developing-phase-4/19484" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0x2dfb5a6e770bf7a34ddb5ab05560c24a169c63f84e9e8d373767a5b072f1f21d" +--- + +## Simple Summary + +Proposal for an engagement of development and security services between the Aave DAO and BGD Labs, from 1st October 2024 until 1st April 2025. + +## Motivation + +For the last two and a half years, BGD Labs has been a service provider of the Aave DAO, contributing with multiple developments, technical maintenance, and security coordination tasks. + +The motivation/goal of this new engagement is: + +- Keep the stability and trust in the Aave technology. +- Innovate responsibly on production systems. +- Support all other non-tech contributors to the DAO, accelerating progress. + +In summary, the same as before, but always striving for better. + +## Specification + +A full description of the scope can be found on the Aave Governance Forum [HERE](https://governance.aave.com/t/aave-bored-ghosts-developing-phase-4/19484#p-49320-aave-bgd-phase-iv-continuous-scope-4) or on the pre-approval on ARFC (Aave Request For Comments) [HERE](https://snapshot.box/#/s:aave.eth/proposal/0x2dfb5a6e770bf7a34ddb5ab05560c24a169c63f84e9e8d373767a5b072f1f21d). + +In terms of implementation, this proposal sets up the payment schedule defined: + +- From the Aave DAO's treasury (Collector smart contract), an upfront transfer for a value of 1'150'000 aUSDC (v3 Ethereum), 50% of the stablecoins budget. +- From the Aave DAO AAVE Ecosystem Reserve, an upfront transfer for a value of 2'500 AAVE tokens, 50% of the tokens budget. +- From the Aave DAO's treasury, the creation of a payment stream for a value of 1'150'000 aUSDC (v3 Ethereum), from the execution time of this proposal until 1st April 2025, completing the 6-month duration, for the other 50% of the stablecoins budget. +- From the Aave DAO AAVE Ecosystem Reserve, the creation of a payment stream for a value of 2'500 AAVE tokens, from the execution time of this proposal until 1st April 2025, completing the 6-month duration, for the other 50% of the tokens budget. + +**The approval of this proposal by Aave governance acts as a binding agreement between the Aave DAO and BGD Labs**. Detailed terms of service can be found [HERE](https://bgdlabs.com/aave-dao-tos). + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x2dfb5a6e770bf7a34ddb5ab05560c24a169c63f84e9e8d373767a5b072f1f21d) +- [Discussion](https://governance.aave.com/t/aave-bored-ghosts-developing-phase-4/19484) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4_20241025.s.sol b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4_20241025.s.sol new file mode 100644 index 000000000..7a2d19e28 --- /dev/null +++ b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4_20241025.s.sol @@ -0,0 +1,57 @@ +// 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 {AaveV3Ethereum_AaveBGDPhase4_20241025} from './AaveV3Ethereum_AaveBGDPhase4_20241025.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4_20241025.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/AaveBGDPhase4_20241025.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_AaveBGDPhase4_20241025).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/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4_20241025.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(AaveV3Ethereum_AaveBGDPhase4_20241025).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile(vm, 'src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md') + ); + } +} diff --git a/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.sol b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.sol new file mode 100644 index 000000000..45f15a494 --- /dev/null +++ b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {CollectorUtils} from 'aave-helpers/src/CollectorUtils.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; + +/** + * @title Aave BGD Phase 4 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x2dfb5a6e770bf7a34ddb5ab05560c24a169c63f84e9e8d373767a5b072f1f21d + * - Discussion: https://governance.aave.com/t/aave-bored-ghosts-developing-phase-4/19484 + */ +contract AaveV3Ethereum_AaveBGDPhase4_20241025 is IProposalGenericExecutor { + address public constant BGD_RECEIVER = 0xb812d0944f8F581DfAA3a93Dda0d22EcEf51A9CF; + uint256 public constant STOP_TIME = 1743485400; // ends on 1 april 2025 + + uint256 public constant UPFRONT_AUSDC_AMOUNT = 1_150_000e6; + uint256 public constant UPFRONT_AAVE_AMOUNT = 2_500e18; + + uint256 public constant STREAM_AUSDC_AMOUNT = 1_150_000e6; + uint256 public constant STREAM_AAVE_AMOUNT = 2_500e18; + + function execute() external { + uint256 DURATION = STOP_TIME - block.timestamp; + + // transfer half upfront + AaveV3Ethereum.COLLECTOR.transfer( + AaveV3EthereumAssets.USDC_A_TOKEN, + BGD_RECEIVER, + UPFRONT_AUSDC_AMOUNT + ); + MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.transfer( + MiscEthereum.ECOSYSTEM_RESERVE, + AaveV3EthereumAssets.AAVE_UNDERLYING, + BGD_RECEIVER, + UPFRONT_AAVE_AMOUNT + ); + + // create streams for the other half + CollectorUtils.stream( + AaveV3Ethereum.COLLECTOR, + CollectorUtils.CreateStreamInput({ + underlying: AaveV3EthereumAssets.USDC_A_TOKEN, + receiver: BGD_RECEIVER, + amount: STREAM_AUSDC_AMOUNT, + start: block.timestamp, + duration: DURATION + }) + ); + MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.createStream( + MiscEthereum.ECOSYSTEM_RESERVE, + BGD_RECEIVER, + (STREAM_AAVE_AMOUNT / DURATION) * DURATION, + AaveV3EthereumAssets.AAVE_UNDERLYING, + block.timestamp, + STOP_TIME + ); + } +} diff --git a/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol new file mode 100644 index 000000000..51cb87a32 --- /dev/null +++ b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {AaveV3Ethereum_AaveBGDPhase4_20241025} from './AaveV3Ethereum_AaveBGDPhase4_20241025.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {IStreamable} from 'aave-address-book/common/IStreamable.sol'; + +/** + * @dev Test for AaveV3Ethereum_AaveBGDPhase4_20241025 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol -vv + */ +contract AaveV3Ethereum_AaveBGDPhase4_20241025_Test is ProtocolV3TestBase { + AaveV3Ethereum_AaveBGDPhase4_20241025 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21043153); + proposal = new AaveV3Ethereum_AaveBGDPhase4_20241025(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest('AaveV3Ethereum_AaveBGDPhase4_20241025', AaveV3Ethereum.POOL, address(proposal)); + } + + function test_upfrontAmounts() public { + address receiverAddress = proposal.BGD_RECEIVER(); + uint256 aUsdcBalanceBefore = IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf( + receiverAddress + ); + uint256 aaveBalanceBefore = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).balanceOf( + receiverAddress + ); + + executePayload(vm, address(proposal)); + + uint256 aUsdcBalanceAfter = IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf( + receiverAddress + ); + uint256 aaveBalanceAfter = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).balanceOf( + receiverAddress + ); + + assertEq(aUsdcBalanceAfter - aUsdcBalanceBefore, 1_150_000e6); + assertEq(aaveBalanceAfter - aaveBalanceBefore, 2_500e18); + } + + function test_streamAmounts() public { + address receiverAddress = proposal.BGD_RECEIVER(); + IStreamable reserve = IStreamable(MiscEthereum.ECOSYSTEM_RESERVE); + + uint256 streamId = AaveV3Ethereum.COLLECTOR.getNextStreamId(); + uint256 streamId_reserve = reserve.getNextStreamId(); + + executePayload(vm, address(proposal)); + + vm.warp(block.timestamp + 7 days); + vm.startPrank(receiverAddress); + + uint256 aUsdcBalanceBefore = IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf( + receiverAddress + ); + uint256 aaveBalanceBefore = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).balanceOf( + receiverAddress + ); + + // withdraw 10 wei from stream + AaveV3Ethereum.COLLECTOR.withdrawFromStream(streamId, 10); + reserve.withdrawFromStream(streamId_reserve, 10); + + assertEq( + IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf(receiverAddress), + (aUsdcBalanceBefore + 10) + ); + assertEq( + IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).balanceOf(receiverAddress), + (aaveBalanceBefore + 10) + ); + + vm.warp(proposal.STOP_TIME()); + + aUsdcBalanceBefore = IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf(receiverAddress); + aaveBalanceBefore = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).balanceOf(receiverAddress); + + // withdraw all remaining amount from stream + AaveV3Ethereum.COLLECTOR.withdrawFromStream( + streamId, + AaveV3Ethereum.COLLECTOR.balanceOf(streamId, receiverAddress) + ); + reserve.withdrawFromStream( + streamId_reserve, + reserve.balanceOf(streamId_reserve, receiverAddress) + ); + + uint256 aUsdcBalanceAfter = IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf( + receiverAddress + ); + uint256 aaveBalanceAfter = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).balanceOf( + receiverAddress + ); + + assertApproxEqAbs(aUsdcBalanceAfter, aUsdcBalanceBefore + 1_150_000e6, 15e6); + assertApproxEqAbs(aaveBalanceAfter, aaveBalanceBefore + 2_500e18, 1e18); + } +} diff --git a/src/20241025_AaveV3Ethereum_AaveBGDPhase4/config.ts b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/config.ts new file mode 100644 index 000000000..1c4ac66ba --- /dev/null +++ b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/config.ts @@ -0,0 +1,15 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3Ethereum'], + title: 'Aave BGD Phase 4', + shortName: 'AaveBGDPhase4', + date: '20241025', + author: 'BGD Labs (@bgdlabs)', + discussion: 'https://governance.aave.com/t/aave-bored-ghosts-developing-phase-4/19484', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x2dfb5a6e770bf7a34ddb5ab05560c24a169c63f84e9e8d373767a5b072f1f21d', + votingNetwork: 'POLYGON', + }, + poolOptions: {AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 21043153}}}, +}; From 608b346993599c87e80e8e101012d4356a4a4011 Mon Sep 17 00:00:00 2001 From: Cache bot Date: Sun, 27 Oct 2024 11:27:23 +0000 Subject: [PATCH 06/15] fix(cache): automated cache update [skip ci] --- src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md index 83c5f9210..56c9a2d50 100644 --- a/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md +++ b/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveBGDPhase4.md @@ -36,8 +36,8 @@ In terms of implementation, this proposal sets up the payment schedule defined: ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/e4ab6eb39d25f4e2ac3469e1a7cac0f4b294d0e1/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/e4ab6eb39d25f4e2ac3469e1a7cac0f4b294d0e1/src/20241025_AaveV3Ethereum_AaveBGDPhase4/AaveV3Ethereum_AaveBGDPhase4_20241025.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x2dfb5a6e770bf7a34ddb5ab05560c24a169c63f84e9e8d373767a5b072f1f21d) - [Discussion](https://governance.aave.com/t/aave-bored-ghosts-developing-phase-4/19484) From 44d915792344590cdbdcc70ff0bc44c9191f2c97 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 28 Oct 2024 08:18:10 +0100 Subject: [PATCH 07/15] chore: update address book & align transformation (#504) --- generator/common.ts | 2 +- lib/aave-helpers | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/generator/common.ts b/generator/common.ts index ab9366420..d77f0bc6a 100644 --- a/generator/common.ts +++ b/generator/common.ts @@ -40,7 +40,7 @@ export function getAssets(pool: PoolIdentifier): string[] { export function getEModes(pool: PoolIdentifierV3): {value: string; id: number}[] { return Object.keys(addressBook[pool].E_MODES).map((key) => ({ // map the complex type to a string as used in the sol libs - value: addressBook[pool].E_MODES[key].label.toUpperCase().replace('-', '_').replace(' ', '_'), + value: addressBook[pool].E_MODES[key].label.toUpperCase().replace(/[^A-Z0-9]+/gi, '_'), id: key as unknown as number, })); } diff --git a/lib/aave-helpers b/lib/aave-helpers index 2a943191c..d378deda2 160000 --- a/lib/aave-helpers +++ b/lib/aave-helpers @@ -1 +1 @@ -Subproject commit 2a943191c1c4cadf88cb209c2ae622ce241ee912 +Subproject commit d378deda2e0477900a84bffb1c2f45d199dc3138 From c4837d92cdc0cd819db9b6c2e43e8cda3dcc96e0 Mon Sep 17 00:00:00 2001 From: Luigy-Lemon Date: Thu, 31 Oct 2024 11:16:54 +0000 Subject: [PATCH 08/15] feat: stkGHO Incentives Q4 (#506) * feat: stkGHO Incentives Q4 * Update AaveV3Ethereum_StkGHOIncentivesQ4_20241029.sol * Update StkGHOIncentivesQ4_20241029.md * fix: bugfix on order of operations * feat: allowance buffer as adjustment from previous update * Update StkGHOIncentivesQ4_20241029.md * Update src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md Co-authored-by: sendra * chore: lint fix --------- Co-authored-by: Fermin 'Piscu' Carranza Co-authored-by: sendra --- ...Ethereum_StkGHOIncentivesQ4_20241029.s.sol | 60 ++++++++++ ...V3Ethereum_StkGHOIncentivesQ4_20241029.sol | 59 +++++++++ ...Ethereum_StkGHOIncentivesQ4_20241029.t.sol | 113 ++++++++++++++++++ .../StkGHOIncentivesQ4_20241029.md | 35 ++++++ .../config.ts | 13 ++ 5 files changed, 280 insertions(+) create mode 100644 src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.s.sol create mode 100644 src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.sol create mode 100644 src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.t.sol create mode 100644 src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md create mode 100644 src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/config.ts diff --git a/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.s.sol b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.s.sol new file mode 100644 index 000000000..398fdbb3b --- /dev/null +++ b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.s.sol @@ -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 {AaveV3Ethereum_StkGHOIncentivesQ4_20241029} from './AaveV3Ethereum_StkGHOIncentivesQ4_20241029.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentives_20240424.s.sol:DeployEthereum chain=mainnet + * verify-command: npx catapulta-verify -b broadcast/StkGHOIncentives_20240424.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_StkGHOIncentivesQ4_20241029).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/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentives_20240424.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(AaveV3Ethereum_StkGHOIncentivesQ4_20241029).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md' + ) + ); + } +} diff --git a/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.sol b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.sol new file mode 100644 index 000000000..279cf8dd5 --- /dev/null +++ b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {AaveSafetyModule} from 'aave-address-book/AaveSafetyModule.sol'; +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; + +import {IStakeToken} from 'aave-address-book/common/IStakeToken.sol'; + +/** + * @title stkGHO Incentives + * @author karpatkey_TokenLogic_ACI + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x0f73500d0f65c72482d352080ea9aa0aa92264eb059b8f646cf6f0e86556bc3d + * - Discussion: https://governance.aave.com/t/arfc-safety-module-stkgho-re-enable-rewards/19626 + */ +contract AaveV3Ethereum_StkGHOIncentivesQ4_20241029 is IProposalGenericExecutor { + uint256 public constant DISTRIBUTION_DURATION = 180 days; + uint128 public constant AAVE_EMISSION_PER_SECOND_STK_GHO = uint128(100e18) / 1 days; + uint256 internal constant ALLOWANCE_BUFFER = 2752e18; // 2751836379823642455614 in block 19778176 + + function execute() external { + // configure with same settings to update internal state + IStakeToken.AssetConfigInput[] memory ghoConfigs = new IStakeToken.AssetConfigInput[](1); + ghoConfigs[0] = IStakeToken.AssetConfigInput({ + emissionPerSecond: AAVE_EMISSION_PER_SECOND_STK_GHO, + totalStaked: 0, // it's overwritten internally + underlyingAsset: AaveSafetyModule.STK_GHO + }); + IStakeToken(AaveSafetyModule.STK_GHO).configureAssets(ghoConfigs); + + IStakeToken(AaveSafetyModule.STK_GHO).setDistributionEnd( + block.timestamp + DISTRIBUTION_DURATION + ); + + uint256 remainingAllowance = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).allowance( + address(MiscEthereum.ECOSYSTEM_RESERVE), + AaveSafetyModule.STK_GHO + ); + + // Approval needs to be reset in order to increase it + MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.approve( + MiscEthereum.ECOSYSTEM_RESERVE, + AaveV3EthereumAssets.AAVE_UNDERLYING, + AaveSafetyModule.STK_GHO, + 0 + ); + + MiscEthereum.AAVE_ECOSYSTEM_RESERVE_CONTROLLER.approve( + MiscEthereum.ECOSYSTEM_RESERVE, + AaveV3EthereumAssets.AAVE_UNDERLYING, + AaveSafetyModule.STK_GHO, + remainingAllowance + + (AAVE_EMISSION_PER_SECOND_STK_GHO * DISTRIBUTION_DURATION) + + ALLOWANCE_BUFFER + ); + } +} diff --git a/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.t.sol b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.t.sol new file mode 100644 index 000000000..da9fcf782 --- /dev/null +++ b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.t.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; + +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {AaveSafetyModule} from 'aave-address-book/AaveSafetyModule.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; + +import {IStakeToken} from 'aave-address-book/common/IStakeToken.sol'; +import {AaveV3Ethereum_StkGHOIncentivesQ4_20241029} from './AaveV3Ethereum_StkGHOIncentivesQ4_20241029.sol'; + +/** + * @dev Test for AaveV3Ethereum_StkGHOIncentivesQ4_20241029 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentivesQ4_20241029.t.sol -vv + **/ +contract AaveV3Ethereum_StkGHOIncentivesQ4_20241029_Test is ProtocolV3TestBase { + AaveV3Ethereum_StkGHOIncentivesQ4_20241029 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21072841); + proposal = new AaveV3Ethereum_StkGHOIncentivesQ4_20241029(); + } + + function test_checkConfig() public { + (uint128 emissionPerSecondBeforeStkGHO, , ) = IStakeToken(AaveSafetyModule.STK_GHO).assets( + AaveSafetyModule.STK_GHO + ); + + assertEq( + emissionPerSecondBeforeStkGHO, + proposal.AAVE_EMISSION_PER_SECOND_STK_GHO(), + 'emissions before not equal stkGHO' + ); + + executePayload(vm, address(proposal)); + + (uint128 emissionPerSecondAfterStkGHO, , ) = IStakeToken(AaveSafetyModule.STK_GHO).assets( + AaveSafetyModule.STK_GHO + ); + + assertEq( + emissionPerSecondAfterStkGHO, + proposal.AAVE_EMISSION_PER_SECOND_STK_GHO(), + 'emissions after not equal stkGHO' + ); + assertApproxEqAbs( + emissionPerSecondAfterStkGHO, + emissionPerSecondBeforeStkGHO, + 1, + 'stkGHO emissions not same as previous' + ); + } + + function test_checkDistributionEnd() public { + uint256 endTimestampBefore = IStakeToken(AaveSafetyModule.STK_GHO).distributionEnd(); + + assertLt( + endTimestampBefore, + block.timestamp + proposal.DISTRIBUTION_DURATION(), + 'New distribution duration is lower than previously configured' + ); + + executePayload(vm, address(proposal)); + + uint256 endTimestampAfter = IStakeToken(AaveSafetyModule.STK_GHO).distributionEnd(); + + assertEq( + endTimestampAfter, + block.timestamp + proposal.DISTRIBUTION_DURATION(), + 'New distribution duration has not been set correctly' + ); + } + + function test_checkAllowance() public { + uint256 allowanceBefore = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).allowance( + MiscEthereum.ECOSYSTEM_RESERVE, + AaveSafetyModule.STK_GHO + ); + + executePayload(vm, address(proposal)); + + uint256 allowanceAfter = IERC20(AaveV3EthereumAssets.AAVE_UNDERLYING).allowance( + MiscEthereum.ECOSYSTEM_RESERVE, + AaveSafetyModule.STK_GHO + ); + + assertGt(allowanceAfter, allowanceBefore); + } + + function test_checkRewards_stkGHO() public { + address staker = 0x00907f9921424583e7ffBfEdf84F92B7B2Be4977; + + uint256 rewardsPerDay = 100e18; + + executePayload(vm, address(proposal)); + + vm.startPrank(staker); + IERC20(AaveV3EthereumAssets.GHO_UNDERLYING).approve(AaveSafetyModule.STK_GHO, 1e18); + + IStakeToken(AaveSafetyModule.STK_GHO).stake(staker, 1e18); + + vm.warp(block.timestamp + 1 days); + + uint256 rewardsBalance = IStakeToken(AaveSafetyModule.STK_GHO).getTotalRewardsBalance(staker); + + assertTrue(rewardsBalance > 0 && rewardsBalance <= rewardsPerDay); + + vm.stopPrank(); + } +} diff --git a/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md new file mode 100644 index 000000000..c5a91abf5 --- /dev/null +++ b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md @@ -0,0 +1,35 @@ +--- +title: "stkGHO Incentives" +author: "karpatkey_TokenLogic_ACI" +discussions: "https://governance.aave.com/t/arfc-safety-module-stkgho-re-enable-rewards/19626" +snapshot: "https://snapshot.org/#/aave.eth/proposal/0x0f73500d0f65c72482d352080ea9aa0aa92264eb059b8f646cf6f0e86556bc3d" +--- + +## Simple Summary + +Re-enable stkGHO incentives emissions for 180 days after previous period ended. +On the previous proposal to enable stkGHO emissions (Proposal 91 on the forum), an error in accounting made it so ~2,750 in emissions were not claimable during the last period. This current proposal includes that approval so that those emissions can be claimed, in addition to the 100 per day. +The previous proposal can be found [here](https://vote.onaave.com/proposal/?proposalId=91&ipfsHash=0x633733ef9b80afd497fd1a25d848fbe91ef694fab798dbbc27617ca07407454c). + +## Motivation + +To support the GHO peg and to pave the way for future GHO supply expansion, this publication proposes maintaining the daily AAVE emission going to stkGHO holders. + +## Specification + +For stkGHO: + +- Set new allowance to 100 AAVE per day for 180 days +- Include prior ~2,750 AAVE in allowance +- Set distribution end for 180 days from Execution date + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentives_20240424.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentives_20240424.t.sol) +- [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x0f73500d0f65c72482d352080ea9aa0aa92264eb059b8f646cf6f0e86556bc3d) +- [Discussion](https://governance.aave.com/t/arfc-safety-module-stkgho-re-enable-rewards/19626) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/config.ts b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/config.ts new file mode 100644 index 000000000..fe0c7cb7c --- /dev/null +++ b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/config.ts @@ -0,0 +1,13 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3Ethereum'], + title: 'stkGHO Incentives Q4', + shortName: 'StkGHOIncentivesQ4', + date: '20240424', + author: 'karpatkey_TokenLogic_ACI', + discussion: 'https://governance.aave.com/t/arfc-amend-safety-module-emissions/16640', + snapshot: 'Direct-to-AIP', + }, + poolOptions: {AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 19727543}}}, +}; From 4fa196230741b12356e9533efce85f47462c7b4c Mon Sep 17 00:00:00 2001 From: Cache bot Date: Thu, 31 Oct 2024 11:17:22 +0000 Subject: [PATCH 09/15] fix(cache): automated cache update [skip ci] --- .../StkGHOIncentivesQ4_20241029.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md index c5a91abf5..1dbdd5a63 100644 --- a/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md +++ b/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/StkGHOIncentivesQ4_20241029.md @@ -25,8 +25,8 @@ For stkGHO: ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentives_20240424.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentives_20240424.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/c4837d92cdc0cd819db9b6c2e43e8cda3dcc96e0/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentives_20240424.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/c4837d92cdc0cd819db9b6c2e43e8cda3dcc96e0/src/20241029_AaveV3Ethereum_StkGHOIncentivesQ4/AaveV3Ethereum_StkGHOIncentives_20240424.t.sol) - [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x0f73500d0f65c72482d352080ea9aa0aa92264eb059b8f646cf6f0e86556bc3d) - [Discussion](https://governance.aave.com/t/arfc-safety-module-stkgho-re-enable-rewards/19626) From 4d2ca786411ef6f1207f81498ee298142721ddfd Mon Sep 17 00:00:00 2001 From: MartinGbz Date: Thu, 31 Oct 2024 13:12:19 +0000 Subject: [PATCH 10/15] =?UTF-8?q?=E2=9C=A8=20Onboard=20wstETH=20on=20BNB?= =?UTF-8?q?=20(#507)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update aave-address-book lib * ✨ add new listing + emode * ✨ add wstETH to "ETH-Correlated" emode * Revert "update aave-address-book lib" This reverts commit 0a1d86d153503c4b838d83596965b1dcbd549775. * 🐛 remove weird import * 🔧 update seed amount * ♻️ fix pavel comment * 🐛 fix AssetEModeUpdate array length * 🐛 update the proposal with the right wstETH token * fix: emode category * Revert "🐛 update the proposal with the right wstETH token" This reverts commit adeaaf8fd16dc3aac3704187cd35731748635798. * 🐛 switch asset address for the right one (again) * ✨ add asset file * ✨ add missing md texts * ✨ add e-mode table * ✨ add emission admin back --------- Co-authored-by: Nandy Bâ Co-authored-by: Rozengarden --- ...WstETHToAaveV3OnBNBChain_20241030_after.md | 140 ++++++++++++++++++ ...boardWstETHToAaveV3OnBNBChain_20241030.sol | 106 +++++++++++++ ...ardWstETHToAaveV3OnBNBChain_20241030.t.sol | 57 +++++++ .../OnboardWstETHToAaveV3OnBNBChain.md | 63 ++++++++ ...ardWstETHToAaveV3OnBNBChain_20241030.s.sol | 60 ++++++++ .../config.ts | 54 +++++++ 6 files changed, 480 insertions(+) create mode 100644 diffs/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_before_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_after.md create mode 100644 src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol create mode 100644 src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol create mode 100644 src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md create mode 100644 src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain_20241030.s.sol create mode 100644 src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/config.ts diff --git a/diffs/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_before_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_after.md b/diffs/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_before_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_after.md new file mode 100644 index 000000000..0e55ef15c --- /dev/null +++ b/diffs/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_before_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_after.md @@ -0,0 +1,140 @@ +## Reserve changes + +### Reserves added + +#### wstETH ([0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C](https://bscscan.com/address/0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C)) + +| description | value | +| --- | --- | +| decimals | 18 | +| isActive | true | +| isFrozen | false | +| supplyCap | 1,900 wstETH | +| borrowCap | 190 wstETH | +| debtCeiling | 0 $ [0] | +| isSiloed | false | +| isFlashloanable | true | +| oracle | [0xc1377B4cdF9116bf7b3d7F72A4f8A7Be8506cE80](https://bscscan.com/address/0xc1377B4cdF9116bf7b3d7F72A4f8A7Be8506cE80) | +| oracleDecimals | 8 | +| oracleDescription | Capped wstETH / stETH(ETH) / USD | +| oracleLatestAnswer | 3179.8496515 | +| usageAsCollateralEnabled | true | +| ltv | 72 % [7200] | +| liquidationThreshold | 75 % [7500] | +| liquidationBonus | 7.5 % | +| liquidationProtocolFee | 10 % [1000] | +| reserveFactor | 15 % [1500] | +| aToken | [0xBDFd4E51D3c14a232135f04988a42576eFb31519](https://bscscan.com/address/0xBDFd4E51D3c14a232135f04988a42576eFb31519) | +| aTokenImpl | [0x6c23bAF050ec192afc0B967a93b83e6c5405df43](https://bscscan.com/address/0x6c23bAF050ec192afc0B967a93b83e6c5405df43) | +| variableDebtToken | [0x2c391998308c56D7572A8F501D58CB56fB9Fe1C5](https://bscscan.com/address/0x2c391998308c56D7572A8F501D58CB56fB9Fe1C5) | +| variableDebtTokenImpl | [0x777fBA024bA1228fDa76149A4ff8B23475ed057D](https://bscscan.com/address/0x777fBA024bA1228fDa76149A4ff8B23475ed057D) | +| borrowingEnabled | true | +| isBorrowableInIsolation | false | +| interestRateStrategy | [0x86AB1C62A8bf868E1b3E1ab87d587Aba6fbCbDC5](https://bscscan.com/address/0x86AB1C62A8bf868E1b3E1ab87d587Aba6fbCbDC5) | +| aTokenName | Aave BNB Smart Chain wstETH | +| aTokenSymbol | aBnbwstETH | +| aTokenUnderlyingBalance | 0.04 wstETH [40000000000000000] | +| id | 7 | +| isPaused | false | +| variableDebtTokenName | Aave BNB Smart Chain Variable Debt wstETH | +| variableDebtTokenSymbol | variableDebtBnbwstETH | +| virtualAccountingActive | true | +| virtualBalance | 0.04 wstETH [40000000000000000] | +| optimalUsageRatio | 45 % | +| maxVariableBorrowRate | 307 % | +| baseVariableBorrowRate | 0 % | +| variableRateSlope1 | 7 % | +| variableRateSlope2 | 300 % | +| interestRate | ![ir](https://dash.onaave.com/api/static?variableRateSlope1=70000000000000000000000000&variableRateSlope2=3000000000000000000000000000&optimalUsageRatio=450000000000000000000000000&baseVariableBorrowRate=0&maxVariableBorrowRate=3070000000000000000000000000) | + + +## Emodes changed + +### EMode: ETH-Correlated(id: 1) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | ETH-Correlated | +| eMode.ltv | - | 93 % | +| eMode.liquidationThreshold | - | 95 % | +| eMode.liquidationBonus | - | 1 % | +| eMode.borrowableBitmap | - | ETH | +| eMode.collateralBitmap | - | wstETH | + + +## Raw diff + +```json +{ + "eModes": { + "1": { + "from": null, + "to": { + "borrowableBitmap": 8, + "collateralBitmap": 128, + "eModeCategory": 1, + "label": "ETH-Correlated", + "liquidationBonus": 10100, + "liquidationThreshold": 9500, + "ltv": 9300 + } + } + }, + "reserves": { + "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C": { + "from": null, + "to": { + "aToken": "0xBDFd4E51D3c14a232135f04988a42576eFb31519", + "aTokenImpl": "0x6c23bAF050ec192afc0B967a93b83e6c5405df43", + "aTokenName": "Aave BNB Smart Chain wstETH", + "aTokenSymbol": "aBnbwstETH", + "aTokenUnderlyingBalance": "40000000000000000", + "borrowCap": 190, + "borrowingEnabled": true, + "debtCeiling": 0, + "decimals": 18, + "id": 7, + "interestRateStrategy": "0x86AB1C62A8bf868E1b3E1ab87d587Aba6fbCbDC5", + "isActive": true, + "isBorrowableInIsolation": false, + "isFlashloanable": true, + "isFrozen": false, + "isPaused": false, + "isSiloed": false, + "liquidationBonus": 10750, + "liquidationProtocolFee": 1000, + "liquidationThreshold": 7500, + "ltv": 7200, + "oracle": "0xc1377B4cdF9116bf7b3d7F72A4f8A7Be8506cE80", + "oracleDecimals": 8, + "oracleDescription": "Capped wstETH / stETH(ETH) / USD", + "oracleLatestAnswer": 317984965150, + "reserveFactor": 1500, + "supplyCap": 1900, + "symbol": "wstETH", + "underlying": "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C", + "usageAsCollateralEnabled": true, + "variableDebtToken": "0x2c391998308c56D7572A8F501D58CB56fB9Fe1C5", + "variableDebtTokenImpl": "0x777fBA024bA1228fDa76149A4ff8B23475ed057D", + "variableDebtTokenName": "Aave BNB Smart Chain Variable Debt wstETH", + "variableDebtTokenSymbol": "variableDebtBnbwstETH", + "virtualAccountingActive": true, + "virtualBalance": "40000000000000000" + } + } + }, + "strategies": { + "0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C": { + "from": null, + "to": { + "address": "0x86AB1C62A8bf868E1b3E1ab87d587Aba6fbCbDC5", + "baseVariableBorrowRate": 0, + "maxVariableBorrowRate": "3070000000000000000000000000", + "optimalUsageRatio": "450000000000000000000000000", + "variableRateSlope1": "70000000000000000000000000", + "variableRateSlope2": "3000000000000000000000000000" + } + } + } +} +``` \ No newline at end of file diff --git a/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol new file mode 100644 index 000000000..07df12008 --- /dev/null +++ b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3BNB, AaveV3BNBAssets} from 'aave-address-book/AaveV3BNB.sol'; +import {AaveV3PayloadBNB} from 'aave-helpers/src/v3-config-engine/AaveV3PayloadBNB.sol'; +import {EngineFlags} from 'aave-v3-origin/contracts/extensions/v3-config-engine/EngineFlags.sol'; +import {IAaveV3ConfigEngine} from 'aave-v3-origin/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {SafeERC20} from 'solidity-utils/contracts/oz-common/SafeERC20.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; +/** + * @title Onboard wstETH to Aave V3 on BNB Chain + * @author ACI + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x752c396a86f1f9b60d3e43b7ed223d9d2f66014e03b6b3eb7ac59e8a169d1fe5 + * - Discussion: https://governance.aave.com/t/arfc-onboard-wsteth-to-aave-v3-on-bnb-chain/18501 + */ +contract AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030 is AaveV3PayloadBNB { + using SafeERC20 for IERC20; + + address public constant wstETH = 0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C; + uint256 public constant wstETH_SEED_AMOUNT = 4e16; + address public constant wstETH_LM_ADMIN = 0xac140648435d03f784879cd789130F22Ef588Fcd; + + function _postExecute() internal override { + IERC20(wstETH).forceApprove(address(AaveV3BNB.POOL), wstETH_SEED_AMOUNT); + AaveV3BNB.POOL.supply(wstETH, wstETH_SEED_AMOUNT, address(AaveV3BNB.COLLECTOR), 0); + + (address awstETH, , ) = AaveV3BNB.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(wstETH); + IEmissionManager(AaveV3BNB.EMISSION_MANAGER).setEmissionAdmin(wstETH, wstETH_LM_ADMIN); + IEmissionManager(AaveV3BNB.EMISSION_MANAGER).setEmissionAdmin(awstETH, wstETH_LM_ADMIN); + } + + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](1); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 1, + ltv: 93_00, + liqThreshold: 95_00, + liqBonus: 1_00, + label: 'ETH-Correlated' + }); + + return eModeUpdates; + } + function assetsEModeUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.AssetEModeUpdate[] memory) + { + IAaveV3ConfigEngine.AssetEModeUpdate[] + memory assetEModeUpdates = new IAaveV3ConfigEngine.AssetEModeUpdate[](2); + + assetEModeUpdates[0] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3BNBAssets.ETH_UNDERLYING, + eModeCategory: 1, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + + assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: wstETH, + eModeCategory: 1, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + + return assetEModeUpdates; + } + function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) { + IAaveV3ConfigEngine.Listing[] memory listings = new IAaveV3ConfigEngine.Listing[](1); + + listings[0] = IAaveV3ConfigEngine.Listing({ + asset: wstETH, + assetSymbol: 'wstETH', + priceFeed: 0xc1377B4cdF9116bf7b3d7F72A4f8A7Be8506cE80, + enabledToBorrow: EngineFlags.ENABLED, + borrowableInIsolation: EngineFlags.DISABLED, + withSiloedBorrowing: EngineFlags.DISABLED, + flashloanable: EngineFlags.ENABLED, + ltv: 72_00, + liqThreshold: 75_00, + liqBonus: 7_50, + reserveFactor: 15_00, + supplyCap: 1_900, + borrowCap: 190, + debtCeiling: 0, + liqProtocolFee: 10_00, + rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({ + optimalUsageRatio: 45_00, + baseVariableBorrowRate: 0, + variableRateSlope1: 7_00, + variableRateSlope2: 300_00 + }) + }); + + return listings; + } +} diff --git a/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol new file mode 100644 index 000000000..348c4f7f3 --- /dev/null +++ b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3BNB} from 'aave-address-book/AaveV3BNB.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; + +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030} from './AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; + +/** + * @dev Test for AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030 + * command: FOUNDRY_PROFILE=bnb forge test --match-path=src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol -vv + */ +contract AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030_Test is ProtocolV3TestBase { + AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('bnb'), 43567833); + proposal = new AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030', + AaveV3BNB.POOL, + address(proposal) + ); + } + + function test_collectorHaswstETHFunds() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aTokenAddress, , ) = AaveV3BNB.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.wstETH() + ); + assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3BNB.COLLECTOR)), 4 * 10 ** 16); + } + + function test_wstETHAdmin() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address awstETH, , ) = AaveV3BNB.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.wstETH() + ); + assertEq( + IEmissionManager(AaveV3BNB.EMISSION_MANAGER).getEmissionAdmin(proposal.wstETH()), + proposal.wstETH_LM_ADMIN() + ); + assertEq( + IEmissionManager(AaveV3BNB.EMISSION_MANAGER).getEmissionAdmin(awstETH), + proposal.wstETH_LM_ADMIN() + ); + } +} diff --git a/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md new file mode 100644 index 000000000..ff023816e --- /dev/null +++ b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md @@ -0,0 +1,63 @@ +--- +title: "Onboard wstETH to Aave V3 on BNB Chain" +author: "ACI" +discussions: "https://governance.aave.com/t/arfc-onboard-wsteth-to-aave-v3-on-bnb-chain/18501" +snapshot: "https://snapshot.org/#/aave.eth/proposal/0x752c396a86f1f9b60d3e43b7ed223d9d2f66014e03b6b3eb7ac59e8a169d1fe5" +--- + +## Simple Summary + +This AIP proposes the onboard of wstETH (wrapped staked ETH) to Aave V3 on BNB Chain to improve Aave’s reach across alternative chains and provide increased choice for Aave users on BNB Chain. + +## Motivation + +wstETH is one of the most popular collateral and borrow tokens on Aave, with over $4 billion in deposits and $100m in borrows across Aave deployments. The onboarding of wstETH to the Aave V3 on BNB Chain will give Aave users more options for generating staking yield on their collateral and increase dominance of Aave on BNB Chain. + +## Specification + +The table below illustrates the configured risk parameters for **wstETH** + +| Parameter | Value | +| ------------------------- | -----------------------------------------: | +| Isolation Mode | false | +| Borrowable | ENABLED | +| Collateral Enabled | true | +| Supply Cap (wstETH) | 1,900 | +| Borrow Cap (wstETH) | 190 | +| Debt Ceiling | USD 0 | +| LTV | 72 % | +| LT | 75 % | +| Liquidation Bonus | 7.5 % | +| Liquidation Protocol Fee | 10 % | +| Reserve Factor | 15 % | +| Base Variable Borrow Rate | 0 % | +| Variable Slope 1 | 7 % | +| Variable Slope 2 | 300 % | +| Uoptimal | 45 % | +| Flashloanable | ENABLED | +| Siloed Borrowing | DISABLED | +| Borrowable in Isolation | DISABLED | +| Oracle | 0xc1377B4cdF9116bf7b3d7F72A4f8A7Be8506cE80 | + +The table below illustrate the configured "ETH-Correlated" E-mode + +| Parameter | wstETH | WETH | +| --------------------- | ------ | ----- | +| Collateral | Yes | No | +| Borrowable | No | Yes | +| Max LTV | 93% | 93% | +| Liquidation Threshold | 95% | 95% | +| Liquidation Penalty | 1.00% | 1.00% | + +Additionaly [0xac140648435d03f784879cd789130F22Ef588Fcd](https://bscscan.com/address/0xac140648435d03f784879cd789130F22Ef588Fcd) has been set as the emission admin for wstETH and the corresponding aToken. + +## References + +- Implementation: [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol) +- Tests: [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol) +- [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x752c396a86f1f9b60d3e43b7ed223d9d2f66014e03b6b3eb7ac59e8a169d1fe5) +- [Discussion](https://governance.aave.com/t/arfc-onboard-wsteth-to-aave-v3-on-bnb-chain/18501) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain_20241030.s.sol b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain_20241030.s.sol new file mode 100644 index 000000000..5d1dad8f7 --- /dev/null +++ b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain_20241030.s.sol @@ -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, BNBScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; +import {AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030} from './AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol'; + +/** + * @dev Deploy BNB + * deploy-command: make deploy-ledger contract=src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain_20241030.s.sol:DeployBNB chain=bnb + * verify-command: FOUNDRY_PROFILE=bnb npx catapulta-verify -b broadcast/OnboardWstETHToAaveV3OnBNBChain_20241030.s.sol/56/run-latest.json + */ +contract DeployBNB is BNBScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030).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/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain_20241030.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 actionsBNB = new IPayloadsControllerCore.ExecutionAction[](1); + actionsBNB[0] = GovV3Helpers.buildAction( + type(AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030).creationCode + ); + payloads[0] = GovV3Helpers.buildBNBPayload(vm, actionsBNB); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md' + ) + ); + } +} diff --git a/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/config.ts b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/config.ts new file mode 100644 index 000000000..1446255c9 --- /dev/null +++ b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/config.ts @@ -0,0 +1,54 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3BNB'], + title: 'Onboard wstETH to Aave V3 on BNB Chain', + shortName: 'OnboardWstETHToAaveV3OnBNBChain', + date: '20241030', + author: 'ACI', + discussion: 'https://governance.aave.com/t/arfc-onboard-wsteth-to-aave-v3-on-bnb-chain/18501', + snapshot: + 'https://snapshot.org/#/aave.eth/proposal/0x752c396a86f1f9b60d3e43b7ed223d9d2f66014e03b6b3eb7ac59e8a169d1fe5', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3BNB: { + configs: { + EMODES_UPDATES: [ + {eModeCategory: 1, ltv: '93', liqThreshold: '95', liqBonus: '1', label: 'ETH-Correlated'}, + ], + EMODES_ASSETS: [ + {asset: 'ETH', eModeCategory: '0', collateral: 'DISABLED', borrowable: 'ENABLED'}, + ], + ASSET_LISTING: [ + { + assetSymbol: 'wstETH', + decimals: 18, + priceFeed: '0xc1377B4cdF9116bf7b3d7F72A4f8A7Be8506cE80', + ltv: '72', + liqThreshold: '75', + liqBonus: '7.5', + debtCeiling: '0', + liqProtocolFee: '10', + enabledToBorrow: 'ENABLED', + flashloanable: 'ENABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '15', + supplyCap: '1900', + borrowCap: '190', + rateStrategyParams: { + optimalUtilizationRate: '45', + baseVariableBorrowRate: '0', + variableRateSlope1: '7', + variableRateSlope2: '300', + }, + asset: '0x26c5e01524d2E6280A48F2c50fF6De7e52E9611C', + admin: '0xac140648435d03f784879cd789130F22Ef588Fcd', + }, + ], + }, + cache: {blockNumber: 43567833}, + }, + }, +}; From 59b5a9491ef8a09891783c4bcc8b031556a9e833 Mon Sep 17 00:00:00 2001 From: Cache bot Date: Thu, 31 Oct 2024 13:12:51 +0000 Subject: [PATCH 11/15] fix(cache): automated cache update [skip ci] --- .../OnboardWstETHToAaveV3OnBNBChain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md index ff023816e..e8eb14d5c 100644 --- a/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md +++ b/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/OnboardWstETHToAaveV3OnBNBChain.md @@ -53,8 +53,8 @@ Additionaly [0xac140648435d03f784879cd789130F22Ef588Fcd](https://bscscan.com/add ## References -- Implementation: [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol) -- Tests: [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol) +- Implementation: [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/4d2ca786411ef6f1207f81498ee298142721ddfd/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.sol) +- Tests: [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/4d2ca786411ef6f1207f81498ee298142721ddfd/src/20241030_AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain/AaveV3BNB_OnboardWstETHToAaveV3OnBNBChain_20241030.t.sol) - [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x752c396a86f1f9b60d3e43b7ed223d9d2f66014e03b6b3eb7ac59e8a169d1fe5) - [Discussion](https://governance.aave.com/t/arfc-onboard-wsteth-to-aave-v3-on-bnb-chain/18501) From 603758edd1814e074b1c7a08c7da140df4338351 Mon Sep 17 00:00:00 2001 From: Fermin 'Piscu' Carranza Date: Thu, 31 Oct 2024 09:15:03 -0400 Subject: [PATCH 12/15] Update Gho Stewards (#502) * chore: initial commit * feat: gho stewards upgrade * feat: add all arbitrum contracts and facilitators * chore: update tests * chore: upgrade 3.2 * feat: update version * feat: update ccip arb * feat: add more tests * chore: wrap up tests * chore: update errors * chore: add more tests * chore: update tests * chore: add references to md * feat: revoke old roles * chore: pr updates * chore: nitpick --- ...3Arbitrum_GHOStewardV2Upgrade_20241007.sol | 63 ++ ...rbitrum_GHOStewardV2Upgrade_20241007.t.sol | 416 ++++++++++++ ...3Ethereum_GHOStewardV2Upgrade_20241007.sol | 92 +++ ...thereum_GHOStewardV2Upgrade_20241007.t.sol | 454 +++++++++++++ .../Ccip.sol | 220 +++++++ .../GHOStewardV2Upgrade.md | 62 ++ .../GHOStewardV2Upgrade_20241007.s.sol | 87 +++ .../ReserveConfiguration.sol | 611 ++++++++++++++++++ .../config.ts | 18 + src/interfaces/IGhoAaveSteward.sol | 127 ++++ src/interfaces/IGhoBucketSteward.sol | 66 ++ src/interfaces/IGhoCcipSteward.sol | 73 +++ src/interfaces/IGhoToken.sol | 34 + src/interfaces/IGsm.sol | 51 ++ src/interfaces/IGsmFeeStrategy.sol | 40 ++ src/interfaces/IGsmSteward.sol | 67 ++ .../ccip/IUpgradeableLockReleaseTokenPool.sol | 31 +- 17 files changed, 2506 insertions(+), 6 deletions(-) create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/Ccip.sol create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade_20241007.s.sol create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/ReserveConfiguration.sol create mode 100644 src/20241007_Multi_GHOStewardV2Upgrade/config.ts create mode 100644 src/interfaces/IGhoAaveSteward.sol create mode 100644 src/interfaces/IGhoBucketSteward.sol create mode 100644 src/interfaces/IGhoCcipSteward.sol create mode 100644 src/interfaces/IGsm.sol create mode 100644 src/interfaces/IGsmFeeStrategy.sol create mode 100644 src/interfaces/IGsmSteward.sol diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol new file mode 100644 index 000000000..f9bd1e496 --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Arbitrum, AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {IACLManager} from 'aave-address-book/AaveV3.sol'; +import {MiscArbitrum} from 'aave-address-book/MiscArbitrum.sol'; +import {ProxyAdmin} from 'solidity-utils/contracts/transparent-proxy/ProxyAdmin.sol'; +import {TransparentUpgradeableProxy} from 'solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol'; +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol'; + +import {IGhoBucketSteward} from 'src/interfaces/IGhoBucketSteward.sol'; +import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; +import {IUpgradeableLockReleaseTokenPool} from 'src/interfaces/ccip/IUpgradeableLockReleaseTokenPool.sol'; + +/** + * @title GHO Steward v2 Upgrade + * @author @karpatkey_TokenLogic + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0xc5e7df1536ef9fc71a7d2e2f6fee6e4e20e37a50b4e0f1646616d066b8697da5 + * - Discussion: https://governance.aave.com/t/arfc-gho-steward-v2-upgrade/19116 + */ +contract AaveV3Arbitrum_GHOStewardV2Upgrade_20241007 is IProposalGenericExecutor { + // https://arbiscan.io/address/0xb78eda33eb5493d56f14a81023ce69438a562a2c + address public constant NEW_CCIP_POOL_IMPL = 0xb78eDA33EB5493d56f14a81023CE69438a562A2c; + + // https://arbiscan.io/address/0xa9afaE6A53E90f9E4CE0717162DF5Bc3d9aBe7B2 + address public constant GHO_BUCKET_STEWARD = 0xa9afaE6A53E90f9E4CE0717162DF5Bc3d9aBe7B2; + + // https://arbiscan.io/address/0xcd04d93bea13921dad05240d577090b5ac36dfca + address public constant GHO_AAVE_STEWARD = 0xCd04D93bEA13921DaD05240D577090b5AC36DfCA; + + // https://arbiscan.io/address/0xb329CEFF2c362F315900d245eC88afd24C4949D5 + address public constant GHO_CCIP_STEWARD = 0xb329CEFF2c362F315900d245eC88afd24C4949D5; + + function execute() external { + // New CCIP Token Pool + ProxyAdmin(MiscArbitrum.PROXY_ADMIN).upgrade( + TransparentUpgradeableProxy(payable(MiscArbitrum.GHO_CCIP_TOKEN_POOL)), + NEW_CCIP_POOL_IMPL + ); + + // Gho Bucket Steward + IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING).grantRole( + IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + GHO_BUCKET_STEWARD + ); + + address[] memory controlledFacilitators = new address[](1); + controlledFacilitators[0] = MiscArbitrum.GHO_CCIP_TOKEN_POOL; + IGhoBucketSteward(GHO_BUCKET_STEWARD).setControlledFacilitator(controlledFacilitators, true); + + // Gho Aave Steward + IAccessControl(address(AaveV3Arbitrum.ACL_MANAGER)).grantRole( + IACLManager(address(AaveV3Arbitrum.ACL_MANAGER)).RISK_ADMIN_ROLE(), + GHO_AAVE_STEWARD + ); + + // Gho CCIP Steward + IUpgradeableLockReleaseTokenPool(MiscArbitrum.GHO_CCIP_TOKEN_POOL).setRateLimitAdmin( + GHO_CCIP_STEWARD + ); + } +} diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol new file mode 100644 index 000000000..a323ba406 --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Arbitrum, AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {MiscArbitrum} from 'aave-address-book/MiscArbitrum.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; +import {IACLManager, IDefaultInterestRateStrategyV2} from 'aave-address-book/AaveV3.sol'; +import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol'; +import {TransparentUpgradeableProxy} from 'solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; + +import {AaveV3Arbitrum_GHOStewardV2Upgrade_20241007} from './AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol'; +import {IGhoAaveSteward} from 'src/interfaces/IGhoAaveSteward.sol'; +import {IGhoBucketSteward} from 'src/interfaces/IGhoBucketSteward.sol'; +import {IGhoCcipSteward} from 'src/interfaces/IGhoCcipSteward.sol'; +import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; +import {IGsm} from 'src/interfaces/IGsm.sol'; +import {RateLimiter, IUpgradeableLockReleaseTokenPool} from './Ccip.sol'; + +/** + * @dev Test for AaveV3Arbitrum_GHOStewardV2Upgrade_20241007 + * command: FOUNDRY_PROFILE=arbitrum forge test --match-path=src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol -vv + */ +contract AaveV3Arbitrum_GHOStewardV2Upgrade_20241007_Test is ProtocolV3TestBase { + AaveV3Arbitrum_GHOStewardV2Upgrade_20241007 internal proposal; + address public RISK_COUNCIL = 0x8513e6F37dBc52De87b166980Fa3F50639694B60; + uint64 public remoteChainSelector = 5009297550715157269; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('arbitrum'), 264094912); + proposal = new AaveV3Arbitrum_GHOStewardV2Upgrade_20241007(); + } + + function test_rolesAreSet_cannotReinitializePoolToken() public { + // Gho Bucket Steward + assertEq( + IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING).hasRole( + IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + proposal.GHO_BUCKET_STEWARD() + ), + false + ); + + // Gho Aave Steward + assertEq( + IAccessControl(address(AaveV3Arbitrum.ACL_MANAGER)).hasRole( + IACLManager(address(AaveV3Arbitrum.ACL_MANAGER)).RISK_ADMIN_ROLE(), + proposal.GHO_AAVE_STEWARD() + ), + false + ); + + // Gho CCIP Steward + vm.expectRevert(); // getRateLimitAdmin doesn't exist yet + IUpgradeableLockReleaseTokenPool(MiscArbitrum.GHO_CCIP_TOKEN_POOL).getRateLimitAdmin(); + + executePayload(vm, address(proposal)); + + vm.prank(MiscArbitrum.PROXY_ADMIN); + address impl = TransparentUpgradeableProxy(payable(MiscArbitrum.GHO_CCIP_TOKEN_POOL)) + .implementation(); + + IUpgradeableLockReleaseTokenPool poolTokenImpl = IUpgradeableLockReleaseTokenPool( + proposal.NEW_CCIP_POOL_IMPL() + ); + + assertEq(impl, proposal.NEW_CCIP_POOL_IMPL()); + assertTrue(poolTokenImpl.owner() != address(0)); + + address owner = makeAddr('owner'); + address router = makeAddr('router'); + address[] memory list = new address[](0); + + // Implementation cannot be re-initialized + vm.expectRevert(); + poolTokenImpl.initialize(owner, list, router); + + // Proxy cannot be re-initialized + vm.expectRevert(); + IUpgradeableLockReleaseTokenPool(MiscArbitrum.GHO_CCIP_TOKEN_POOL).initialize( + owner, + list, + router + ); + + // Gho Bucket Steward + assertEq( + IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING).hasRole( + IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + proposal.GHO_BUCKET_STEWARD() + ), + true + ); + + assertTrue( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ) + ); + + // Gho Aave Steward + assertEq( + IAccessControl(address(AaveV3Arbitrum.ACL_MANAGER)).hasRole( + IACLManager(address(AaveV3Arbitrum.ACL_MANAGER)).RISK_ADMIN_ROLE(), + proposal.GHO_AAVE_STEWARD() + ), + true + ); + + assertEq( + IUpgradeableLockReleaseTokenPool(MiscArbitrum.GHO_CCIP_TOKEN_POOL).getRateLimitAdmin(), + proposal.GHO_CCIP_STEWARD() + ); + } + + function test_poolTokenFunctionality() public { + executePayload(vm, address(proposal)); + + IUpgradeableLockReleaseTokenPool poolToken = IUpgradeableLockReleaseTokenPool( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ); + + assertEq(proposal.GHO_CCIP_STEWARD(), poolToken.getRateLimitAdmin()); + assertEq(GovernanceV3Arbitrum.EXECUTOR_LVL_1, poolToken.owner()); + + address newAdmin = makeAddr('new-admin'); + + vm.startPrank(GovernanceV3Arbitrum.EXECUTOR_LVL_1); + poolToken.setRateLimitAdmin(newAdmin); + + assertEq(newAdmin, poolToken.getRateLimitAdmin()); + } + + function test_ghoAaveSteward_updateGhoBorrowRate() public { + executePayload(vm, address(proposal)); + + address rateStrategyAddress = AaveV3Arbitrum + .AAVE_PROTOCOL_DATA_PROVIDER + .getInterestRateStrategyAddress(AaveV3ArbitrumAssets.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, + AaveV3ArbitrumAssets.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.GHO_AAVE_STEWARD()).updateGhoBorrowRate( + newOptimalUsageRatio, + newBaseVariableBorrowRate, + newVariableRateSlope1, + newVariableRateSlope2 + ); + vm.stopPrank(); + + vm.clearMockedCalls(); + + assertEq(_getOptimalUsageRatio(), newOptimalUsageRatio); + assertEq(_getBaseVariableBorrowRate(), newBaseVariableBorrowRate); + assertEq(_getVariableRateSlope1(), newVariableRateSlope1); + assertEq(_getVariableRateSlope2(), newVariableRateSlope2); + } + + function test_ghoBucketSteward_updateFacilitatorBucketCapacity() public { + executePayload(vm, address(proposal)); + + (uint256 currentBucketCapacity, ) = IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING) + .getFacilitatorBucket(MiscArbitrum.GHO_CCIP_TOKEN_POOL); + vm.startPrank(RISK_COUNCIL); + uint128 newBucketCapacity = uint128(currentBucketCapacity) + 1; + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).updateFacilitatorBucketCapacity( + MiscArbitrum.GHO_CCIP_TOKEN_POOL, + newBucketCapacity + ); + (uint256 bucketCapacity, ) = IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING) + .getFacilitatorBucket(MiscArbitrum.GHO_CCIP_TOKEN_POOL); + assertEq(bucketCapacity, newBucketCapacity); + } + + function test_ghoBucketSteward_setControlledFacilitator() public { + executePayload(vm, address(proposal)); + + address[] memory newGsmList = new address[](1); + address gho_gsm_4626 = makeAddr('gho_gsm_4626'); + newGsmList[0] = gho_gsm_4626; + + vm.startPrank(GovernanceV3Arbitrum.EXECUTOR_LVL_1); + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).setControlledFacilitator(newGsmList, true); + assertTrue( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator(gho_gsm_4626) + ); + + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).setControlledFacilitator(newGsmList, false); + assertFalse( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator(gho_gsm_4626) + ); + } + + function test_ghoCcipSteward_updateRateLimit() public { + executePayload(vm, address(proposal)); + + RateLimiter.TokenBucket memory outboundConfig = IUpgradeableLockReleaseTokenPool( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ).getCurrentOutboundRateLimiterState(remoteChainSelector); + RateLimiter.TokenBucket memory inboundConfig = IUpgradeableLockReleaseTokenPool( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ).getCurrentInboundRateLimiterState(remoteChainSelector); + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: outboundConfig.capacity + 1, + rate: outboundConfig.rate + }); + + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: inboundConfig.capacity, + rate: inboundConfig.rate + }); + + IGhoCcipSteward steward = IGhoCcipSteward(proposal.GHO_CCIP_STEWARD()); + + // Currently rate limit set to 0, so can't even change by 1 because 100% of 0 is 0 + vm.startPrank(RISK_COUNCIL); + vm.expectRevert('INVALID_RATE_LIMIT_UPDATE'); + steward.updateRateLimit( + remoteChainSelector, + newOutboundConfig.isEnabled, + newOutboundConfig.capacity, + newOutboundConfig.rate, + newInboundConfig.isEnabled, + newInboundConfig.capacity, + newInboundConfig.rate + ); + } + + function test_ghoCcipSteward_revertUpdateRateLimitUnauthorizedBeforeUpgrade() public { + RateLimiter.TokenBucket memory mockConfig = RateLimiter.TokenBucket({ + rate: 50, + capacity: 50, + tokens: 1, + lastUpdated: 1, + isEnabled: true + }); + // Mocking response due to rate limit currently being 0 + vm.mockCall( + MiscArbitrum.GHO_CCIP_TOKEN_POOL, + abi.encodeWithSelector( + IUpgradeableLockReleaseTokenPool(MiscArbitrum.GHO_CCIP_TOKEN_POOL) + .getCurrentOutboundRateLimiterState + .selector, + remoteChainSelector + ), + abi.encode(mockConfig) + ); + + RateLimiter.TokenBucket memory outboundConfig = IUpgradeableLockReleaseTokenPool( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ).getCurrentOutboundRateLimiterState(remoteChainSelector); + RateLimiter.TokenBucket memory inboundConfig = IUpgradeableLockReleaseTokenPool( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ).getCurrentInboundRateLimiterState(remoteChainSelector); + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: outboundConfig.capacity, + rate: outboundConfig.rate + 1 + }); + + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: inboundConfig.capacity, + rate: inboundConfig.rate + }); + + IGhoCcipSteward steward = IGhoCcipSteward(proposal.GHO_CCIP_STEWARD()); + + vm.expectRevert('Only callable by owner'); + vm.prank(RISK_COUNCIL); + steward.updateRateLimit( + remoteChainSelector, + newOutboundConfig.isEnabled, + newOutboundConfig.capacity, + newOutboundConfig.rate, + newInboundConfig.isEnabled, + newInboundConfig.capacity, + newInboundConfig.rate + ); + } + + function test_ghoCcipSteward_updateRateLimitAfterPoolUpgrade() public { + executePayload(vm, address(proposal)); + + RateLimiter.TokenBucket memory mockConfig = RateLimiter.TokenBucket({ + rate: 50, + capacity: 50, + tokens: 1, + lastUpdated: 1, + isEnabled: true + }); + + // Mocking response due to rate limit currently being 0 + vm.mockCall( + MiscArbitrum.GHO_CCIP_TOKEN_POOL, + abi.encodeWithSelector( + IUpgradeableLockReleaseTokenPool(MiscArbitrum.GHO_CCIP_TOKEN_POOL) + .getCurrentOutboundRateLimiterState + .selector, + remoteChainSelector + ), + abi.encode(mockConfig) + ); + vm.mockCall( + MiscArbitrum.GHO_CCIP_TOKEN_POOL, + abi.encodeWithSelector( + IUpgradeableLockReleaseTokenPool(MiscArbitrum.GHO_CCIP_TOKEN_POOL) + .getCurrentInboundRateLimiterState + .selector, + remoteChainSelector + ), + abi.encode(mockConfig) + ); + + RateLimiter.TokenBucket memory outboundConfig = IUpgradeableLockReleaseTokenPool( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ).getCurrentOutboundRateLimiterState(remoteChainSelector); + RateLimiter.TokenBucket memory inboundConfig = IUpgradeableLockReleaseTokenPool( + MiscArbitrum.GHO_CCIP_TOKEN_POOL + ).getCurrentInboundRateLimiterState(remoteChainSelector); + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: outboundConfig.capacity + 1, + rate: outboundConfig.rate + }); + + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: inboundConfig.capacity + 1, + rate: inboundConfig.rate + }); + + vm.startPrank(RISK_COUNCIL); + IGhoCcipSteward(proposal.GHO_CCIP_STEWARD()).updateRateLimit( + remoteChainSelector, + newOutboundConfig.isEnabled, + newOutboundConfig.capacity, + newOutboundConfig.rate, + newInboundConfig.isEnabled, + newInboundConfig.capacity, + newInboundConfig.rate + ); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Arbitrum_GHOStewardV2Upgrade_20241007', + AaveV3Arbitrum.POOL, + address(proposal) + ); + } + + // Helpers + 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 = AaveV3Arbitrum + .AAVE_PROTOCOL_DATA_PROVIDER + .getInterestRateStrategyAddress(AaveV3ArbitrumAssets.GHO_UNDERLYING); + return + IDefaultInterestRateStrategyV2(rateStrategyAddress).getInterestRateDataBps( + AaveV3ArbitrumAssets.GHO_UNDERLYING + ); + } +} diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol new file mode 100644 index 000000000..df9f71d71 --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {IACLManager} from 'aave-address-book/AaveV3.sol'; +import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.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 {IUpgradeableLockReleaseTokenPool} from 'src/interfaces/ccip/IUpgradeableLockReleaseTokenPool.sol'; + +/** + * @title GHO Steward v2 Upgrade + * @author @karpatkey_TokenLogic + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0xc5e7df1536ef9fc71a7d2e2f6fee6e4e20e37a50b4e0f1646616d066b8697da5 + * - Discussion: https://governance.aave.com/t/arfc-gho-steward-v2-upgrade/19116 + */ +contract AaveV3Ethereum_GHOStewardV2Upgrade_20241007 is IProposalGenericExecutor { + // https://etherscan.io/address/0x46Aa1063e5265b43663E81329333B47c517A5409 + address public constant GHO_BUCKET_STEWARD = 0x46Aa1063e5265b43663E81329333B47c517A5409; + + // https://etherscan.io/address/0xfeb4e54591660f42288312ae8eb59e9f2b746b66 + address public constant GHO_AAVE_STEWARD = 0xFEb4e54591660F42288312AE8eB59e9f2B746b66; + + // https://etherscan.io/address/0x101efb7b9beb073b1219cd5473a7c8a2f2eb84f4 + address public constant GHO_CCIP_STEWARD = 0x101Efb7b9Beb073B1219Cd5473a7C8A2f2EB84f4; + + // https://etherscan.io/address/0xd1e856a947cdf56b4f000ee29d34f5808e0a6848 + address public constant GHO_GSM_STEWARD = 0xD1E856a947CdF56b4f000ee29d34F5808E0A6848; + + // https://etherscan.io/address/0xb9481a29f0f996BCAc759aB201FB3844c81866c4 + address public constant OLD_STEWARD = 0xb9481a29f0f996BCAc759aB201FB3844c81866c4; + + function execute() external { + // Old Gho Steward + AaveV3Ethereum.ACL_MANAGER.removeRiskAdmin(OLD_STEWARD); + + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).revokeRole( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + OLD_STEWARD + ); + + IGsm(MiscEthereum.GSM_USDC).revokeRole( + IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(), + OLD_STEWARD + ); + + IGsm(MiscEthereum.GSM_USDT).revokeRole( + IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(), + OLD_STEWARD + ); + + // Gho Bucket Steward + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).grantRole( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + GHO_BUCKET_STEWARD + ); + + address[] memory controlledFacilitators = new address[](3); + controlledFacilitators[0] = AaveV3EthereumAssets.GHO_A_TOKEN; + controlledFacilitators[1] = MiscEthereum.GSM_USDC; + controlledFacilitators[2] = MiscEthereum.GSM_USDT; + IGhoBucketSteward(GHO_BUCKET_STEWARD).setControlledFacilitator(controlledFacilitators, true); + + // Gho Aave Steward + IAccessControl(address(AaveV3Ethereum.ACL_MANAGER)).grantRole( + IACLManager(address(AaveV3Ethereum.ACL_MANAGER)).RISK_ADMIN_ROLE(), + GHO_AAVE_STEWARD + ); + + // Gho CCIP Steward + IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL).setRateLimitAdmin( + GHO_CCIP_STEWARD + ); + IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL).setBridgeLimitAdmin( + GHO_CCIP_STEWARD + ); + + // GHO GSM Steward + IGsm(MiscEthereum.GSM_USDC).grantRole( + IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(), + GHO_GSM_STEWARD + ); + IGsm(MiscEthereum.GSM_USDT).grantRole( + IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(), + GHO_GSM_STEWARD + ); + } +} diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol new file mode 100644 index 000000000..caa0f16b8 --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {IACLManager, IDefaultInterestRateStrategyV2} from 'aave-address-book/AaveV3.sol'; +import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; + +import {ReserveConfiguration} from './ReserveConfiguration.sol'; +import {DataTypes} from 'aave-v3-origin/contracts/protocol/libraries/types/DataTypes.sol'; + +import {AaveV3Ethereum_GHOStewardV2Upgrade_20241007} from './AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol'; +import {IGhoAaveSteward} from 'src/interfaces/IGhoAaveSteward.sol'; +import {IGhoBucketSteward} from 'src/interfaces/IGhoBucketSteward.sol'; +import {IGhoCcipSteward} from 'src/interfaces/IGhoCcipSteward.sol'; +import {IGsmSteward} from 'src/interfaces/IGsmSteward.sol'; +import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; +import {IGsm} from 'src/interfaces/IGsm.sol'; +import {IGsmFeeStrategy} from 'src/interfaces/IGsmFeeStrategy.sol'; +import {RateLimiter, IUpgradeableLockReleaseTokenPool} from './Ccip.sol'; + +/** + * @dev Test for AaveV3Ethereum_GHOStewardV2Upgrade_20241007 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol -vv + */ +contract AaveV3Ethereum_GHOStewardV2Upgrade_20241007_Test is ProtocolV3TestBase { + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + + AaveV3Ethereum_GHOStewardV2Upgrade_20241007 internal proposal; + address public RISK_COUNCIL = 0x8513e6F37dBc52De87b166980Fa3F50639694B60; + uint64 public remoteChainSelector = 4949039107694359620; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 20967884); + proposal = new AaveV3Ethereum_GHOStewardV2Upgrade_20241007(); + } + + function test_roles() public { + assertTrue(AaveV3Ethereum.ACL_MANAGER.isRiskAdmin(proposal.OLD_STEWARD())); + + assertEq( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).hasRole( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + proposal.OLD_STEWARD() + ), + true, + 'Did not have bucket manager role' + ); + + assertEq( + IGsm(MiscEthereum.GSM_USDC).hasRole( + IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(), + proposal.OLD_STEWARD() + ), + true, + 'Did not have USDC GSM Configurator role' + ); + + assertEq( + IGsm(MiscEthereum.GSM_USDT).hasRole( + IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(), + proposal.OLD_STEWARD() + ), + true, + 'Did not have USDT GSM Configurator role' + ); + + // Gho Bucket Steward + assertEq( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).hasRole( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + proposal.GHO_BUCKET_STEWARD() + ), + false + ); + + // Gho Aave Steward + assertEq( + IAccessControl(address(AaveV3Ethereum.ACL_MANAGER)).hasRole( + IACLManager(address(AaveV3Ethereum.ACL_MANAGER)).RISK_ADMIN_ROLE(), + proposal.GHO_AAVE_STEWARD() + ), + false + ); + + // Gho CCIP Steward + assertEq( + IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL).getBridgeLimitAdmin(), + address(0) + ); + assertEq( + IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL).getRateLimitAdmin(), + address(0) + ); + + // GHO GSM Steward + assertEq( + IGsm(MiscEthereum.GSM_USDC).hasRole( + IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(), + proposal.GHO_GSM_STEWARD() + ), + false + ); + assertEq( + IGsm(MiscEthereum.GSM_USDT).hasRole( + IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(), + proposal.GHO_GSM_STEWARD() + ), + false + ); + + executePayload(vm, address(proposal)); + + assertFalse(AaveV3Ethereum.ACL_MANAGER.isRiskAdmin(proposal.OLD_STEWARD())); + + assertEq( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).hasRole( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + proposal.OLD_STEWARD() + ), + false + ); + + assertEq( + IGsm(MiscEthereum.GSM_USDC).hasRole( + IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(), + proposal.OLD_STEWARD() + ), + false + ); + + assertEq( + IGsm(MiscEthereum.GSM_USDT).hasRole( + IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(), + proposal.OLD_STEWARD() + ), + false + ); + + // Gho Bucket Steward + assertEq( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).hasRole( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).BUCKET_MANAGER_ROLE(), + proposal.GHO_BUCKET_STEWARD() + ), + true + ); + + assertTrue( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator( + AaveV3EthereumAssets.GHO_A_TOKEN + ) + ); + assertTrue( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator( + MiscEthereum.GSM_USDC + ) + ); + assertTrue( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator( + MiscEthereum.GSM_USDT + ) + ); + + // Gho Aave Steward + assertEq( + IAccessControl(address(AaveV3Ethereum.ACL_MANAGER)).hasRole( + IACLManager(address(AaveV3Ethereum.ACL_MANAGER)).RISK_ADMIN_ROLE(), + proposal.GHO_AAVE_STEWARD() + ), + true + ); + + assertEq( + IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL).getBridgeLimitAdmin(), + proposal.GHO_CCIP_STEWARD() + ); + assertEq( + IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL).getRateLimitAdmin(), + proposal.GHO_CCIP_STEWARD() + ); + + // GHO GSM Steward + assertEq( + IGsm(MiscEthereum.GSM_USDC).hasRole( + IGsm(MiscEthereum.GSM_USDC).CONFIGURATOR_ROLE(), + proposal.GHO_GSM_STEWARD() + ), + true + ); + assertEq( + IGsm(MiscEthereum.GSM_USDT).hasRole( + IGsm(MiscEthereum.GSM_USDT).CONFIGURATOR_ROLE(), + proposal.GHO_GSM_STEWARD() + ), + true + ); + } + + function test_ghoAaveSteward_updateGhoBorrowCap() public { + executePayload(vm, address(proposal)); + + uint256 currentBorrowCap = _getGhoBorrowCap(); + uint256 newBorrowCap = currentBorrowCap + 1; + vm.startPrank(RISK_COUNCIL); + IGhoAaveSteward(proposal.GHO_AAVE_STEWARD()).updateGhoBorrowCap(newBorrowCap); + assertEq(_getGhoBorrowCap(), newBorrowCap); + } + + function test_ghoAaveSteward_updateGhoSupplyCap() public { + // Current config value is: 181338872942194741560446170431488 + // existingConfig |= (10 << 116); turns into 830948836238514615306439858845646848 + // this gives a value of 10 for the current supplyCap + uint256 configValue = 830948836238514615306439858845646848; + vm.mockCall( + address(AaveV3Ethereum.POOL), + abi.encodeWithSelector(AaveV3Ethereum.POOL.getConfiguration.selector), + abi.encode(configValue) + ); + + uint256 currentSupplyCap = _getGhoSupplyCap(); + assertEq(currentSupplyCap, 10); + uint256 newSupplyCap = currentSupplyCap + 1; + + executePayload(vm, address(proposal)); + + IGhoAaveSteward steward = IGhoAaveSteward(proposal.GHO_AAVE_STEWARD()); + + vm.startPrank(RISK_COUNCIL); + steward.updateGhoSupplyCap(newSupplyCap); + vm.stopPrank(); + + vm.clearMockedCalls(); + + assertEq(_getGhoSupplyCap(), newSupplyCap); + } + + function test_ghoAaveSteward_revertsChangeFromZero() public { + uint256 currentSupplyCap = _getGhoSupplyCap(); + assertEq(currentSupplyCap, 0); + uint256 newSupplyCap = currentSupplyCap + 1; + + IGhoAaveSteward steward = IGhoAaveSteward(proposal.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)); + + IDefaultInterestRateStrategyV2.InterestRateData memory currentRates = _getGhoBorrowRates(); + vm.startPrank(RISK_COUNCIL); + IGhoAaveSteward(proposal.GHO_AAVE_STEWARD()).updateGhoBorrowRate( + currentRates.optimalUsageRatio - 1, + currentRates.baseVariableBorrowRate + 1, + currentRates.variableRateSlope1 + 1, + currentRates.variableRateSlope2 + 1 + ); + assertEq(_getOptimalUsageRatio(), currentRates.optimalUsageRatio - 1); + assertEq(_getBaseVariableBorrowRate(), currentRates.baseVariableBorrowRate + 1); + assertEq(_getVariableRateSlope1(), currentRates.variableRateSlope1 + 1); + assertEq(_getVariableRateSlope2(), currentRates.variableRateSlope2 + 1); + } + + function test_ghoBucketSteward_updateFacilitatorBucketCapacity() public { + executePayload(vm, address(proposal)); + + (uint256 currentBucketCapacity, ) = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING) + .getFacilitatorBucket(AaveV3EthereumAssets.GHO_A_TOKEN); + vm.startPrank(RISK_COUNCIL); + uint128 newBucketCapacity = uint128(currentBucketCapacity) + 1; + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).updateFacilitatorBucketCapacity( + AaveV3EthereumAssets.GHO_A_TOKEN, + newBucketCapacity + ); + (uint256 capacity, ) = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).getFacilitatorBucket( + AaveV3EthereumAssets.GHO_A_TOKEN + ); + assertEq(newBucketCapacity, capacity); + } + + function test_ghoBucketSteward_setControlledFacilitator() public { + executePayload(vm, address(proposal)); + + address[] memory newGsmList = new address[](1); + newGsmList[0] = MiscEthereum.GSM_USDC; + + vm.startPrank(GovernanceV3Ethereum.EXECUTOR_LVL_1); + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).setControlledFacilitator(newGsmList, true); + assertTrue( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator( + MiscEthereum.GSM_USDC + ) + ); + + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).setControlledFacilitator(newGsmList, false); + assertFalse( + IGhoBucketSteward(proposal.GHO_BUCKET_STEWARD()).isControlledFacilitator( + MiscEthereum.GSM_USDC + ) + ); + } + + function test_ghoCcipSteward_updateBridgeLimit() public { + executePayload(vm, address(proposal)); + + uint256 oldBridgeLimit = IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL) + .getBridgeLimit(); + uint256 newBridgeLimit = oldBridgeLimit + 1; + vm.startPrank(RISK_COUNCIL); + IGhoCcipSteward(proposal.GHO_CCIP_STEWARD()).updateBridgeLimit(newBridgeLimit); + uint256 currentBridgeLimit = IUpgradeableLockReleaseTokenPool(MiscEthereum.GHO_CCIP_TOKEN_POOL) + .getBridgeLimit(); + assertEq(currentBridgeLimit, newBridgeLimit); + } + + function test_ghoCcip_stewardUpdateRateLimit() public { + executePayload(vm, address(proposal)); + + RateLimiter.TokenBucket memory outboundConfig = IUpgradeableLockReleaseTokenPool( + MiscEthereum.GHO_CCIP_TOKEN_POOL + ).getCurrentOutboundRateLimiterState(remoteChainSelector); + RateLimiter.TokenBucket memory inboundConfig = IUpgradeableLockReleaseTokenPool( + MiscEthereum.GHO_CCIP_TOKEN_POOL + ).getCurrentInboundRateLimiterState(remoteChainSelector); + + RateLimiter.Config memory newOutboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: outboundConfig.capacity + 1, + rate: outboundConfig.rate + }); + + RateLimiter.Config memory newInboundConfig = RateLimiter.Config({ + isEnabled: outboundConfig.isEnabled, + capacity: inboundConfig.capacity, + rate: inboundConfig.rate + }); + + IGhoCcipSteward steward = IGhoCcipSteward(proposal.GHO_CCIP_STEWARD()); + + // Currently rate limit set to 0, so can't even change by 1 because 100% of 0 is 0 + vm.expectRevert('INVALID_RATE_LIMIT_UPDATE'); + vm.startPrank(RISK_COUNCIL); + steward.updateRateLimit( + remoteChainSelector, + newOutboundConfig.isEnabled, + newOutboundConfig.capacity, + newOutboundConfig.rate, + newInboundConfig.isEnabled, + newInboundConfig.capacity, + newInboundConfig.rate + ); + } + + function test_ghoGsmSteward_pdateExposureCap() public { + executePayload(vm, address(proposal)); + + uint128 oldExposureCap = IGsm(MiscEthereum.GSM_USDC).getExposureCap(); + uint128 newExposureCap = oldExposureCap + 1; + + vm.startPrank(RISK_COUNCIL); + IGsmSteward(proposal.GHO_GSM_STEWARD()).updateGsmExposureCap( + MiscEthereum.GSM_USDC, + newExposureCap + ); + uint128 currentExposureCap = IGsm(MiscEthereum.GSM_USDC).getExposureCap(); + assertEq(currentExposureCap, newExposureCap); + } + + function test_ghoGsmSteward_updateGsmBuySellFees() public { + executePayload(vm, address(proposal)); + + address feeStrategy = IGsm(MiscEthereum.GSM_USDC).getFeeStrategy(); + uint256 buyFee = IGsmFeeStrategy(feeStrategy).getBuyFee(1e4); + uint256 sellFee = IGsmFeeStrategy(feeStrategy).getSellFee(1e4); + + vm.startPrank(RISK_COUNCIL); + IGsmSteward(proposal.GHO_GSM_STEWARD()).updateGsmBuySellFees( + MiscEthereum.GSM_USDC, + buyFee + 1, + sellFee + ); + address newStrategy = IGsm(MiscEthereum.GSM_USDC).getFeeStrategy(); + uint256 newBuyFee = IGsmFeeStrategy(newStrategy).getBuyFee(1e4); + assertEq(newBuyFee, buyFee + 1); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_GHOStewardV2Upgrade_20241007', + AaveV3Ethereum.POOL, + address(proposal) + ); + } + + // Helpers + + function _getGhoBorrowCap() internal view returns (uint256) { + DataTypes.ReserveConfigurationMap memory configuration = AaveV3Ethereum.POOL.getConfiguration( + AaveV3EthereumAssets.GHO_UNDERLYING + ); + return configuration.getBorrowCap(); + } + + function _getGhoSupplyCap() internal view returns (uint256) { + DataTypes.ReserveConfigurationMap memory configuration = AaveV3Ethereum.POOL.getConfiguration( + AaveV3EthereumAssets.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 = AaveV3Ethereum + .AAVE_PROTOCOL_DATA_PROVIDER + .getInterestRateStrategyAddress(AaveV3EthereumAssets.GHO_UNDERLYING); + return + IDefaultInterestRateStrategyV2(rateStrategyAddress).getInterestRateDataBps( + AaveV3EthereumAssets.GHO_UNDERLYING + ); + } +} diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/Ccip.sol b/src/20241007_Multi_GHOStewardV2Upgrade/Ccip.sol new file mode 100644 index 000000000..97a5468fd --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/Ccip.sol @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +// End consumer library. +library Client { + /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. + struct EVMTokenAmount { + address token; // token address on the local chain. + uint256 amount; // Amount of tokens. + } + + struct Any2EVMMessage { + bytes32 messageId; // MessageId corresponding to ccipSend on source. + uint64 sourceChainSelector; // Source chain selector. + bytes sender; // abi.decode(sender) if coming from an EVM chain. + bytes data; // payload sent in original message. + EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation. + } + + // If extraArgs is empty bytes, the default is 200k gas limit. + struct EVM2AnyMessage { + bytes receiver; // abi.encode(receiver address) for dest EVM chains + bytes data; // Data payload + EVMTokenAmount[] tokenAmounts; // Token transfers + address feeToken; // Address of feeToken. address(0) means you will send msg.value. + bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1) + } + + // bytes4(keccak256("CCIP EVMExtraArgsV1")); + bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9; + struct EVMExtraArgsV1 { + uint256 gasLimit; + } + + function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) { + return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs); + } +} + +/// @notice Implements Token Bucket rate limiting. +/// @dev Reduced library from https://github.com/aave/ccip/blob/ccip-gho/contracts/src/v0.8/ccip/libraries/RateLimiter.sol +/// @dev uint128 is safe for rate limiter state. +/// For USD value rate limiting, it can adequately store USD value in 18 decimals. +/// For ERC20 token amount rate limiting, all tokens that will be listed will have at most +/// a supply of uint128.max tokens, and it will therefore not overflow the bucket. +/// In exceptional scenarios where tokens consumed may be larger than uint128, +/// e.g. compromised issuer, an enabled RateLimiter will check and revert. +library RateLimiter { + error InvalidRatelimitRate(Config rateLimiterConfig); + error DisabledNonZeroRateLimit(Config config); + error RateLimitMustBeDisabled(); + + event ConfigChanged(Config config); + + struct TokenBucket { + uint128 tokens; // ──────╮ Current number of tokens that are in the bucket. + uint32 lastUpdated; // │ Timestamp in seconds of the last token refill, good for 100+ years. + bool isEnabled; // ──────╯ Indication whether the rate limiting is enabled or not + uint128 capacity; // ────╮ Maximum number of tokens that can be in the bucket. + uint128 rate; // ────────╯ Number of tokens per second that the bucket is refilled. + } + + struct Config { + bool isEnabled; // Indication whether the rate limiting should be enabled + uint128 capacity; // ────╮ Specifies the capacity of the rate limiter + uint128 rate; // ───────╯ Specifies the rate of the rate limiter + } + + /// @notice Gets the token bucket with its values for the block it was requested at. + /// @return The token bucket. + function _currentTokenBucketState( + TokenBucket memory bucket + ) internal view returns (TokenBucket memory) { + // We update the bucket to reflect the status at the exact time of the + // call. This means we might need to refill a part of the bucket based + // on the time that has passed since the last update. + bucket.tokens = uint128( + _calculateRefill( + bucket.capacity, + bucket.tokens, + block.timestamp - bucket.lastUpdated, + bucket.rate + ) + ); + bucket.lastUpdated = uint32(block.timestamp); + return bucket; + } + + /// @notice Sets the rate limited config. + /// @param s_bucket The token bucket + /// @param config The new config + function _setTokenBucketConfig(TokenBucket storage s_bucket, Config memory config) internal { + // First update the bucket to make sure the proper rate is used for all the time + // up until the config change. + uint256 timeDiff = block.timestamp - s_bucket.lastUpdated; + if (timeDiff != 0) { + s_bucket.tokens = uint128( + _calculateRefill(s_bucket.capacity, s_bucket.tokens, timeDiff, s_bucket.rate) + ); + + s_bucket.lastUpdated = uint32(block.timestamp); + } + + s_bucket.tokens = uint128(_min(config.capacity, s_bucket.tokens)); + s_bucket.isEnabled = config.isEnabled; + s_bucket.capacity = config.capacity; + s_bucket.rate = config.rate; + + emit ConfigChanged(config); + } + + /// @notice Validates the token bucket config + function _validateTokenBucketConfig(Config memory config, bool mustBeDisabled) internal pure { + if (config.isEnabled) { + if (config.rate >= config.capacity || config.rate == 0) { + revert InvalidRatelimitRate(config); + } + if (mustBeDisabled) { + revert RateLimitMustBeDisabled(); + } + } else { + if (config.rate != 0 || config.capacity != 0) { + revert DisabledNonZeroRateLimit(config); + } + } + } + + /// @notice Calculate refilled tokens + /// @param capacity bucket capacity + /// @param tokens current bucket tokens + /// @param timeDiff block time difference since last refill + /// @param rate bucket refill rate + /// @return the value of tokens after refill + function _calculateRefill( + uint256 capacity, + uint256 tokens, + uint256 timeDiff, + uint256 rate + ) private pure returns (uint256) { + return _min(capacity, tokens + timeDiff * rate); + } + + /// @notice Return the smallest of two integers + /// @param a first int + /// @param b second int + /// @return smallest + function _min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } +} + +/// @dev Reduced interface of CCIP Router contract with needed functions only +/// @dev Adapted from https://github.com/aave/ccip/blob/ccip-gho/contracts/src/v0.8/ccip/interfaces/IRouter.sol +interface IRouter { + error OnlyOffRamp(); + + /// @notice Route the message to its intended receiver contract. + /// @param message Client.Any2EVMMessage struct. + /// @param gasForCallExactCheck of params for exec + /// @param gasLimit set of params for exec + /// @param receiver set of params for exec + /// @dev if the receiver is a contracts that signals support for CCIP execution through EIP-165. + /// the contract is called. If not, only tokens are transferred. + /// @return success A boolean value indicating whether the ccip message was received without errors. + /// @return retBytes A bytes array containing return data form CCIP receiver. + /// @return gasUsed the gas used by the external customer call. Does not include any overhead. + function routeMessage( + Client.Any2EVMMessage calldata message, + uint16 gasForCallExactCheck, + uint256 gasLimit, + address receiver + ) external returns (bool success, bytes memory retBytes, uint256 gasUsed); + + /// @notice Returns the configured onramp for a specific destination chain. + /// @param destChainSelector The destination chain Id to get the onRamp for. + /// @return onRampAddress The address of the onRamp. + function getOnRamp(uint64 destChainSelector) external view returns (address onRampAddress); + + /// @notice Return true if the given offRamp is a configured offRamp for the given source chain. + /// @param sourceChainSelector The source chain selector to check. + /// @param offRamp The address of the offRamp to check. + function isOffRamp( + uint64 sourceChainSelector, + address offRamp + ) external view returns (bool isOffRamp); +} + +/// @dev Reduced interface of CCIP UpgradeableLockReleaseTokenPool contract with needed functions only +/// @dev Adapted from https://github.com/aave/ccip/blob/ccip-gho/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol +interface IUpgradeableLockReleaseTokenPool { + function initialize(address owner, address[] memory allowlist, address router) external; + + function owner() external view returns (address); + + function setBridgeLimit(uint256 newBridgeLimit) external; + + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + RateLimiter.Config memory outboundConfig, + RateLimiter.Config memory inboundConfig + ) external; + + function setRateLimitAdmin(address rateLimitAdmin) external; + + function setBridgeLimitAdmin(address bridgeLimitAdmin) external; + + function getRateLimitAdmin() external view returns (address); + + function getBridgeLimitAdmin() external view returns (address); + + function getBridgeLimit() external view returns (uint256); + + function getCurrentOutboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (RateLimiter.TokenBucket memory); + + function getCurrentInboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (RateLimiter.TokenBucket memory); +} diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md b/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md new file mode 100644 index 000000000..f11d830c6 --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md @@ -0,0 +1,62 @@ +--- +title: "GHO Steward v2 Upgrade" +author: "@karpatkey_TokenLogic" +discussions: "https://governance.aave.com/t/arfc-gho-steward-v2-upgrade/19116" +snapshot: "https://snapshot.org/#/aave.eth/proposal/0xc5e7df1536ef9fc71a7d2e2f6fee6e4e20e37a50b4e0f1646616d066b8697da5" +--- + +## Simple Summary + +This publication proposes upgrading the GHO Steward Role to incorporate additional functionality to accomodate the current and future growth of GHO. + +## Motivation + +In response to the expanding GHO ecosystem, GhoSteward v2 incorporates several different stewards to avoid the need to redeploy the entire steward contract whenever an upgrade or change is proposed. + +- GhoBucketSteward +- GhoAaveSteward +- GhoCcipSteward +- GhoGsmSteward + +Any future change to the GHO Steward functionality will require only the corresponding steward to be updated. This reduces the complexity and streamlines future amendments to the GHO Steward role. + +In addition, some new features have been added to allow for controlling parameters related to CCIP. + +## Specification + +The following contracts must be granted these roles by the DAO: + +- GhoAaveSteward + 1. RiskAdmin in Aave V3 Ethereum Pool +- GhoBucketSteward (both on Ethereum and Arbitrum) + 1. GhoTokenBucketManagerRole on GhoToken +- GhoCcipSteward + 1. RateLimitAdmin and BridgeLimitAdmin roles on GhoTokenPool (just rateLimitAdmin on Arbitrum) +- GhoGsmSteward + 1. Configurator in every GSM asset that the DAO wants the risk council to manage + +To facilitate the CCIP Steward, a new CCIP token pool implementation will be implemented on Arbitrum to allow setting of rateLimitAdmin. + +List of new addresses: + +| Contract | Arbitrum | Ethereum | +| ------------------- | ------------------------------------------ | ------------------------------------------ | +| New CCIP Token Pool | 0xb78eDA33EB5493d56f14a81023CE69438a562A2c | | +| Gho Bucket Steward | 0xa9afaE6A53E90f9E4CE0717162DF5Bc3d9aBe7B2 | 0x46Aa1063e5265b43663E81329333B47c517A5409 | +| Gho Aave Steward | 0xCd04D93bEA13921DaD05240D577090b5AC36DfCA | 0xFEb4e54591660F42288312AE8eB59e9f2B746b66 | +| Gho CCIP Steward | 0xb329CEFF2c362F315900d245eC88afd24C4949D5 | 0x101Efb7b9Beb073B1219Cd5473a7C8A2f2EB84f4 | +| Gho GSM Steward | | 0xD1E856a947CdF56b4f000ee29d34F5808E0A6848 | + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol) +- [Snapshot](https://snapshot.org/#/aave.eth/proposal/0xc5e7df1536ef9fc71a7d2e2f6fee6e4e20e37a50b4e0f1646616d066b8697da5) +- [Discussion](https://governance.aave.com/t/arfc-gho-steward-v2-upgrade/19116) +- The GHO Stewards implementations can be found [here](https://github.com/aave/gho-core/tree/main/src/contracts/misc) +- The original PR introducing the Stewards to GHO-CORE can be found [here](https://github.com/aave/gho-core/pull/414/files) +- The Certora review of the Stewards can be found [here](https://github.com/aave/gho-core/pull/423) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade_20241007.s.sol b/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade_20241007.s.sol new file mode 100644 index 000000000..57da03469 --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade_20241007.s.sol @@ -0,0 +1,87 @@ +// 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, ArbitrumScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; +import {AaveV3Ethereum_GHOStewardV2Upgrade_20241007} from './AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol'; +import {AaveV3Arbitrum_GHOStewardV2Upgrade_20241007} from './AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade_20241007.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/GHOStewardV2Upgrade_20241007.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_GHOStewardV2Upgrade_20241007).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Arbitrum + * deploy-command: make deploy-ledger contract=src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade_20241007.s.sol:DeployArbitrum chain=arbitrum + * verify-command: FOUNDRY_PROFILE=arbitrum npx catapulta-verify -b broadcast/GHOStewardV2Upgrade_20241007.s.sol/42161/run-latest.json + */ +contract DeployArbitrum is ArbitrumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Arbitrum_GHOStewardV2Upgrade_20241007).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/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade_20241007.s.sol:CreateProposal chain=mainnet + */ +contract CreateProposal is EthereumScript { + function run() external { + // create payloads + PayloadsControllerUtils.Payload[] memory payloads = new PayloadsControllerUtils.Payload[](2); + + // compose actions for validation + IPayloadsControllerCore.ExecutionAction[] + memory actionsEthereum = new IPayloadsControllerCore.ExecutionAction[](1); + actionsEthereum[0] = GovV3Helpers.buildAction( + type(AaveV3Ethereum_GHOStewardV2Upgrade_20241007).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsArbitrum = new IPayloadsControllerCore.ExecutionAction[](1); + actionsArbitrum[0] = GovV3Helpers.buildAction( + type(AaveV3Arbitrum_GHOStewardV2Upgrade_20241007).creationCode + ); + payloads[1] = GovV3Helpers.buildArbitrumPayload(vm, actionsArbitrum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile(vm, 'src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md') + ); + } +} diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/ReserveConfiguration.sol b/src/20241007_Multi_GHOStewardV2Upgrade/ReserveConfiguration.sol new file mode 100644 index 000000000..670b4ca6d --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/ReserveConfiguration.sol @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {Errors} from 'aave-v3-origin/contracts/protocol/libraries/helpers/Errors.sol'; +import {DataTypes} from 'aave-v3-origin/contracts/protocol/libraries/types/DataTypes.sol'; + +/** + * @title ReserveConfiguration library + * @author Aave + * @notice Implements the bitmap logic to handle the reserve configuration + */ +library ReserveConfiguration { + uint256 internal constant LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore + uint256 internal constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore + uint256 internal constant LIQUIDATION_BONUS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore + uint256 internal constant DECIMALS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore + uint256 internal constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant BORROWABLE_IN_ISOLATION_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant SILOED_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant FLASHLOAN_ENABLED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant BORROW_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant SUPPLY_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant LIQUIDATION_PROTOCOL_FEE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant EMODE_CATEGORY_MASK = 0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant UNBACKED_MINT_CAP_MASK = 0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + uint256 internal constant DEBT_CEILING_MASK = 0xF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore + + /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed + uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; + uint256 internal constant LIQUIDATION_BONUS_START_BIT_POSITION = 32; + uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48; + uint256 internal constant IS_ACTIVE_START_BIT_POSITION = 56; + uint256 internal constant IS_FROZEN_START_BIT_POSITION = 57; + uint256 internal constant BORROWING_ENABLED_START_BIT_POSITION = 58; + uint256 internal constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; + uint256 internal constant IS_PAUSED_START_BIT_POSITION = 60; + uint256 internal constant BORROWABLE_IN_ISOLATION_START_BIT_POSITION = 61; + uint256 internal constant SILOED_BORROWING_START_BIT_POSITION = 62; + uint256 internal constant FLASHLOAN_ENABLED_START_BIT_POSITION = 63; + uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64; + uint256 internal constant BORROW_CAP_START_BIT_POSITION = 80; + uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116; + uint256 internal constant LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION = 152; + uint256 internal constant EMODE_CATEGORY_START_BIT_POSITION = 168; + uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176; + uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212; + + uint256 internal constant MAX_VALID_LTV = 65535; + uint256 internal constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; + uint256 internal constant MAX_VALID_LIQUIDATION_BONUS = 65535; + uint256 internal constant MAX_VALID_DECIMALS = 255; + uint256 internal constant MAX_VALID_RESERVE_FACTOR = 65535; + uint256 internal constant MAX_VALID_BORROW_CAP = 68719476735; + uint256 internal constant MAX_VALID_SUPPLY_CAP = 68719476735; + uint256 internal constant MAX_VALID_LIQUIDATION_PROTOCOL_FEE = 65535; + uint256 internal constant MAX_VALID_EMODE_CATEGORY = 255; + uint256 internal constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735; + uint256 internal constant MAX_VALID_DEBT_CEILING = 1099511627775; + + uint256 public constant DEBT_CEILING_DECIMALS = 2; + uint16 public constant MAX_RESERVES_COUNT = 128; + + /** + * @notice Sets the Loan to Value of the reserve + * @param self The reserve configuration + * @param ltv The new ltv + */ + function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure { + require(ltv <= MAX_VALID_LTV, Errors.INVALID_LTV); + + self.data = (self.data & LTV_MASK) | ltv; + } + + /** + * @notice Gets the Loan to Value of the reserve + * @param self The reserve configuration + * @return The loan to value + */ + function getLtv(DataTypes.ReserveConfigurationMap memory self) internal pure returns (uint256) { + return self.data & ~LTV_MASK; + } + + /** + * @notice Sets the liquidation threshold of the reserve + * @param self The reserve configuration + * @param threshold The new liquidation threshold + */ + function setLiquidationThreshold( + DataTypes.ReserveConfigurationMap memory self, + uint256 threshold + ) internal pure { + require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.INVALID_LIQ_THRESHOLD); + + self.data = + (self.data & LIQUIDATION_THRESHOLD_MASK) | + (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION); + } + + /** + * @notice Gets the liquidation threshold of the reserve + * @param self The reserve configuration + * @return The liquidation threshold + */ + function getLiquidationThreshold( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; + } + + /** + * @notice Sets the liquidation bonus of the reserve + * @param self The reserve configuration + * @param bonus The new liquidation bonus + */ + function setLiquidationBonus( + DataTypes.ReserveConfigurationMap memory self, + uint256 bonus + ) internal pure { + require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.INVALID_LIQ_BONUS); + + self.data = + (self.data & LIQUIDATION_BONUS_MASK) | + (bonus << LIQUIDATION_BONUS_START_BIT_POSITION); + } + + /** + * @notice Gets the liquidation bonus of the reserve + * @param self The reserve configuration + * @return The liquidation bonus + */ + function getLiquidationBonus( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION; + } + + /** + * @notice Sets the decimals of the underlying asset of the reserve + * @param self The reserve configuration + * @param decimals The decimals + */ + function setDecimals( + DataTypes.ReserveConfigurationMap memory self, + uint256 decimals + ) internal pure { + require(decimals <= MAX_VALID_DECIMALS, Errors.INVALID_DECIMALS); + + self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION); + } + + /** + * @notice Gets the decimals of the underlying asset of the reserve + * @param self The reserve configuration + * @return The decimals of the asset + */ + function getDecimals( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; + } + + /** + * @notice Sets the active state of the reserve + * @param self The reserve configuration + * @param active The active state + */ + function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure { + self.data = + (self.data & ACTIVE_MASK) | + (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION); + } + + /** + * @notice Gets the active state of the reserve + * @param self The reserve configuration + * @return The active state + */ + function getActive(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) { + return (self.data & ~ACTIVE_MASK) != 0; + } + + /** + * @notice Sets the frozen state of the reserve + * @param self The reserve configuration + * @param frozen The frozen state + */ + function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure { + self.data = + (self.data & FROZEN_MASK) | + (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION); + } + + /** + * @notice Gets the frozen state of the reserve + * @param self The reserve configuration + * @return The frozen state + */ + function getFrozen(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) { + return (self.data & ~FROZEN_MASK) != 0; + } + + /** + * @notice Sets the paused state of the reserve + * @param self The reserve configuration + * @param paused The paused state + */ + function setPaused(DataTypes.ReserveConfigurationMap memory self, bool paused) internal pure { + self.data = + (self.data & PAUSED_MASK) | + (uint256(paused ? 1 : 0) << IS_PAUSED_START_BIT_POSITION); + } + + /** + * @notice Gets the paused state of the reserve + * @param self The reserve configuration + * @return The paused state + */ + function getPaused(DataTypes.ReserveConfigurationMap memory self) internal pure returns (bool) { + return (self.data & ~PAUSED_MASK) != 0; + } + + /** + * @notice Sets the borrowable in isolation flag for the reserve. + * @dev When this flag is set to true, the asset will be borrowable against isolated collaterals and the borrowed + * amount will be accumulated in the isolated collateral's total debt exposure. + * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep + * consistency in the debt ceiling calculations. + * @param self The reserve configuration + * @param borrowable True if the asset is borrowable + */ + function setBorrowableInIsolation( + DataTypes.ReserveConfigurationMap memory self, + bool borrowable + ) internal pure { + self.data = + (self.data & BORROWABLE_IN_ISOLATION_MASK) | + (uint256(borrowable ? 1 : 0) << BORROWABLE_IN_ISOLATION_START_BIT_POSITION); + } + + /** + * @notice Gets the borrowable in isolation flag for the reserve. + * @dev If the returned flag is true, the asset is borrowable against isolated collateral. Assets borrowed with + * isolated collateral is accounted for in the isolated collateral's total debt exposure. + * @dev Only assets of the same family (eg USD stablecoins) should be borrowable in isolation mode to keep + * consistency in the debt ceiling calculations. + * @param self The reserve configuration + * @return The borrowable in isolation flag + */ + function getBorrowableInIsolation( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (bool) { + return (self.data & ~BORROWABLE_IN_ISOLATION_MASK) != 0; + } + + /** + * @notice Sets the siloed borrowing flag for the reserve. + * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset. + * @param self The reserve configuration + * @param siloed True if the asset is siloed + */ + function setSiloedBorrowing( + DataTypes.ReserveConfigurationMap memory self, + bool siloed + ) internal pure { + self.data = + (self.data & SILOED_BORROWING_MASK) | + (uint256(siloed ? 1 : 0) << SILOED_BORROWING_START_BIT_POSITION); + } + + /** + * @notice Gets the siloed borrowing flag for the reserve. + * @dev When this flag is set to true, users borrowing this asset will not be allowed to borrow any other asset. + * @param self The reserve configuration + * @return The siloed borrowing flag + */ + function getSiloedBorrowing( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (bool) { + return (self.data & ~SILOED_BORROWING_MASK) != 0; + } + + /** + * @notice Enables or disables borrowing on the reserve + * @param self The reserve configuration + * @param enabled True if the borrowing needs to be enabled, false otherwise + */ + function setBorrowingEnabled( + DataTypes.ReserveConfigurationMap memory self, + bool enabled + ) internal pure { + self.data = + (self.data & BORROWING_MASK) | + (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION); + } + + /** + * @notice Gets the borrowing state of the reserve + * @param self The reserve configuration + * @return The borrowing state + */ + function getBorrowingEnabled( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (bool) { + return (self.data & ~BORROWING_MASK) != 0; + } + + /** + * @notice Enables or disables stable rate borrowing on the reserve + * @param self The reserve configuration + * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise + */ + function setStableRateBorrowingEnabled( + DataTypes.ReserveConfigurationMap memory self, + bool enabled + ) internal pure { + self.data = + (self.data & STABLE_BORROWING_MASK) | + (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION); + } + + /** + * @notice Gets the stable rate borrowing state of the reserve + * @param self The reserve configuration + * @return The stable rate borrowing state + */ + function getStableRateBorrowingEnabled( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (bool) { + return (self.data & ~STABLE_BORROWING_MASK) != 0; + } + + /** + * @notice Sets the reserve factor of the reserve + * @param self The reserve configuration + * @param reserveFactor The reserve factor + */ + function setReserveFactor( + DataTypes.ReserveConfigurationMap memory self, + uint256 reserveFactor + ) internal pure { + require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.INVALID_RESERVE_FACTOR); + + self.data = + (self.data & RESERVE_FACTOR_MASK) | + (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION); + } + + /** + * @notice Gets the reserve factor of the reserve + * @param self The reserve configuration + * @return The reserve factor + */ + function getReserveFactor( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; + } + + /** + * @notice Sets the borrow cap of the reserve + * @param self The reserve configuration + * @param borrowCap The borrow cap + */ + function setBorrowCap( + DataTypes.ReserveConfigurationMap memory self, + uint256 borrowCap + ) internal pure { + require(borrowCap <= MAX_VALID_BORROW_CAP, Errors.INVALID_BORROW_CAP); + + self.data = (self.data & BORROW_CAP_MASK) | (borrowCap << BORROW_CAP_START_BIT_POSITION); + } + + /** + * @notice Gets the borrow cap of the reserve + * @param self The reserve configuration + * @return The borrow cap + */ + function getBorrowCap( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION; + } + + /** + * @notice Sets the supply cap of the reserve + * @param self The reserve configuration + * @param supplyCap The supply cap + */ + function setSupplyCap( + DataTypes.ReserveConfigurationMap memory self, + uint256 supplyCap + ) internal pure { + require(supplyCap <= MAX_VALID_SUPPLY_CAP, Errors.INVALID_SUPPLY_CAP); + + self.data = (self.data & SUPPLY_CAP_MASK) | (supplyCap << SUPPLY_CAP_START_BIT_POSITION); + } + + /** + * @notice Gets the supply cap of the reserve + * @param self The reserve configuration + * @return The supply cap + */ + function getSupplyCap( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION; + } + + /** + * @notice Sets the debt ceiling in isolation mode for the asset + * @param self The reserve configuration + * @param ceiling The maximum debt ceiling for the asset + */ + function setDebtCeiling( + DataTypes.ReserveConfigurationMap memory self, + uint256 ceiling + ) internal pure { + require(ceiling <= MAX_VALID_DEBT_CEILING, Errors.INVALID_DEBT_CEILING); + + self.data = (self.data & DEBT_CEILING_MASK) | (ceiling << DEBT_CEILING_START_BIT_POSITION); + } + + /** + * @notice Gets the debt ceiling for the asset if the asset is in isolation mode + * @param self The reserve configuration + * @return The debt ceiling (0 = isolation mode disabled) + */ + function getDebtCeiling( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~DEBT_CEILING_MASK) >> DEBT_CEILING_START_BIT_POSITION; + } + + /** + * @notice Sets the liquidation protocol fee of the reserve + * @param self The reserve configuration + * @param liquidationProtocolFee The liquidation protocol fee + */ + function setLiquidationProtocolFee( + DataTypes.ReserveConfigurationMap memory self, + uint256 liquidationProtocolFee + ) internal pure { + require( + liquidationProtocolFee <= MAX_VALID_LIQUIDATION_PROTOCOL_FEE, + Errors.INVALID_LIQUIDATION_PROTOCOL_FEE + ); + + self.data = + (self.data & LIQUIDATION_PROTOCOL_FEE_MASK) | + (liquidationProtocolFee << LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION); + } + + /** + * @dev Gets the liquidation protocol fee + * @param self The reserve configuration + * @return The liquidation protocol fee + */ + function getLiquidationProtocolFee( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return + (self.data & ~LIQUIDATION_PROTOCOL_FEE_MASK) >> LIQUIDATION_PROTOCOL_FEE_START_BIT_POSITION; + } + + /** + * @notice Sets the unbacked mint cap of the reserve + * @param self The reserve configuration + * @param unbackedMintCap The unbacked mint cap + */ + function setUnbackedMintCap( + DataTypes.ReserveConfigurationMap memory self, + uint256 unbackedMintCap + ) internal pure { + require(unbackedMintCap <= MAX_VALID_UNBACKED_MINT_CAP, Errors.INVALID_UNBACKED_MINT_CAP); + + self.data = + (self.data & UNBACKED_MINT_CAP_MASK) | + (unbackedMintCap << UNBACKED_MINT_CAP_START_BIT_POSITION); + } + + /** + * @dev Gets the unbacked mint cap of the reserve + * @param self The reserve configuration + * @return The unbacked mint cap + */ + function getUnbackedMintCap( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~UNBACKED_MINT_CAP_MASK) >> UNBACKED_MINT_CAP_START_BIT_POSITION; + } + + /** + * @notice Sets the eMode asset category + * @param self The reserve configuration + * @param category The asset category when the user selects the eMode + */ + function setEModeCategory( + DataTypes.ReserveConfigurationMap memory self, + uint256 category + ) internal pure { + require(category <= MAX_VALID_EMODE_CATEGORY, Errors.INVALID_EMODE_CATEGORY); + + self.data = (self.data & EMODE_CATEGORY_MASK) | (category << EMODE_CATEGORY_START_BIT_POSITION); + } + + /** + * @dev Gets the eMode asset category + * @param self The reserve configuration + * @return The eMode category for the asset + */ + function getEModeCategory( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256) { + return (self.data & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_START_BIT_POSITION; + } + + /** + * @notice Sets the flashloanable flag for the reserve + * @param self The reserve configuration + * @param flashLoanEnabled True if the asset is flashloanable, false otherwise + */ + function setFlashLoanEnabled( + DataTypes.ReserveConfigurationMap memory self, + bool flashLoanEnabled + ) internal pure { + self.data = + (self.data & FLASHLOAN_ENABLED_MASK) | + (uint256(flashLoanEnabled ? 1 : 0) << FLASHLOAN_ENABLED_START_BIT_POSITION); + } + + /** + * @notice Gets the flashloanable flag for the reserve + * @param self The reserve configuration + * @return The flashloanable flag + */ + function getFlashLoanEnabled( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (bool) { + return (self.data & ~FLASHLOAN_ENABLED_MASK) != 0; + } + + /** + * @notice Gets the configuration flags of the reserve + * @param self The reserve configuration + * @return The state flag representing active + * @return The state flag representing frozen + * @return The state flag representing borrowing enabled + * @return The state flag representing stableRateBorrowing enabled + * @return The state flag representing paused + */ + function getFlags( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (bool, bool, bool, bool, bool) { + uint256 dataLocal = self.data; + + return ( + (dataLocal & ~ACTIVE_MASK) != 0, + (dataLocal & ~FROZEN_MASK) != 0, + (dataLocal & ~BORROWING_MASK) != 0, + (dataLocal & ~STABLE_BORROWING_MASK) != 0, + (dataLocal & ~PAUSED_MASK) != 0 + ); + } + + /** + * @notice Gets the configuration parameters of the reserve from storage + * @param self The reserve configuration + * @return The state param representing ltv + * @return The state param representing liquidation threshold + * @return The state param representing liquidation bonus + * @return The state param representing reserve decimals + * @return The state param representing reserve factor + * @return The state param representing eMode category + */ + function getParams( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256, uint256, uint256, uint256, uint256, uint256) { + uint256 dataLocal = self.data; + + return ( + dataLocal & ~LTV_MASK, + (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, + (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, + (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, + (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION, + (dataLocal & ~EMODE_CATEGORY_MASK) >> EMODE_CATEGORY_START_BIT_POSITION + ); + } + + /** + * @notice Gets the caps parameters of the reserve from storage + * @param self The reserve configuration + * @return The state param representing borrow cap + * @return The state param representing supply cap. + */ + function getCaps( + DataTypes.ReserveConfigurationMap memory self + ) internal pure returns (uint256, uint256) { + uint256 dataLocal = self.data; + + return ( + (dataLocal & ~BORROW_CAP_MASK) >> BORROW_CAP_START_BIT_POSITION, + (dataLocal & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION + ); + } +} diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/config.ts b/src/20241007_Multi_GHOStewardV2Upgrade/config.ts new file mode 100644 index 000000000..7e2905f67 --- /dev/null +++ b/src/20241007_Multi_GHOStewardV2Upgrade/config.ts @@ -0,0 +1,18 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3Ethereum', 'AaveV3Arbitrum'], + title: 'GHO Steward v2 Upgrade', + shortName: 'GHOStewardV2Upgrade', + date: '20241007', + author: '@karpatkey_TokenLogic', + discussion: 'https://governance.aave.com/t/arfc-gho-steward-v2-upgrade/19116', + snapshot: + 'https://snapshot.org/#/aave.eth/proposal/0xc5e7df1536ef9fc71a7d2e2f6fee6e4e20e37a50b4e0f1646616d066b8697da5', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 20914155}}, + AaveV3Arbitrum: {configs: {OTHERS: {}}, cache: {blockNumber: 261341067}}, + }, +}; diff --git a/src/interfaces/IGhoAaveSteward.sol b/src/interfaces/IGhoAaveSteward.sol new file mode 100644 index 000000000..002ae3ef8 --- /dev/null +++ b/src/interfaces/IGhoAaveSteward.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +/** + * @title IGhoAaveSteward + * @author Aave Labs + * @notice Defines the basic interface of the GhoAaveSteward + */ +interface IGhoAaveSteward { + /** + * @notice Struct storing the last update by the steward of each borrow rate param + */ + struct GhoDebounce { + uint40 ghoBorrowCapLastUpdate; + uint40 ghoSupplyCapLastUpdate; + uint40 ghoBorrowRateLastUpdate; + } + + /** + * @notice Struct storing the configuration for the borrow rate params + */ + struct BorrowRateConfig { + uint16 optimalUsageRatioMaxChange; + uint32 baseVariableBorrowRateMaxChange; + uint32 variableRateSlope1MaxChange; + uint32 variableRateSlope2MaxChange; + } + + /** + * @notice Updates the borrow rate of GHO, only if: + * - respects `MINIMUM_DELAY`, the minimum time delay between updates + * - the update changes parameters up to the maximum allowed change according to risk config + * - the update is lower than `GHO_BORROW_RATE_MAX` + * @dev Only callable by Risk Council + * @dev Values are all expressed in BPS + * @param optimalUsageRatio The new optimal usage ratio + * @param baseVariableBorrowRate The new base variable borrow rate + * @param variableRateSlope1 The new variable rate slope 1 + * @param variableRateSlope2 The new variable rate slope 2 + */ + function updateGhoBorrowRate( + uint16 optimalUsageRatio, + uint32 baseVariableBorrowRate, + uint32 variableRateSlope1, + uint32 variableRateSlope2 + ) external; + + /** + * @notice Updates the GHO borrow cap, only if: + * - respects `MINIMUM_DELAY`, the minimum time delay between updates + * - the update changes up to 100% upwards or downwards + * @dev Only callable by Risk Council + * @param newBorrowCap The new borrow cap (in whole tokens) + */ + function updateGhoBorrowCap(uint256 newBorrowCap) external; + + /** + * @notice Updates the GHO supply cap, only if: + * - respects `MINIMUM_DELAY`, the minimum time delay between updates + * - the update changes up to 100% upwards or downwards + * @dev Only callable by Risk Council + * @param newSupplyCap The new supply cap (in whole tokens) + */ + function updateGhoSupplyCap(uint256 newSupplyCap) external; + + /** + * @notice Updates the configuration conditions for borrow rate changes + * @dev Values are all expressed in BPS + * @param optimalUsageRatioMaxChange The new allowed max percentage change for optimal usage ratio + * @param baseVariableBorrowRateMaxChange The new allowed max percentage change for base variable borrow rate + * @param variableRateSlope1MaxChange The new allowed max percentage change for variable rate slope 1 + * @param variableRateSlope2MaxChange The new allowed max percentage change for variable rate slope 2 + */ + function setBorrowRateConfig( + uint16 optimalUsageRatioMaxChange, + uint32 baseVariableBorrowRateMaxChange, + uint32 variableRateSlope1MaxChange, + uint32 variableRateSlope2MaxChange + ) external; + + /** + * @notice Returns the configuration conditions for a GHO borrow rate change + * @return struct containing the borrow rate configuration + */ + function getBorrowRateConfig() external view returns (BorrowRateConfig memory); + + /** + * @notice Returns timestamp of the last update of GHO parameters + * @return The GhoDebounce struct describing the last update of GHO parameters + */ + function getGhoTimelocks() external view returns (GhoDebounce memory); + + /** + * @notice Returns maximum value that can be assigned to GHO borrow rate. + * @return The maximum value that can be assigned to GHO borrow rate in ray (e.g. 0.01e27 results in 1.0%) + */ + function GHO_BORROW_RATE_MAX() external view returns (uint32); + + /** + * @notice The address of pool data provider of the POOL the steward controls + */ + function POOL_DATA_PROVIDER() external view returns (address); + + /** + * @notice Returns the minimum delay that must be respected between parameters update. + * @return The minimum delay between parameter updates (in seconds) + */ + function MINIMUM_DELAY() external view returns (uint256); + + /** + * @notice Returns the address of the Pool Addresses Provider of the Aave V3 Ethereum Pool + * @return The address of the PoolAddressesProvider of Aave V3 Ethereum Pool + */ + function POOL_ADDRESSES_PROVIDER() external view returns (address); + + /** + * @notice Returns the address of the Gho Token + * @return The address of the GhoToken + */ + function GHO_TOKEN() external view returns (address); + + /** + * @notice Returns the address of the risk council + * @return The address of the RiskCouncil + */ + function RISK_COUNCIL() external view returns (address); +} diff --git a/src/interfaces/IGhoBucketSteward.sol b/src/interfaces/IGhoBucketSteward.sol new file mode 100644 index 000000000..19dae0c77 --- /dev/null +++ b/src/interfaces/IGhoBucketSteward.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +/** + * @title IGhoBucketSteward + * @author Aave Labs + * @notice Defines the basic interface of the GhoBucketSteward + */ +interface IGhoBucketSteward { + /** + * @notice Updates the bucket capacity of facilitator, only if: + * - respects `MINIMUM_DELAY`, the minimum time delay between updates + * - the update changes up to 100% upwards + * - the facilitator is controlled + * @dev Only callable by Risk Council + * @param facilitator The facilitator address + * @param newBucketCapacity The new facilitator bucket capacity + */ + function updateFacilitatorBucketCapacity(address facilitator, uint128 newBucketCapacity) external; + + /** + * @notice Adds/Removes controlled facilitators + * @dev Only callable by owner + * @param facilitatorList A list of facilitators addresses to add to control + * @param approve True to add as controlled facilitators, false to remove + */ + function setControlledFacilitator(address[] memory facilitatorList, bool approve) external; + + /** + * @notice Returns the list of controlled facilitators by this steward. + * @return An array of facilitator addresses + */ + function getControlledFacilitators() external view returns (address[] memory); + + /** + * @notice Checks if a facilitator is controlled by this steward + * @param facilitator The facilitator address to check + * @return True if the facilitator is controlled by this steward + */ + function isControlledFacilitator(address facilitator) external view returns (bool); + + /** + * @notice Returns timestamp of the facilitators last bucket capacity update + * @param facilitator The facilitator address + * @return The unix time of the last bucket capacity (in seconds). + */ + function getFacilitatorBucketCapacityTimelock(address facilitator) external view returns (uint40); + + /** + * @notice Returns the minimum delay that must be respected between parameters update. + * @return The minimum delay between parameter updates (in seconds) + */ + function MINIMUM_DELAY() external view returns (uint256); + + /** + * @notice Returns the address of the Gho Token + * @return The address of the GhoToken + */ + function GHO_TOKEN() external view returns (address); + + /** + * @notice Returns the address of the risk council + * @return The address of the RiskCouncil + */ + function RISK_COUNCIL() external view returns (address); +} diff --git a/src/interfaces/IGhoCcipSteward.sol b/src/interfaces/IGhoCcipSteward.sol new file mode 100644 index 000000000..39bec9294 --- /dev/null +++ b/src/interfaces/IGhoCcipSteward.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +/** + * @title IGhoCcipSteward + * @author Aave Labs + * @notice Defines the basic interface of the GhoCcipSteward + */ +interface IGhoCcipSteward { + struct CcipDebounce { + uint40 bridgeLimitLastUpdate; + uint40 rateLimitLastUpdate; + } + + /** + * @notice Updates the CCIP bridge limit + * @dev Only callable by Risk Council + * @param newBridgeLimit The new desired bridge limit + */ + function updateBridgeLimit(uint256 newBridgeLimit) external; + + /** + * @notice Updates the CCIP rate limit config + * @dev Only callable by Risk Council + * @dev Rate limit update must be consistent with other pools' rate limit + * @param remoteChainSelector The remote chain selector for which the rate limits apply. + * @param outboundEnabled True if the outbound rate limiter is enabled. + * @param outboundCapacity The outbound rate limiter capacity. + * @param outboundRate The outbound rate limiter rate. + * @param inboundEnabled True if the inbound rate limiter is enabled. + * @param inboundCapacity The inbound rate limiter capacity. + * @param inboundRate The inbound rate limiter rate. + */ + function updateRateLimit( + uint64 remoteChainSelector, + bool outboundEnabled, + uint128 outboundCapacity, + uint128 outboundRate, + bool inboundEnabled, + uint128 inboundCapacity, + uint128 inboundRate + ) external; + + /** + * @notice Returns the minimum delay that must be respected between parameters update. + * @return The minimum delay between parameter updates (in seconds) + */ + function MINIMUM_DELAY() external view returns (uint256); + + /** + * @notice Returns the address of the Gho Token + * @return The address of the GhoToken + */ + function GHO_TOKEN() external view returns (address); + + /** + * @notice Returns the address of the Gho CCIP Token Pool + * @return The address of the Gho CCIP Token Pool + */ + function GHO_TOKEN_POOL() external view returns (address); + + /** + * @notice Returns whether the bridge limit feature is supported in the GhoTokenPool + * @return True if bridge limit is enabled in the CCIP GhoTokenPool, false otherwise + */ + function BRIDGE_LIMIT_ENABLED() external view returns (bool); + + /** + * @notice Returns the address of the risk council + * @return The address of the RiskCouncil + */ + function RISK_COUNCIL() external view returns (address); +} diff --git a/src/interfaces/IGhoToken.sol b/src/interfaces/IGhoToken.sol index ba73caa3b..2c78aeebd 100644 --- a/src/interfaces/IGhoToken.sol +++ b/src/interfaces/IGhoToken.sol @@ -32,4 +32,38 @@ interface IGhoToken { * @return The level of the facilitator's bucket */ function getFacilitatorBucket(address facilitator) external view returns (uint256, uint256); + + /** + * @notice Returns the identifier of the Bucket Manager Role + * @return The bytes32 id hash of the BucketManager role + */ + function BUCKET_MANAGER_ROLE() external view returns (bytes32); + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) external; + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) external view returns (bool); + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) external; } diff --git a/src/interfaces/IGsm.sol b/src/interfaces/IGsm.sol new file mode 100644 index 000000000..4722761a3 --- /dev/null +++ b/src/interfaces/IGsm.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IGsm { + /** + * @notice Returns the identifier of the Configurator Role + * @return The bytes32 id hash of the Configurator role + */ + function CONFIGURATOR_ROLE() external view returns (bytes32); + + /** + * @notice Returns the exposure limit to the underlying asset + * @return The maximum amount of underlying asset that can be sold to the GSM + */ + function getExposureCap() external view returns (uint128); + + /** + * @notice Returns the Fee Strategy for the GSM + * @dev It returns 0x0 in case of no fee strategy + * @return The address of the FeeStrategy + */ + function getFeeStrategy() external view returns (address); + + /** + * @dev Grants `role` to `account`. + * + * If `account` had not been already granted `role`, emits a {RoleGranted} + * event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function grantRole(bytes32 role, address account) external; + + /** + * @dev Returns `true` if `account` has been granted `role`. + */ + function hasRole(bytes32 role, address account) external view returns (bool); + + /** + * @dev Revokes `role` from `account`. + * + * If `account` had been granted `role`, emits a {RoleRevoked} event. + * + * Requirements: + * + * - the caller must have ``role``'s admin role. + */ + function revokeRole(bytes32 role, address account) external; +} diff --git a/src/interfaces/IGsmFeeStrategy.sol b/src/interfaces/IGsmFeeStrategy.sol new file mode 100644 index 000000000..2c2ac05cc --- /dev/null +++ b/src/interfaces/IGsmFeeStrategy.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title IGsmFeeStrategy + * @author Aave + * @notice Defines the behaviour of Fee Strategies + * @dev Functions' logic must be invertible, being possible to calculate the fee amount based on the gross amount, and + * the other way round. + * @dev All math operations must round up, favoring the protocol. + */ +interface IGsmFeeStrategy { + /** + * @notice Returns the fee to be applied when buying an underlying asset in exchange for GHO + * @param grossAmount The amount of GHO being sold for the underlying asset + * @return The fee amount of GHO + */ + function getBuyFee(uint256 grossAmount) external view returns (uint256); + + /** + * @notice Returns the fee to be applied when buying GHO in exchange for an underlying asset + * @param grossAmount The amount of underlying, converted to GHO, being sold + * @return The fee amount of GHO + */ + function getSellFee(uint256 grossAmount) external view returns (uint256); + + /** + * @notice Returns the gross amount of GHO being bought based on the total bought amount + * @param totalAmount The total amount of GHO being bought (gross amount, GHO bought plus fee) + * @return The gross amount of GHO being bought (total amount minus fee) + */ + function getGrossAmountFromTotalBought(uint256 totalAmount) external view returns (uint256); + + /** + * @notice Returns the amount of GHO being sold based on the total sold amount + * @param totalAmount The total amount of GHO being sold (gross amount, GHO sold minus fee) + * @return The gross amount of GHO being sold (total amount plus fee) + */ + function getGrossAmountFromTotalSold(uint256 totalAmount) external view returns (uint256); +} diff --git a/src/interfaces/IGsmSteward.sol b/src/interfaces/IGsmSteward.sol new file mode 100644 index 000000000..f5cedc75f --- /dev/null +++ b/src/interfaces/IGsmSteward.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.10; + +/** + * @title IGsmSteward + * @author Aave Labs + * @notice Defines the basic interface of the GhoGsmSteward + */ +interface IGsmSteward { + struct GsmDebounce { + uint40 gsmExposureCapLastUpdated; + uint40 gsmFeeStrategyLastUpdated; + } + + /** + * @notice Updates the exposure cap of the GSM, only if: + * - respects `MINIMUM_DELAY`, the minimum time delay between updates + * - the update changes up to 100% upwards or downwards + * @dev Only callable by Risk Council + * @param gsm The gsm address to update + * @param newExposureCap The new exposure cap (in underlying asset terms) + */ + function updateGsmExposureCap(address gsm, uint128 newExposureCap) external; + + /** + * @notice Updates the fixed percent fees of the GSM, only if: + * - respects `MINIMUM_DELAY`, the minimum time delay between updates + * - the update changes up to `GSM_FEE_RATE_CHANGE_MAX` upwards or downwards (for both buy and sell individually) + * @dev Only callable by Risk Council + * @dev Reverts if fee strategy is not set, or zero fees. Must be updated via AIP in this case + * @param gsm The gsm address to update + * @param buyFee The new buy fee (expressed in bps) (e.g. 0.0150e4 results in 1.50%) + * @param sellFee The new sell fee (expressed in bps) (e.g. 0.0150e4 results in 1.50%) + */ + function updateGsmBuySellFees(address gsm, uint256 buyFee, uint256 sellFee) external; + + /** + * @notice Returns timestamp of the last update of Gsm parameters + * @param gsm The GSM address + * @return The GsmDebounce struct describing the last update of GSM parameters + */ + function getGsmTimelocks(address gsm) external view returns (GsmDebounce memory); + + /** + * @notice Returns the maximum increase for GSM fee rates (buy or sell). + * @return The maximum increase change for GSM fee rates updates in bps (e.g. 0.010e4 results in 1.00%) + */ + function GSM_FEE_RATE_CHANGE_MAX() external view returns (uint256); + + /** + * @notice Returns the minimum delay that must be respected between parameters update. + * @return The minimum delay between parameter updates (in seconds) + */ + function MINIMUM_DELAY() external view returns (uint256); + + /** + * @notice Returns the address of the GSM Fee Strategy Factory + * @return The address of the GSM Fee Strategy Factory + */ + function FIXED_FEE_STRATEGY_FACTORY() external view returns (address); + + /** + * @notice Returns the address of the risk council + * @return The address of the RiskCouncil + */ + function RISK_COUNCIL() external view returns (address); +} diff --git a/src/interfaces/ccip/IUpgradeableLockReleaseTokenPool.sol b/src/interfaces/ccip/IUpgradeableLockReleaseTokenPool.sol index 0bf5fc315..02252f92c 100644 --- a/src/interfaces/ccip/IUpgradeableLockReleaseTokenPool.sol +++ b/src/interfaces/ccip/IUpgradeableLockReleaseTokenPool.sol @@ -7,15 +7,34 @@ import {IClient} from 'src/interfaces/ccip/IClient.sol'; interface IUpgradeableLockReleaseTokenPool { error BridgeLimitExceeded(uint256 bridgeLimit); - /// @notice Gets the bridge limit - /// @return The maximum amount of tokens that can be transferred out to other chains - function getBridgeLimit() external view returns (uint256); + /// @dev Initializer + /// @dev The address passed as `owner` must accept ownership after initialization. + /// @dev The `allowlist` is only effective if pool is set to access-controlled mode + /// @param owner The address of the owner + /// @param allowlist A set of addresses allowed to trigger lockOrBurn as original senders + /// @param router The address of the router + function initialize(address owner, address[] memory allowlist, address router) external; + + /// @dev Ownable + function owner() external view returns (address); /// @notice Sets the bridge limit /// @param limit The new limit function setBridgeLimit(uint256 limit) external; - /// @notice Gets the current bridged amount to other chains - /// @return The amount of tokens transferred out to other chains - function getCurrentBridgedAmount() external view returns (uint256); + /// @notice Sets the bridge limit admin address. + /// @dev Only callable by the owner. + /// @param bridgeLimitAdmin The new bridge limit admin address. + function setBridgeLimitAdmin(address bridgeLimitAdmin) external; + + /// @notice Sets the rate limiter admin address. + /// @dev Only callable by the owner. + /// @param rateLimitAdmin The new rate limiter admin address. + function setRateLimitAdmin(address rateLimitAdmin) external; + + /// @notice Gets the bridge limiter admin address. + function getBridgeLimitAdmin() external view returns (address); + + /// @notice Gets the rate limiter admin address. + function getRateLimitAdmin() external view returns (address); } From 0d2f05320941ce2993b1bfa14d042bff6e80f48c Mon Sep 17 00:00:00 2001 From: Cache bot Date: Thu, 31 Oct 2024 13:15:32 +0000 Subject: [PATCH 13/15] fix(cache): automated cache update [skip ci] --- src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md b/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md index f11d830c6..e5a41161a 100644 --- a/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md +++ b/src/20241007_Multi_GHOStewardV2Upgrade/GHOStewardV2Upgrade.md @@ -49,8 +49,8 @@ List of new addresses: ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/603758edd1814e074b1c7a08c7da140df4338351/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/603758edd1814e074b1c7a08c7da140df4338351/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/603758edd1814e074b1c7a08c7da140df4338351/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Ethereum_GHOStewardV2Upgrade_20241007.t.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/603758edd1814e074b1c7a08c7da140df4338351/src/20241007_Multi_GHOStewardV2Upgrade/AaveV3Arbitrum_GHOStewardV2Upgrade_20241007.t.sol) - [Snapshot](https://snapshot.org/#/aave.eth/proposal/0xc5e7df1536ef9fc71a7d2e2f6fee6e4e20e37a50b4e0f1646616d066b8697da5) - [Discussion](https://governance.aave.com/t/arfc-gho-steward-v2-upgrade/19116) - The GHO Stewards implementations can be found [here](https://github.com/aave/gho-core/tree/main/src/contracts/misc) From 6133c63c69341c4462fc0fc00874096752a01fcb Mon Sep 17 00:00:00 2001 From: Harsh Pandey Date: Fri, 1 Nov 2024 16:14:22 +0530 Subject: [PATCH 14/15] feat: activate generalized risk stewards (#413) * feat: generalized risk stewards * fix: build * chore: update libs * feat: dont revoke from caps stewards and add zksync payload * chore: remove zksync * feat: set gho as restricted * fix: test * feat: add etherfi * chore: update ether fi test block * chore: remove gho as restricted * chore: update libs * feat: add deployed contracts * fix: address * feat: add zksync steward * chore: update deploy script * chore: update writeup * Update src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md Co-authored-by: sendra * chore: update libs * fix: update to new stewards address * Update RiskStewardPhase2.md Co-authored-by: sendra --------- Co-authored-by: sendra --- ...bitrum_RiskStewardPhase2_20240805_after.md | 5 + ...lanche_RiskStewardPhase2_20240805_after.md | 5 + ...eV3BNB_RiskStewardPhase2_20240805_after.md | 5 + ...V3Base_RiskStewardPhase2_20240805_after.md | 5 + ...therFi_RiskStewardPhase2_20240805_after.md | 5 + ...umLido_RiskStewardPhase2_20240805_after.md | 5 + ...hereum_RiskStewardPhase2_20240805_after.md | 5 + ...Gnosis_RiskStewardPhase2_20240805_after.md | 5 + ...3Metis_RiskStewardPhase2_20240805_after.md | 5 + ...timism_RiskStewardPhase2_20240805_after.md | 5 + ...olygon_RiskStewardPhase2_20240805_after.md | 5 + ...Scroll_RiskStewardPhase2_20240805_after.md | 5 + ...ZkSync_RiskStewardPhase2_20240805_after.md | 5 + lib/aave-helpers | 2 +- ...eV3Arbitrum_RiskStewardPhase2_20240805.sol | 22 ++ ...3Arbitrum_RiskStewardPhase2_20240805.t.sol | 47 +++ ...V3Avalanche_RiskStewardPhase2_20240805.sol | 17 + ...Avalanche_RiskStewardPhase2_20240805.t.sol | 36 ++ .../AaveV3BNB_RiskStewardPhase2_20240805.sol | 17 + ...AaveV3BNB_RiskStewardPhase2_20240805.t.sol | 32 ++ .../AaveV3Base_RiskStewardPhase2_20240805.sol | 17 + ...aveV3Base_RiskStewardPhase2_20240805.t.sol | 32 ++ ...reumEtherFi_RiskStewardPhase2_20240805.sol | 17 + ...umEtherFi_RiskStewardPhase2_20240805.t.sol | 39 ++ ...thereumLido_RiskStewardPhase2_20240805.sol | 17 + ...ereumLido_RiskStewardPhase2_20240805.t.sol | 36 ++ ...eV3Ethereum_RiskStewardPhase2_20240805.sol | 22 ++ ...3Ethereum_RiskStewardPhase2_20240805.t.sol | 47 +++ ...aveV3Gnosis_RiskStewardPhase2_20240805.sol | 17 + ...eV3Gnosis_RiskStewardPhase2_20240805.t.sol | 32 ++ ...AaveV3Metis_RiskStewardPhase2_20240805.sol | 17 + ...veV3Metis_RiskStewardPhase2_20240805.t.sol | 32 ++ ...eV3Optimism_RiskStewardPhase2_20240805.sol | 17 + ...3Optimism_RiskStewardPhase2_20240805.t.sol | 36 ++ ...veV3Polygon_RiskStewardPhase2_20240805.sol | 17 + ...V3Polygon_RiskStewardPhase2_20240805.t.sol | 32 ++ ...aveV3Scroll_RiskStewardPhase2_20240805.sol | 17 + ...eV3Scroll_RiskStewardPhase2_20240805.t.sol | 32 ++ .../RiskStewardPhase2.md | 81 ++++ .../RiskStewardPhase2_20240805.s.sol | 351 ++++++++++++++++++ .../config.ts | 38 ++ .../interfaces/IRiskSteward.sol | 18 + ...aveV3ZkSync_RiskStewardPhase2_20240805.sol | 17 + ...eV3ZkSync_RiskStewardPhase2_20240805.t.sol | 39 ++ .../RiskStewardPhase2_20240805.s.sol | 37 ++ 45 files changed, 1294 insertions(+), 1 deletion(-) create mode 100644 diffs/AaveV3Arbitrum_RiskStewardPhase2_20240805_before_AaveV3Arbitrum_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Avalanche_RiskStewardPhase2_20240805_before_AaveV3Avalanche_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3BNB_RiskStewardPhase2_20240805_before_AaveV3BNB_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Base_RiskStewardPhase2_20240805_before_AaveV3Base_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_before_AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3EthereumLido_RiskStewardPhase2_20240805_before_AaveV3EthereumLido_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Ethereum_RiskStewardPhase2_20240805_before_AaveV3Ethereum_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Gnosis_RiskStewardPhase2_20240805_before_AaveV3Gnosis_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Metis_RiskStewardPhase2_20240805_before_AaveV3Metis_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Optimism_RiskStewardPhase2_20240805_before_AaveV3Optimism_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Polygon_RiskStewardPhase2_20240805_before_AaveV3Polygon_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3Scroll_RiskStewardPhase2_20240805_before_AaveV3Scroll_RiskStewardPhase2_20240805_after.md create mode 100644 diffs/AaveV3ZkSync_RiskStewardPhase2_20240805_before_AaveV3ZkSync_RiskStewardPhase2_20240805_after.md create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md create mode 100644 src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol create mode 100644 src/20240805_Multi_RiskStewardPhase2/config.ts create mode 100644 src/20240805_Multi_RiskStewardPhase2/interfaces/IRiskSteward.sol create mode 100644 zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.sol create mode 100644 zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol create mode 100644 zksync/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol diff --git a/diffs/AaveV3Arbitrum_RiskStewardPhase2_20240805_before_AaveV3Arbitrum_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Arbitrum_RiskStewardPhase2_20240805_before_AaveV3Arbitrum_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Arbitrum_RiskStewardPhase2_20240805_before_AaveV3Arbitrum_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Avalanche_RiskStewardPhase2_20240805_before_AaveV3Avalanche_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Avalanche_RiskStewardPhase2_20240805_before_AaveV3Avalanche_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Avalanche_RiskStewardPhase2_20240805_before_AaveV3Avalanche_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3BNB_RiskStewardPhase2_20240805_before_AaveV3BNB_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3BNB_RiskStewardPhase2_20240805_before_AaveV3BNB_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3BNB_RiskStewardPhase2_20240805_before_AaveV3BNB_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Base_RiskStewardPhase2_20240805_before_AaveV3Base_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Base_RiskStewardPhase2_20240805_before_AaveV3Base_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Base_RiskStewardPhase2_20240805_before_AaveV3Base_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_before_AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_before_AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_before_AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3EthereumLido_RiskStewardPhase2_20240805_before_AaveV3EthereumLido_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3EthereumLido_RiskStewardPhase2_20240805_before_AaveV3EthereumLido_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3EthereumLido_RiskStewardPhase2_20240805_before_AaveV3EthereumLido_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Ethereum_RiskStewardPhase2_20240805_before_AaveV3Ethereum_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Ethereum_RiskStewardPhase2_20240805_before_AaveV3Ethereum_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Ethereum_RiskStewardPhase2_20240805_before_AaveV3Ethereum_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Gnosis_RiskStewardPhase2_20240805_before_AaveV3Gnosis_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Gnosis_RiskStewardPhase2_20240805_before_AaveV3Gnosis_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Gnosis_RiskStewardPhase2_20240805_before_AaveV3Gnosis_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Metis_RiskStewardPhase2_20240805_before_AaveV3Metis_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Metis_RiskStewardPhase2_20240805_before_AaveV3Metis_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Metis_RiskStewardPhase2_20240805_before_AaveV3Metis_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Optimism_RiskStewardPhase2_20240805_before_AaveV3Optimism_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Optimism_RiskStewardPhase2_20240805_before_AaveV3Optimism_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Optimism_RiskStewardPhase2_20240805_before_AaveV3Optimism_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Polygon_RiskStewardPhase2_20240805_before_AaveV3Polygon_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Polygon_RiskStewardPhase2_20240805_before_AaveV3Polygon_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Polygon_RiskStewardPhase2_20240805_before_AaveV3Polygon_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Scroll_RiskStewardPhase2_20240805_before_AaveV3Scroll_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3Scroll_RiskStewardPhase2_20240805_before_AaveV3Scroll_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Scroll_RiskStewardPhase2_20240805_before_AaveV3Scroll_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3ZkSync_RiskStewardPhase2_20240805_before_AaveV3ZkSync_RiskStewardPhase2_20240805_after.md b/diffs/AaveV3ZkSync_RiskStewardPhase2_20240805_before_AaveV3ZkSync_RiskStewardPhase2_20240805_after.md new file mode 100644 index 000000000..722a7620e --- /dev/null +++ b/diffs/AaveV3ZkSync_RiskStewardPhase2_20240805_before_AaveV3ZkSync_RiskStewardPhase2_20240805_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` diff --git a/lib/aave-helpers b/lib/aave-helpers index d378deda2..19d8ece8a 160000 --- a/lib/aave-helpers +++ b/lib/aave-helpers @@ -1 +1 @@ -Subproject commit d378deda2e0477900a84bffb1c2f45d199dc3138 +Subproject commit 19d8ece8a12d3d789ccb96fd184fef0a646b201c diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..028526f90 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {IRiskSteward} from './interfaces/IRiskSteward.sol'; +import {AaveV3Arbitrum, AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Arbitrum_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Arbitrum.ACL_MANAGER.addRiskAdmin(AaveV3Arbitrum.RISK_STEWARD); + IRiskSteward(AaveV3Arbitrum.RISK_STEWARD).setAddressRestricted( + AaveV3ArbitrumAssets.GHO_UNDERLYING, + true + ); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..a5dd11c92 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Arbitrum, AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {IRiskSteward} from './interfaces/IRiskSteward.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Arbitrum_RiskStewardPhase2_20240805} from './AaveV3Arbitrum_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Arbitrum_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=arbitrum forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Arbitrum_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Arbitrum_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('arbitrum'), 269547589); + proposal = new AaveV3Arbitrum_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Arbitrum_RiskStewardPhase2_20240805', + AaveV3Arbitrum.POOL, + address(proposal) + ); + } + + function test_permissions() public { + assertFalse( + IRiskSteward(AaveV3Arbitrum.RISK_STEWARD).isAddressRestricted( + AaveV3ArbitrumAssets.GHO_UNDERLYING + ) + ); + executePayload(vm, address(proposal)); + + assertEq(AaveV3Arbitrum.ACL_MANAGER.isRiskAdmin(AaveV3Arbitrum.RISK_STEWARD), true); + assertTrue( + IRiskSteward(AaveV3Arbitrum.RISK_STEWARD).isAddressRestricted( + AaveV3ArbitrumAssets.GHO_UNDERLYING + ) + ); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..d62dfb092 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Avalanche_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Avalanche.ACL_MANAGER.addRiskAdmin(AaveV3Avalanche.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..73fb83851 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Avalanche} from 'aave-address-book/AaveV3Avalanche.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Avalanche_RiskStewardPhase2_20240805} from './AaveV3Avalanche_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Avalanche_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=avalanche forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Avalanche_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Avalanche_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('avalanche'), 52476941); + proposal = new AaveV3Avalanche_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Avalanche_RiskStewardPhase2_20240805', + AaveV3Avalanche.POOL, + address(proposal) + ); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3Avalanche.ACL_MANAGER.isRiskAdmin(AaveV3Avalanche.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..f71de6a1e --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3BNB} from 'aave-address-book/AaveV3BNB.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3BNB_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3BNB.ACL_MANAGER.addRiskAdmin(AaveV3BNB.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..b02710367 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3BNB} from 'aave-address-book/AaveV3BNB.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3BNB_RiskStewardPhase2_20240805} from './AaveV3BNB_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3BNB_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=bnb forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3BNB_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3BNB_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('bnb'), 43597598); + proposal = new AaveV3BNB_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest('AaveV3BNB_RiskStewardPhase2_20240805', AaveV3BNB.POOL, address(proposal)); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3BNB.ACL_MANAGER.isRiskAdmin(AaveV3BNB.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..ff43bc191 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Base_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Base.ACL_MANAGER.addRiskAdmin(AaveV3Base.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..f531905c8 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Base_RiskStewardPhase2_20240805} from './AaveV3Base_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Base_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=base forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Base_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Base_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('base'), 21791615); + proposal = new AaveV3Base_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest('AaveV3Base_RiskStewardPhase2_20240805', AaveV3Base.POOL, address(proposal)); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3Base.ACL_MANAGER.isRiskAdmin(AaveV3Base.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..7564d0af8 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3EthereumEtherFi} from 'aave-address-book/AaveV3EthereumEtherFi.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3EthereumEtherFi_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3EthereumEtherFi.ACL_MANAGER.addRiskAdmin(AaveV3EthereumEtherFi.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..31248f52e --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumEtherFi} from 'aave-address-book/AaveV3EthereumEtherFi.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3EthereumEtherFi_RiskStewardPhase2_20240805} from './AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3EthereumEtherFi_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3EthereumEtherFi_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3EthereumEtherFi_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21085127); + proposal = new AaveV3EthereumEtherFi_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3EthereumEtherFi_RiskStewardPhase2_20240805', + AaveV3EthereumEtherFi.POOL, + address(proposal) + ); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq( + AaveV3EthereumEtherFi.ACL_MANAGER.isRiskAdmin(AaveV3EthereumEtherFi.RISK_STEWARD), + true + ); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..ff92721ee --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3EthereumLido} from 'aave-address-book/AaveV3EthereumLido.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3EthereumLido_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3EthereumLido.ACL_MANAGER.addRiskAdmin(AaveV3EthereumLido.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..7ec8ed28c --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumLido} from 'aave-address-book/AaveV3EthereumLido.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3EthereumLido_RiskStewardPhase2_20240805} from './AaveV3EthereumLido_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3EthereumLido_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3EthereumLido_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3EthereumLido_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21085127); + proposal = new AaveV3EthereumLido_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3EthereumLido_RiskStewardPhase2_20240805', + AaveV3EthereumLido.POOL, + address(proposal) + ); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3EthereumLido.ACL_MANAGER.isRiskAdmin(AaveV3EthereumLido.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..a6c939dcc --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {IRiskSteward} from './interfaces/IRiskSteward.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Ethereum_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Ethereum.ACL_MANAGER.addRiskAdmin(AaveV3Ethereum.RISK_STEWARD); + IRiskSteward(AaveV3Ethereum.RISK_STEWARD).setAddressRestricted( + AaveV3EthereumAssets.GHO_UNDERLYING, + true + ); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..3e17eca53 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Ethereum_RiskStewardPhase2_20240805} from './AaveV3Ethereum_RiskStewardPhase2_20240805.sol'; +import {IRiskSteward} from './interfaces/IRiskSteward.sol'; + +/** + * @dev Test for AaveV3Ethereum_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Ethereum_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Ethereum_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21085127); + proposal = new AaveV3Ethereum_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_RiskStewardPhase2_20240805', + AaveV3Ethereum.POOL, + address(proposal) + ); + } + + function test_permissions() public { + assertFalse( + IRiskSteward(AaveV3Ethereum.RISK_STEWARD).isAddressRestricted( + AaveV3EthereumAssets.GHO_UNDERLYING + ) + ); + executePayload(vm, address(proposal)); + + assertEq(AaveV3Ethereum.ACL_MANAGER.isRiskAdmin(AaveV3Ethereum.RISK_STEWARD), true); + assertTrue( + IRiskSteward(AaveV3Ethereum.RISK_STEWARD).isAddressRestricted( + AaveV3EthereumAssets.GHO_UNDERLYING + ) + ); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..bbbbb6e92 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Gnosis} from 'aave-address-book/AaveV3Gnosis.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Gnosis_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Gnosis.ACL_MANAGER.addRiskAdmin(AaveV3Gnosis.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..393e030be --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Gnosis} from 'aave-address-book/AaveV3Gnosis.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Gnosis_RiskStewardPhase2_20240805} from './AaveV3Gnosis_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Gnosis_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=gnosis forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Gnosis_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Gnosis_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('gnosis'), 36783528); + proposal = new AaveV3Gnosis_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest('AaveV3Gnosis_RiskStewardPhase2_20240805', AaveV3Gnosis.POOL, address(proposal)); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3Gnosis.ACL_MANAGER.isRiskAdmin(AaveV3Gnosis.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..b27bb2568 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Metis} from 'aave-address-book/AaveV3Metis.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Metis_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Metis.ACL_MANAGER.addRiskAdmin(AaveV3Metis.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..fe1f617df --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Metis} from 'aave-address-book/AaveV3Metis.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Metis_RiskStewardPhase2_20240805} from './AaveV3Metis_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Metis_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=metis forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Metis_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Metis_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('metis'), 18845256); + proposal = new AaveV3Metis_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest('AaveV3Metis_RiskStewardPhase2_20240805', AaveV3Metis.POOL, address(proposal)); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3Metis.ACL_MANAGER.isRiskAdmin(AaveV3Metis.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..c026210d6 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Optimism_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Optimism.ACL_MANAGER.addRiskAdmin(AaveV3Optimism.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..ffcff7230 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Optimism} from 'aave-address-book/AaveV3Optimism.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Optimism_RiskStewardPhase2_20240805} from './AaveV3Optimism_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Optimism_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=optimism forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Optimism_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Optimism_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('optimism'), 127387217); + proposal = new AaveV3Optimism_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Optimism_RiskStewardPhase2_20240805', + AaveV3Optimism.POOL, + address(proposal) + ); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3Optimism.ACL_MANAGER.isRiskAdmin(AaveV3Optimism.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..7ea99e15f --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Polygon_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Polygon.ACL_MANAGER.addRiskAdmin(AaveV3Polygon.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..d9bd6e801 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Polygon} from 'aave-address-book/AaveV3Polygon.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Polygon_RiskStewardPhase2_20240805} from './AaveV3Polygon_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Polygon_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=polygon forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Polygon_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Polygon_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('polygon'), 63655296); + proposal = new AaveV3Polygon_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest('AaveV3Polygon_RiskStewardPhase2_20240805', AaveV3Polygon.POOL, address(proposal)); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3Polygon.ACL_MANAGER.isRiskAdmin(AaveV3Polygon.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..65095e5b6 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3Scroll} from 'aave-address-book/AaveV3Scroll.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3Scroll_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3Scroll.ACL_MANAGER.addRiskAdmin(AaveV3Scroll.RISK_STEWARD); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol b/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..73fd20f0c --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Scroll} from 'aave-address-book/AaveV3Scroll.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Scroll_RiskStewardPhase2_20240805} from './AaveV3Scroll_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3Scroll_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=scroll forge test --match-path=src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3Scroll_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3Scroll_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('scroll'), 10688886); + proposal = new AaveV3Scroll_RiskStewardPhase2_20240805(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest('AaveV3Scroll_RiskStewardPhase2_20240805', AaveV3Scroll.POOL, address(proposal)); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3Scroll.ACL_MANAGER.isRiskAdmin(AaveV3Scroll.RISK_STEWARD), true); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md b/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md new file mode 100644 index 000000000..bf11b8390 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md @@ -0,0 +1,81 @@ +--- +title: "Risk Steward Phase 2" +author: "BGD Labs (@bgdlabs)" +discussions: "https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204" +snapshot: "https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16" +--- + +## Simple Summary + +Expanding from the scope of [CapsPlusRiskSteward](https://governance.aave.com/t/bgd-risk-steward-phase-1-capsplusrisksteward/12602), we now introduce the new generalized RiskSteward, allowing hardly constrained risk parameter updates by risk service providers and reducing governance overhead. + +## Motivation + +Aave's decentralized governance system, while robust, creates significant operational overhead and cost due to its comprehensive proposal process. This process includes pre-AIP procedures, payload creation, multiple reviews, voting, and on-chain verification. +While recent improvements have optimized some steps, further efficiency gains are possible, particularly in risk-related proposals. + +Rationale for reducing risk-related proposals: + +- Two expert, independent risk teams (ChaosLabs, LlamaRisk) are engaged with the DAO. +- Historical voting unanimity on non-controversial, incremental risk proposals. +- Technical feasibility of constraining updates using Aave v3 roles and specific validations. + +The success of the CapsPlusRiskStewards experiment demonstrates the viability of expanding this approach. As Aave expands to new networks, minimizing governance proposals becomes crucial to prevent voter fatigue. + +## Specification + +The new RiskSteward we propose follows the same design as the CapsPlusRiskSteward, an smart contract to which the Aave Governance gives `RISK_ADMIN` role over all v3 instances, controlled by a 2-of-2 multisig, and heavily constrained on what can do and how by its own logic. + +The 2-of-2 multisig controlling the RiskSteward will have Chaos and BGD Labs as signers, with the inclusion of BGD Labs as a matter of extra security procedure. + +`ACL_MANAGER.addRiskAdmin()` is called to add a the new risk steward. + +Additionally, params for GHO on Arbitrum and Ethereum cannot be changed by this RiskStewards, as the params will be updated by the GhoStewards instead, this is done by setting the GHO asset as restricted via the following method: `RISK_STEWARD.setAddressRestricted();` + +To give some extra buffer and protection, we will remove the `RISK_ADMIN` role from the old `CapsPlusRiskSteward` once risk providers have adapted to use the new Risk Stewards. + +Deployed Risk Steward Contracts: + +| Network Instance | Risk Steward Contract | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| Ethereum | [0x7C7143f4bE189928A6a98D8686c5e84c893c59c7](https://etherscan.io/address/0x7C7143f4bE189928A6a98D8686c5e84c893c59c7) | +| Ethereum Lido | [0x30adC2f98ff78fDde12F191Acb82699f640694FB](https://etherscan.io/address/0x30adC2f98ff78fDde12F191Acb82699f640694FB) | +| Ethereum EtherFi | [0xBF79d8339303148E345277a994Eb2cD5d82F0067](https://etherscan.io/address/0xBF79d8339303148E345277a994Eb2cD5d82F0067) | +| Arbitrum | [0x14568979093FFF97aeBD73F58051a4f0e297b1eB](https://arbiscan.io/address/0x14568979093FFF97aeBD73F58051a4f0e297b1eB) | +| Optimism | [0x928807b90a74210268b590d0159fcf1340ad76bd](https://optimistic.etherscan.io/address/0x928807b90a74210268b590d0159fcf1340ad76bd) | +| Polygon | [0x88dECc4bf07f5219ea7D1E32fe3762DA28ff7609](https://polygonscan.com/address/0x88dECc4bf07f5219ea7D1E32fe3762DA28ff7609) | +| Avalanche | [0xbf03aB677DEdA36E19D294d1735b93Dd9d1E0c05](https://snowscan.xyz/address/0xbf03aB677DEdA36E19D294d1735b93Dd9d1E0c05) | +| Scroll | [0x64093fe5f8Cf62aFb4377cf7EF4373537fe9155B](https://scrollscan.com/address/0x64093fe5f8Cf62aFb4377cf7EF4373537fe9155B) | +| Metis | [0x58226D26658F19724cB881E9F747EeDC846BB1c9](https://explorer.metis.io/address/0x58226D26658F19724cB881E9F747EeDC846BB1c9) | +| Base | [0xD3DE4b3571744EB77946d65aBF01408902E92c4E](https://basescan.org/address/0xD3DE4b3571744EB77946d65aBF01408902E92c4E) | +| BNB | [0xbe7998712402b6a63975515a532ce503437998b7](https://bscscan.com/address/0xbe7998712402b6a63975515a532ce503437998b7) | +| Gnosis | [0x677c9f358dA5DC83aF6760a839E4448D72840d04](https://gnosisscan.io/address/0x677c9f358dA5DC83aF6760a839E4448D72840d04) | +| ZkSync | [0x05c77Cf62346329a157d7A6F874464D049CECb26](https://era.zksync.network/address/0x05c77Cf62346329a157d7A6F874464D049CECb26) | + +The new risk stewards have been configured with the following risk params on all Aave instances: + +| Parameter | Percent change allowed | minimumDelay | +| ------------------------- | ----------------------- | ------------ | +| Supply and Borrow Caps | 100% (relative change) | 3 days | +| Debt Ceiling | 20% (relative change) | 3 days | +| LST Cap adapter params | 5% (relative change) | 3 days | +| Stable price cap | 0.5% (relative change) | 3 days | +| Base Variable Borrow Rate | 0.5% (absolute change) | 3 days | +| Slope 1 | 0.5% (absolute change) | 3 days | +| Slope 2 | 5% (absolute change) | 3 days | +| Optimal Point (Kink) | 3% (absolute change) | 3 days | +| Loan to Value (LTV) | 0.25% (absolute change) | 3 days | +| Liquidation Threshold | 0.25% (absolute change) | 3 days | +| Liquidation Bonus | 0.5% (absolute change) | 3 days | + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.sol), [AaveV3EthereumEtherFi](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol), [AaveV3Polygon](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.sol), [AaveV3Avalanche](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.sol), [AaveV3Optimism](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.sol), [AaveV3Metis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.sol), [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.sol), [AaveV3Scroll](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.sol), [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.sol), [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol), [AaveV3EthereumEtherFi](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol), [AaveV3Polygon](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol), [AaveV3Avalanche](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol), [AaveV3Optimism](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol), [AaveV3Metis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol), [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol), [AaveV3Scroll](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol), [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol), [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol) +- [Discussion](https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204) +- [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16) +- Github Repo: [Aave V3 Risk Stewards](https://github.com/aave-dao/aave-v3-risk-stewards) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol b/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol new file mode 100644 index 000000000..dd8445b9c --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol @@ -0,0 +1,351 @@ +// 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 {GovernanceV3ZkSync} from 'aave-address-book/GovernanceV3ZkSync.sol'; +import {EthereumScript, PolygonScript, AvalancheScript, OptimismScript, ArbitrumScript, MetisScript, BaseScript, GnosisScript, ScrollScript, BNBScript, ChainIds} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; +import {AaveV3Ethereum_RiskStewardPhase2_20240805} from './AaveV3Ethereum_RiskStewardPhase2_20240805.sol'; +import {AaveV3EthereumLido_RiskStewardPhase2_20240805} from './AaveV3EthereumLido_RiskStewardPhase2_20240805.sol'; +import {AaveV3EthereumEtherFi_RiskStewardPhase2_20240805} from './AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol'; +import {AaveV3Polygon_RiskStewardPhase2_20240805} from './AaveV3Polygon_RiskStewardPhase2_20240805.sol'; +import {AaveV3Avalanche_RiskStewardPhase2_20240805} from './AaveV3Avalanche_RiskStewardPhase2_20240805.sol'; +import {AaveV3Optimism_RiskStewardPhase2_20240805} from './AaveV3Optimism_RiskStewardPhase2_20240805.sol'; +import {AaveV3Arbitrum_RiskStewardPhase2_20240805} from './AaveV3Arbitrum_RiskStewardPhase2_20240805.sol'; +import {AaveV3Metis_RiskStewardPhase2_20240805} from './AaveV3Metis_RiskStewardPhase2_20240805.sol'; +import {AaveV3Base_RiskStewardPhase2_20240805} from './AaveV3Base_RiskStewardPhase2_20240805.sol'; +import {AaveV3Gnosis_RiskStewardPhase2_20240805} from './AaveV3Gnosis_RiskStewardPhase2_20240805.sol'; +import {AaveV3Scroll_RiskStewardPhase2_20240805} from './AaveV3Scroll_RiskStewardPhase2_20240805.sol'; +import {AaveV3BNB_RiskStewardPhase2_20240805} from './AaveV3BNB_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_RiskStewardPhase2_20240805).creationCode + ); + address payload1 = GovV3Helpers.deployDeterministic( + type(AaveV3EthereumLido_RiskStewardPhase2_20240805).creationCode + ); + address payload2 = GovV3Helpers.deployDeterministic( + type(AaveV3EthereumEtherFi_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](3); + actions[0] = GovV3Helpers.buildAction(payload0); + actions[1] = GovV3Helpers.buildAction(payload1); + actions[2] = GovV3Helpers.buildAction(payload2); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Polygon + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployPolygon chain=polygon + * verify-command: FOUNDRY_PROFILE=polygon npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/137/run-latest.json + */ +contract DeployPolygon is PolygonScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Polygon_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Avalanche + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployAvalanche chain=avalanche + * verify-command: FOUNDRY_PROFILE=avalanche npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/43114/run-latest.json + */ +contract DeployAvalanche is AvalancheScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Avalanche_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Optimism + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployOptimism chain=optimism + * verify-command: FOUNDRY_PROFILE=optimism npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/10/run-latest.json + */ +contract DeployOptimism is OptimismScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Optimism_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Arbitrum + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployArbitrum chain=arbitrum + * verify-command: FOUNDRY_PROFILE=arbitrum npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/42161/run-latest.json + */ +contract DeployArbitrum is ArbitrumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Arbitrum_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Metis + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployMetis chain=metis + * verify-command: FOUNDRY_PROFILE=metis npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/1088/run-latest.json + */ +contract DeployMetis is MetisScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Metis_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Base + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployBase chain=base + * verify-command: FOUNDRY_PROFILE=base npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/8453/run-latest.json + */ +contract DeployBase is BaseScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Base_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Gnosis + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployGnosis chain=gnosis + * verify-command: FOUNDRY_PROFILE=gnosis npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/100/run-latest.json + */ +contract DeployGnosis is GnosisScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Gnosis_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy Scroll + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployScroll chain=scroll + * verify-command: FOUNDRY_PROFILE=scroll npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/534352/run-latest.json + */ +contract DeployScroll is ScrollScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Scroll_RiskStewardPhase2_20240805).creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Deploy BNB + * deploy-command: make deploy-ledger contract=src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployBNB chain=bnb + * verify-command: FOUNDRY_PROFILE=bnb npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/56/run-latest.json + */ +contract DeployBNB is BNBScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3BNB_RiskStewardPhase2_20240805).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/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:CreateProposal chain=mainnet + */ +contract CreateProposal is EthereumScript { + function run() external { + // create payloads + PayloadsControllerUtils.Payload[] memory payloads = new PayloadsControllerUtils.Payload[](11); + + // compose actions for validation + IPayloadsControllerCore.ExecutionAction[] + memory actionsEthereum = new IPayloadsControllerCore.ExecutionAction[](3); + actionsEthereum[0] = GovV3Helpers.buildAction( + type(AaveV3Ethereum_RiskStewardPhase2_20240805).creationCode + ); + actionsEthereum[1] = GovV3Helpers.buildAction( + type(AaveV3EthereumLido_RiskStewardPhase2_20240805).creationCode + ); + actionsEthereum[2] = GovV3Helpers.buildAction( + type(AaveV3EthereumEtherFi_RiskStewardPhase2_20240805).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsPolygon = new IPayloadsControllerCore.ExecutionAction[](1); + actionsPolygon[0] = GovV3Helpers.buildAction( + type(AaveV3Polygon_RiskStewardPhase2_20240805).creationCode + ); + payloads[1] = GovV3Helpers.buildPolygonPayload(vm, actionsPolygon); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsAvalanche = new IPayloadsControllerCore.ExecutionAction[](1); + actionsAvalanche[0] = GovV3Helpers.buildAction( + type(AaveV3Avalanche_RiskStewardPhase2_20240805).creationCode + ); + payloads[2] = GovV3Helpers.buildAvalanchePayload(vm, actionsAvalanche); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsOptimism = new IPayloadsControllerCore.ExecutionAction[](1); + actionsOptimism[0] = GovV3Helpers.buildAction( + type(AaveV3Optimism_RiskStewardPhase2_20240805).creationCode + ); + payloads[3] = GovV3Helpers.buildOptimismPayload(vm, actionsOptimism); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsArbitrum = new IPayloadsControllerCore.ExecutionAction[](1); + actionsArbitrum[0] = GovV3Helpers.buildAction( + type(AaveV3Arbitrum_RiskStewardPhase2_20240805).creationCode + ); + payloads[4] = GovV3Helpers.buildArbitrumPayload(vm, actionsArbitrum); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsMetis = new IPayloadsControllerCore.ExecutionAction[](1); + actionsMetis[0] = GovV3Helpers.buildAction( + type(AaveV3Metis_RiskStewardPhase2_20240805).creationCode + ); + payloads[5] = GovV3Helpers.buildMetisPayload(vm, actionsMetis); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsBase = new IPayloadsControllerCore.ExecutionAction[](1); + actionsBase[0] = GovV3Helpers.buildAction( + type(AaveV3Base_RiskStewardPhase2_20240805).creationCode + ); + payloads[6] = GovV3Helpers.buildBasePayload(vm, actionsBase); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsGnosis = new IPayloadsControllerCore.ExecutionAction[](1); + actionsGnosis[0] = GovV3Helpers.buildAction( + type(AaveV3Gnosis_RiskStewardPhase2_20240805).creationCode + ); + payloads[7] = GovV3Helpers.buildGnosisPayload(vm, actionsGnosis); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsScroll = new IPayloadsControllerCore.ExecutionAction[](1); + actionsScroll[0] = GovV3Helpers.buildAction( + type(AaveV3Scroll_RiskStewardPhase2_20240805).creationCode + ); + payloads[8] = GovV3Helpers.buildScrollPayload(vm, actionsScroll); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsBNB = new IPayloadsControllerCore.ExecutionAction[](1); + actionsBNB[0] = GovV3Helpers.buildAction( + type(AaveV3BNB_RiskStewardPhase2_20240805).creationCode + ); + payloads[9] = GovV3Helpers.buildBNBPayload(vm, actionsBNB); + + payloads[10] = PayloadsControllerUtils.Payload({ + chain: ChainIds.ZKSYNC, + accessLevel: PayloadsControllerUtils.AccessControl.Level_1, + payloadsController: address(GovernanceV3ZkSync.PAYLOADS_CONTROLLER), + payloadId: 8 + }); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile(vm, 'src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md') + ); + } +} diff --git a/src/20240805_Multi_RiskStewardPhase2/config.ts b/src/20240805_Multi_RiskStewardPhase2/config.ts new file mode 100644 index 000000000..7ec3dba56 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/config.ts @@ -0,0 +1,38 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: [ + 'AaveV3Ethereum', + 'AaveV3EthereumLido', + 'AaveV3Polygon', + 'AaveV3Avalanche', + 'AaveV3Optimism', + 'AaveV3Arbitrum', + 'AaveV3Metis', + 'AaveV3Base', + 'AaveV3Gnosis', + 'AaveV3Scroll', + 'AaveV3BNB', + ], + title: 'Risk Steward Phase 2', + shortName: 'RiskStewardPhase2', + date: '20240805', + author: 'BGD Labs (@bgdlabs)', + discussion: 'https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204', + snapshot: 'Direct To AIP', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 20460200}}, + AaveV3EthereumLido: {configs: {OTHERS: {}}, cache: {blockNumber: 20460202}}, + AaveV3Polygon: {configs: {OTHERS: {}}, cache: {blockNumber: 60210763}}, + AaveV3Avalanche: {configs: {OTHERS: {}}, cache: {blockNumber: 48863700}}, + AaveV3Optimism: {configs: {OTHERS: {}}, cache: {blockNumber: 123618024}}, + AaveV3Arbitrum: {configs: {OTHERS: {}}, cache: {blockNumber: 239583893}}, + AaveV3Metis: {configs: {OTHERS: {}}, cache: {blockNumber: 17959349}}, + AaveV3Base: {configs: {OTHERS: {}}, cache: {blockNumber: 18022742}}, + AaveV3Gnosis: {configs: {OTHERS: {}}, cache: {blockNumber: 35324260}}, + AaveV3Scroll: {configs: {OTHERS: {}}, cache: {blockNumber: 8079683}}, + AaveV3BNB: {configs: {OTHERS: {}}, cache: {blockNumber: 41088826}}, + }, +}; diff --git a/src/20240805_Multi_RiskStewardPhase2/interfaces/IRiskSteward.sol b/src/20240805_Multi_RiskStewardPhase2/interfaces/IRiskSteward.sol new file mode 100644 index 000000000..81cd11c61 --- /dev/null +++ b/src/20240805_Multi_RiskStewardPhase2/interfaces/IRiskSteward.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IRiskSteward { + /** + * @notice method called by the owner to set an asset as restricted + * @param contractAddress address of the underlying asset + * @param isRestricted true if asset needs to be restricted, false otherwise + */ + function setAddressRestricted(address contractAddress, bool isRestricted) external; + + /** + * @notice method to check if an asset is restricted to be used by the risk stewards + * @param contractAddress address of the underlying asset + * @return bool if asset is restricted or not + */ + function isAddressRestricted(address contractAddress) external view returns (bool); +} diff --git a/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.sol b/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.sol new file mode 100644 index 000000000..9063153e2 --- /dev/null +++ b/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {AaveV3ZkSync} from 'aave-address-book/AaveV3ZkSync.sol'; + +/** + * @title Risk Steward Phase 2 + * @author BGD Labs (@bgdlabs) + * - Snapshot: https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16 + * - Discussion: https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204 + */ +contract AaveV3ZkSync_RiskStewardPhase2_20240805 is IProposalGenericExecutor { + function execute() external { + AaveV3ZkSync.ACL_MANAGER.addRiskAdmin(AaveV3ZkSync.RISK_STEWARD); + } +} diff --git a/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol b/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol new file mode 100644 index 000000000..464f599c4 --- /dev/null +++ b/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3ZkSync} from 'aave-address-book/AaveV3ZkSync.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/zksync/src/ProtocolV3TestBase.sol'; +import {AaveV3ZkSync_RiskStewardPhase2_20240805} from './AaveV3ZkSync_RiskStewardPhase2_20240805.sol'; + +/** + * @dev Test for AaveV3ZkSync_RiskStewardPhase2_20240805 + * command: FOUNDRY_PROFILE=zksync forge test --zksync --match-path=zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol -vv + */ +contract AaveV3ZkSync_RiskStewardPhase2_20240805_Test is ProtocolV3TestBase { + AaveV3ZkSync_RiskStewardPhase2_20240805 internal proposal; + + function setUp() public override { + vm.createSelectFork(vm.rpcUrl('zksync'), 47823875); + proposal = new AaveV3ZkSync_RiskStewardPhase2_20240805(); + + super.setUp(); + } + + /** + * @dev executes the generic test suite like config snapshots, e2e tests are disabled due to pool not being activated + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3ZkSync_RiskStewardPhase2_20240805', + AaveV3ZkSync.POOL, + address(proposal), + false + ); + } + + function test_permissions() public { + executePayload(vm, address(proposal)); + + assertEq(AaveV3ZkSync.ACL_MANAGER.isRiskAdmin(AaveV3ZkSync.RISK_STEWARD), true); + } +} diff --git a/zksync/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol b/zksync/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol new file mode 100644 index 000000000..3def37a24 --- /dev/null +++ b/zksync/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers, IPayloadsControllerCore} from 'aave-helpers/src/GovV3Helpers.sol'; +import {ZkSyncScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; +import {AaveV3ZkSync_RiskStewardPhase2_20240805} from './AaveV3ZkSync_RiskStewardPhase2_20240805.sol'; + +// @dev wrapper factory contract for deploying the payload +contract Deploy_AaveV3ZkSync_RiskStewardPhase2_20240805 { + address public immutable PAYLOAD; + + constructor() { + PAYLOAD = GovV3Helpers.deployDeterministicZkSync( + type(AaveV3ZkSync_RiskStewardPhase2_20240805).creationCode + ); + } +} + +/** + * @dev Deploy ZkSync + * deploy-command: make deploy-ledger contract=zksync/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2_20240805.s.sol:DeployZkSync chain=zksync + * verify-command: FOUNDRY_PROFILE=zksync npx catapulta-verify -b broadcast/RiskStewardPhase2_20240805.s.sol/324/run-latest.json + */ +contract DeployZkSync is ZkSyncScript { + function run() external broadcast { + // deploy payloads + address payload0 = new Deploy_AaveV3ZkSync_RiskStewardPhase2_20240805().PAYLOAD(); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} From 911acb263ec3e504b8c84830c1b7dc4c043ca077 Mon Sep 17 00:00:00 2001 From: Cache bot Date: Fri, 1 Nov 2024 10:44:51 +0000 Subject: [PATCH 15/15] fix(cache): automated cache update [skip ci] --- src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md b/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md index bf11b8390..822770a5f 100644 --- a/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md +++ b/src/20240805_Multi_RiskStewardPhase2/RiskStewardPhase2.md @@ -70,8 +70,8 @@ The new risk stewards have been configured with the following risk params on all ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.sol), [AaveV3EthereumEtherFi](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol), [AaveV3Polygon](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.sol), [AaveV3Avalanche](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.sol), [AaveV3Optimism](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.sol), [AaveV3Metis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.sol), [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.sol), [AaveV3Scroll](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.sol), [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.sol), [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol), [AaveV3EthereumEtherFi](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol), [AaveV3Polygon](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol), [AaveV3Avalanche](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol), [AaveV3Optimism](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol), [AaveV3Metis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol), [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol), [AaveV3Scroll](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol), [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol), [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.sol), [AaveV3EthereumEtherFi](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.sol), [AaveV3Polygon](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.sol), [AaveV3Avalanche](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.sol), [AaveV3Optimism](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.sol), [AaveV3Metis](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.sol), [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.sol), [AaveV3Scroll](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.sol), [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.sol), [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Ethereum_RiskStewardPhase2_20240805.t.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumLido_RiskStewardPhase2_20240805.t.sol), [AaveV3EthereumEtherFi](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3EthereumEtherFi_RiskStewardPhase2_20240805.t.sol), [AaveV3Polygon](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Polygon_RiskStewardPhase2_20240805.t.sol), [AaveV3Avalanche](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Avalanche_RiskStewardPhase2_20240805.t.sol), [AaveV3Optimism](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Optimism_RiskStewardPhase2_20240805.t.sol), [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Arbitrum_RiskStewardPhase2_20240805.t.sol), [AaveV3Metis](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Metis_RiskStewardPhase2_20240805.t.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Base_RiskStewardPhase2_20240805.t.sol), [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Gnosis_RiskStewardPhase2_20240805.t.sol), [AaveV3Scroll](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3Scroll_RiskStewardPhase2_20240805.t.sol), [AaveV3BNB](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/src/20240805_Multi_RiskStewardPhase2/AaveV3BNB_RiskStewardPhase2_20240805.t.sol), [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/6133c63c69341c4462fc0fc00874096752a01fcb/zksync/src/20240805_Multi_RiskStewardPhase2/AaveV3ZkSync_RiskStewardPhase2_20240805.t.sol) - [Discussion](https://governance.aave.com/t/arfc-bgd-risk-steward-phase-2-risksteward/16204) - [Snapshot](https://snapshot.org/#/aave.eth/proposal/0x4809f179e517e5745ec13eba8f40d98dab73ca65f8a141bd2f18cc16dcd0cc16) - Github Repo: [Aave V3 Risk Stewards](https://github.com/aave-dao/aave-v3-risk-stewards)