From f44270b61b402ce70bd5105418a8191ceea06959 Mon Sep 17 00:00:00 2001 From: Marc Zeller <21088542+marczeller@users.noreply.github.com> Date: Tue, 31 Dec 2024 11:04:01 +0100 Subject: [PATCH 01/21] Remove USDS from sUSDe emode in Aave Prime instance - Review (#553) Co-authored-by: Rozengarden --- ...EModeInAavePrimeInstance_20241224_after.md | 48 +++++++++++++ ...EModeInAavePrimeInstance_20241224_after.md | 36 ++++++++++ ...iquidEModeInAavePrimeInstance_20241224.sol | 54 ++++++++++++++ ...uidEModeInAavePrimeInstance_20241224.t.sol | 35 +++++++++ ...iquidEModeInAavePrimeInstance_20241224.sol | 36 ++++++++++ ...uidEModeInAavePrimeInstance_20241224.t.sol | 35 +++++++++ ...FromSUSDeLiquidEModeInAavePrimeInstance.md | 45 ++++++++++++ ...uidEModeInAavePrimeInstance_20241224.s.sol | 72 +++++++++++++++++++ .../config.ts | 53 ++++++++++++++ 9 files changed, 414 insertions(+) create mode 100644 diffs/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md create mode 100644 diffs/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md create mode 100644 src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol create mode 100644 src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol create mode 100644 src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol create mode 100644 src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol create mode 100644 src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md create mode 100644 src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.s.sol create mode 100644 src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/config.ts diff --git a/diffs/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md b/diffs/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md new file mode 100644 index 000000000..400bb480a --- /dev/null +++ b/diffs/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md @@ -0,0 +1,48 @@ +## Emodes changed + +### EMode: ETH correlated(id: 1) + + + +### EMode: LRT Stablecoins main(id: 2) + + + +### EMode: LRT wstETH main(id: 3) + + + +### EMode: sUSDe Stablecoins(id: 4) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label (unchanged) | sUSDe Stablecoins | sUSDe Stablecoins | +| eMode.ltv (unchanged) | 90 % | 90 % | +| eMode.liquidationThreshold (unchanged) | 92 % | 92 % | +| eMode.liquidationBonus | 3 % | 4 % | +| eMode.borrowableBitmap | USDS, USDC, GHO | USDC, GHO | +| eMode.collateralBitmap (unchanged) | sUSDe | sUSDe | + + +### EMode: rsETH LST main(id: 5) + + + +## Raw diff + +```json +{ + "eModes": { + "4": { + "borrowableBitmap": { + "from": "76", + "to": "72" + }, + "liquidationBonus": { + "from": 10300, + "to": 10400 + } + } + } +} +``` \ No newline at end of file diff --git a/diffs/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md b/diffs/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md new file mode 100644 index 000000000..499afdcb0 --- /dev/null +++ b/diffs/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_before_AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_after.md @@ -0,0 +1,36 @@ +## Emodes changed + +### EMode: ETH correlated(id: 1) + + + +### EMode: sUSDe Stablecoins(id: 2) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label (unchanged) | sUSDe Stablecoins | sUSDe Stablecoins | +| eMode.ltv (unchanged) | 90 % | 90 % | +| eMode.liquidationThreshold (unchanged) | 92 % | 92 % | +| eMode.liquidationBonus | 3 % | 4 % | +| eMode.borrowableBitmap (unchanged) | USDC, USDT, USDS | USDC, USDT, USDS | +| eMode.collateralBitmap (unchanged) | sUSDe | sUSDe | + + +### EMode: rsETH LST main(id: 3) + + + +## Raw diff + +```json +{ + "eModes": { + "2": { + "liquidationBonus": { + "from": 10300, + "to": 10400 + } + } + } +} +``` \ No newline at end of file diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol new file mode 100644 index 000000000..34822d0b0 --- /dev/null +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {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'; +/** + * @title Proposal to Remove USDS from sUSDe Liquid E-Mode in Aave Prime Instance + * @author Aave-chan Initiative + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x2be035a75fb8c5bb4e99e56006e57b7eb7df8bdd5616d903309ef6fc5b7446de + * - Discussion: https://governance.aave.com/t/arfc-proposal-to-remove-usds-from-susde-liquid-e-mode-in-aave-prime-instance/20264 + */ +contract AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224 is + AaveV3PayloadEthereumLido +{ + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](1); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: AaveV3EthereumLidoEModes.SUSDE_STABLECOINS, + ltv: EngineFlags.KEEP_CURRENT, + liqThreshold: EngineFlags.KEEP_CURRENT, + liqBonus: 4_00, + label: EngineFlags.KEEP_CURRENT_STRING + }); + + return eModeUpdates; + } + function assetsEModeUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.AssetEModeUpdate[] memory) + { + IAaveV3ConfigEngine.AssetEModeUpdate[] + memory assetEModeUpdates = new IAaveV3ConfigEngine.AssetEModeUpdate[](1); + + assetEModeUpdates[0] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3EthereumLidoAssets.USDS_UNDERLYING, + eModeCategory: AaveV3EthereumLidoEModes.SUSDE_STABLECOINS, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.KEEP_CURRENT + }); + + return assetEModeUpdates; + } +} diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol new file mode 100644 index 000000000..673c18f5b --- /dev/null +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumLido} from 'aave-address-book/AaveV3EthereumLido.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224} from './AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol'; + +/** + * @dev Test for AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol -vv + */ +contract AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_Test is + ProtocolV3TestBase +{ + AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224 + internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21474028); + proposal = new AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224', + AaveV3EthereumLido.POOL, + address(proposal) + ); + } +} diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol new file mode 100644 index 000000000..61f55e0dc --- /dev/null +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumEModes} 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 Proposal to Remove USDS from sUSDe Liquid E-Mode in Aave Prime Instance + * @author Aave-chan Initiative + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x2be035a75fb8c5bb4e99e56006e57b7eb7df8bdd5616d903309ef6fc5b7446de + * - Discussion: https://governance.aave.com/t/arfc-proposal-to-remove-usds-from-susde-liquid-e-mode-in-aave-prime-instance/20264 + */ +contract AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224 is + AaveV3PayloadEthereum +{ + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](1); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: AaveV3EthereumEModes.SUSDE_STABLECOINS, + ltv: EngineFlags.KEEP_CURRENT, + liqThreshold: EngineFlags.KEEP_CURRENT, + liqBonus: 4_00, + label: EngineFlags.KEEP_CURRENT_STRING + }); + + return eModeUpdates; + } +} diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol new file mode 100644 index 000000000..6d5d74244 --- /dev/null +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol @@ -0,0 +1,35 @@ +// 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_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224} from './AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol'; + +/** + * @dev Test for AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol -vv + */ +contract AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224_Test is + ProtocolV3TestBase +{ + AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224 + internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21474019); + proposal = new AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224', + AaveV3Ethereum.POOL, + address(proposal) + ); + } +} diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md new file mode 100644 index 000000000..e5b6e1731 --- /dev/null +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md @@ -0,0 +1,45 @@ +--- +title: "Proposal to Remove USDS from sUSDe Liquid E-Mode in Aave Prime Instance" +author: "Aave-chan Initiative" +discussions: "https://governance.aave.com/t/arfc-proposal-to-remove-usds-from-susde-liquid-e-mode-in-aave-prime-instance/20264" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0x2be035a75fb8c5bb4e99e56006e57b7eb7df8bdd5616d903309ef6fc5b7446de" +--- + +## Simple Summary + +This proposal recommends the removal of USDS from the sUSDe Liquid E-Mode in the Aave Prime instance. + +## Motivation + +The sUSDe Liquid E-Mode was introduced to enhance capital efficiency for users by allowing higher loan-to-value (LTV) ratios when using sUSDe as collateral to borrow stablecoins like USDS. See [this proposal](https://governance.aave.com/t/arfc-onboard-and-enable-susde-liquid-e-mode-on-aave-v3-mainnet-and-lido-instance/19703) for more context. + +However, recent market observations have indicated increased borrow rates and potential liquidity mismatches involving USDS within this E-Mode. To address these concerns, this proposal suggests temporarily removing USDS from the sUSDe Liquid E-Mode in the Aave Prime instance. + +The primary motivations for this proposal are: + +1. **Risk Mitigation:** The inclusion of USDS in the sUSDe Liquid E-Mode has led to elevated borrow rates and potential liquidity mismatches. +2. **Collateral Isolation:** Until a wrapper is available to isolate USDS collateral in the Prime instance, USDS remains the primary exposure. Removing it from the sUSDe Liquid E-Mode will help manage associated risks more effectively. +3. **User Impact:** This change will not negatively impact existing user positions but will prevent the establishment of new ones involving USDS in the sUSDe Liquid E-Mode, thereby safeguarding current users while mitigating potential future risks. + +## Specification + +The proposed changes are as follows: + +- **Asset Removal:** Exclude USDS from the sUSDe Liquid E-Mode in the Aave Prime instance. +- **Parameter Adjustments:** Update the E-Mode configuration to reflect the removal of USDS, ensuring alignment with the protocol’s risk management framework. +- **Liquidation buffer improvement:** + echoing concerns from @LlamaRisk and other service providers, sUSDe emode on both Prime & Core instances are set to increase their buffer for liquidations. + - Increase Liquidation Bonus from 3 to 4% on e-modes on Core and Prime instances. + +Current ARFC will be reviewed by Risk Service Providers and their feedback will be included in the current ARFC. + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x2be035a75fb8c5bb4e99e56006e57b7eb7df8bdd5616d903309ef6fc5b7446de) +- [Discussion](https://governance.aave.com/t/arfc-proposal-to-remove-usds-from-susde-liquid-e-mode-in-aave-prime-instance/20264) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.s.sol b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.s.sol new file mode 100644 index 000000000..6e2f8e065 --- /dev/null +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.s.sol @@ -0,0 +1,72 @@ +// 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_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224} from './AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol'; +import {AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224} from './AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224) + .creationCode + ); + address payload1 = GovV3Helpers.deployDeterministic( + type(AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224) + .creationCode + ); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](2); + actions[0] = GovV3Helpers.buildAction(payload0); + actions[1] = GovV3Helpers.buildAction(payload1); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Create Proposal + * command: make deploy-ledger contract=src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.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[](2); + actionsEthereum[0] = GovV3Helpers.buildAction( + type(AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224) + .creationCode + ); + actionsEthereum[1] = GovV3Helpers.buildAction( + type(AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224) + .creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md' + ) + ); + } +} diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/config.ts b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/config.ts new file mode 100644 index 000000000..55c053781 --- /dev/null +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/config.ts @@ -0,0 +1,53 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + author: 'Aave-chan Initiative', + pools: ['AaveV3Ethereum', 'AaveV3EthereumLido'], + title: 'Proposal to Remove USDS from sUSDe Liquid E-Mode in Aave Prime Instance', + shortName: 'ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance', + date: '20241224', + discussion: + 'https://governance.aave.com/t/arfc-proposal-to-remove-usds-from-susde-liquid-e-mode-in-aave-prime-instance/20264', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x2be035a75fb8c5bb4e99e56006e57b7eb7df8bdd5616d903309ef6fc5b7446de', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Ethereum: { + configs: { + EMODES_UPDATES: [ + { + eModeCategory: 'AaveV3EthereumEModes.SUSDE_STABLECOINS', + ltv: '', + liqThreshold: '', + liqBonus: '4', + label: '', + }, + ], + }, + cache: {blockNumber: 21474019}, + }, + AaveV3EthereumLido: { + configs: { + EMODES_UPDATES: [ + { + eModeCategory: 'AaveV3EthereumLidoEModes.SUSDE_STABLECOINS', + ltv: '', + liqThreshold: '', + liqBonus: '4', + label: '', + }, + ], + EMODES_ASSETS: [ + { + asset: 'USDS', + eModeCategory: 'AaveV3EthereumLidoEModes.SUSDE_STABLECOINS', + collateral: 'KEEP_CURRENT', + borrowable: 'DISABLED', + }, + ], + }, + cache: {blockNumber: 21474028}, + }, + }, +}; From d7674fd882d853761903961a9d1e2ef4f34b4b9c Mon Sep 17 00:00:00 2001 From: Cache bot Date: Tue, 31 Dec 2024 10:04:26 +0000 Subject: [PATCH 02/21] fix(cache): automated cache update [skip ci] --- ...osalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md index e5b6e1731..527259063 100644 --- a/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md +++ b/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance.md @@ -35,8 +35,8 @@ Current ARFC will be reviewed by Risk Service Providers and their feedback will ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/f44270b61b402ce70bd5105418a8191ceea06959/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/f44270b61b402ce70bd5105418a8191ceea06959/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/f44270b61b402ce70bd5105418a8191ceea06959/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3Ethereum_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol), [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/f44270b61b402ce70bd5105418a8191ceea06959/src/20241224_Multi_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance/AaveV3EthereumLido_ProposalToRemoveUSDSFromSUSDeLiquidEModeInAavePrimeInstance_20241224.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x2be035a75fb8c5bb4e99e56006e57b7eb7df8bdd5616d903309ef6fc5b7446de) - [Discussion](https://governance.aave.com/t/arfc-proposal-to-remove-usds-from-susde-liquid-e-mode-in-aave-prime-instance/20264) From d3121403bfca70eac18f6adaa966a1229df75d67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 08:55:51 +0100 Subject: [PATCH 03/21] chore(deps): bump lib/aave-helpers from `1821d62` to `7cdcda6` (#556) Bumps [lib/aave-helpers](https://github.com/bgd-labs/aave-helpers) from `1821d62` to `7cdcda6`. - [Release notes](https://github.com/bgd-labs/aave-helpers/releases) - [Commits](https://github.com/bgd-labs/aave-helpers/compare/1821d62ccddb4d9d66ad35405cbaaeec795bd1b2...7cdcda6516b6c08d3f3cee9e27b541c179e66fe3) --- updated-dependencies: - dependency-name: lib/aave-helpers dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- lib/aave-helpers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/aave-helpers b/lib/aave-helpers index 1821d62cc..7cdcda651 160000 --- a/lib/aave-helpers +++ b/lib/aave-helpers @@ -1 +1 @@ -Subproject commit 1821d62ccddb4d9d66ad35405cbaaeec795bd1b2 +Subproject commit 7cdcda6516b6c08d3f3cee9e27b541c179e66fe3 From 0ddae1e40d3255d4a2ff2768ae707bcb99a4aa9e Mon Sep 17 00:00:00 2001 From: Alice <121383428+Rozengarden@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:21:11 +0100 Subject: [PATCH 04/21] Aavechan/gnosis opti (#555) * feat: Gnosis update * fix: osGNO pricefeed * implement Part 1 without OsGNO --------- Co-authored-by: Marc Zeller <21088542+marczeller@users.noreply.github.com> Co-authored-by: marczeller --- ...eV3GnosisInstanceUpdates_20241224_after.md | 70 ++++++++++++ .../AaveV3GnosisInstanceUpdates.md | 49 +++++++++ ...AaveV3GnosisInstanceUpdates_20241224.s.sol | 60 ++++++++++ ...s_AaveV3GnosisInstanceUpdates_20241224.sol | 103 ++++++++++++++++++ ...AaveV3GnosisInstanceUpdates_20241224.t.sol | 35 ++++++ .../config.ts | 86 +++++++++++++++ 6 files changed, 403 insertions(+) create mode 100644 diffs/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_before_AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_after.md create mode 100644 src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md create mode 100644 src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates_20241224.s.sol create mode 100644 src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol create mode 100644 src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol create mode 100644 src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/config.ts diff --git a/diffs/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_before_AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_after.md b/diffs/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_before_AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_after.md new file mode 100644 index 000000000..13ec6044f --- /dev/null +++ b/diffs/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_before_AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_after.md @@ -0,0 +1,70 @@ +## Reserve changes + +### Reserve altered + +#### GNO ([0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb](https://gnosisscan.io/address/0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb)) + +| description | value before | value after | +| --- | --- | --- | +| debtCeiling | 2,000,000 $ [200000000] | 0 $ [0] | + + +#### EURe ([0xcB444e90D8198415266c6a2724b7900fb12FC56E](https://gnosisscan.io/address/0xcB444e90D8198415266c6a2724b7900fb12FC56E)) + +| description | value before | value after | +| --- | --- | --- | +| reserveFactor | 20 % [2000] | 10 % [1000] | + + +## Emodes changed + +### EMode: ETH correlated(id: 1) + + + +### EMode: sDAI / EURe(id: 2) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | sDAI / EURe | +| eMode.ltv | - | 85 % | +| eMode.liquidationThreshold | - | 87.5 % | +| eMode.liquidationBonus | - | 5 % | +| eMode.borrowableBitmap | - | EURe | +| eMode.collateralBitmap | - | sDAI | + + +## Raw diff + +```json +{ + "eModes": { + "2": { + "from": null, + "to": { + "borrowableBitmap": "32", + "collateralBitmap": "64", + "eModeCategory": 2, + "label": "sDAI / EURe", + "liquidationBonus": 10500, + "liquidationThreshold": 8750, + "ltv": 8500 + } + } + }, + "reserves": { + "0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb": { + "debtCeiling": { + "from": 200000000, + "to": 0 + } + }, + "0xcB444e90D8198415266c6a2724b7900fb12FC56E": { + "reserveFactor": { + "from": 2000, + "to": 1000 + } + } + } +} +``` \ No newline at end of file diff --git a/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md new file mode 100644 index 000000000..50f533f44 --- /dev/null +++ b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md @@ -0,0 +1,49 @@ +--- +title: "Aave v3 Gnosis Instance Updates Part 1" +author: "Aave-chan Initiative" +discussions: "https://governance.aave.com/t/arfc-aave-v3-gnosis-instance-updates/20334" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0x2e93ddd01ba5ec415b0962907b7c65def947d1ed94f1e5b402c5578560b1dddb" +--- + +## Simple Summary + +This AIP proposes several updates to the Aave v3 Gnosis instance to improve capital efficiency and add new use cases on the network. The key changes include removing GNO from isolation mode, adjusting the reserve factor for EURe, and creating a new relevant E-mode. + +## Motivation + +GNO has demonstrated strong stability and market presence on Gnosis Chain, making isolation mode unnecessarily restrictive and hindering network growth. Removing GNO from isolation mode will facilitate further expansion of the network. + +The reduction in the EURe reserve factor aligns with the asset's performance and incentivizes increased lending activity. + +introducing a new E-mode for sDAI & EURe will enhance capital efficiency and foster synergies between stable assets. The unique combination of EUR and USD borrowing opportunities is a distinct advantage for Gnosis Chain. + +## Specification + +### Rates and parameters updates + +- Remove GNO from isolation mode +- change EURe Reserve Factor from 20% to 10% + +### E-Modes + +The followings E-mode will be created: + +| **Parameter** | **Value** | **Value** | +| --------------------- | --------- | --------- | +| Asset | sDAI | EURe | +| Collateral | Yes | No | +| Borrowable | No | Yes | +| Max LTV | 85% | 85% | +| Liquidation Threshold | 87.5% | 87.5% | +| Liquidation Bonus | 5% | 5% | + +## References + +- Implementation: [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol) +- Tests: [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x2e93ddd01ba5ec415b0962907b7c65def947d1ed94f1e5b402c5578560b1dddb) +- [Discussion](https://governance.aave.com/t/arfc-aave-v3-gnosis-instance-updates/20334) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates_20241224.s.sol b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates_20241224.s.sol new file mode 100644 index 000000000..9d4631866 --- /dev/null +++ b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates_20241224.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, GnosisScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; +import {AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224} from './AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol'; + +/** + * @dev Deploy Gnosis + * deploy-command: make deploy-ledger contract=src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates_20241224.s.sol:DeployGnosis chain=gnosis + * verify-command: FOUNDRY_PROFILE=gnosis npx catapulta-verify -b broadcast/AaveV3GnosisInstanceUpdates_20241224.s.sol/100/run-latest.json + */ +contract DeployGnosis is GnosisScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224).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/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates_20241224.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 actionsGnosis = new IPayloadsControllerCore.ExecutionAction[](1); + actionsGnosis[0] = GovV3Helpers.buildAction( + type(AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224).creationCode + ); + payloads[0] = GovV3Helpers.buildGnosisPayload(vm, actionsGnosis); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md' + ) + ); + } +} diff --git a/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol new file mode 100644 index 000000000..9f22a712a --- /dev/null +++ b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Gnosis, AaveV3GnosisAssets} from 'aave-address-book/AaveV3Gnosis.sol'; +import {AaveV3PayloadGnosis} from 'aave-helpers/src/v3-config-engine/AaveV3PayloadGnosis.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 Aave v3 Gnosis Instance Updates + * @author Aave-chan Initiative + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x2e93ddd01ba5ec415b0962907b7c65def947d1ed94f1e5b402c5578560b1dddb + * - Discussion: https://governance.aave.com/t/arfc-aave-v3-gnosis-instance-updates/20334 + */ +contract AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224 is AaveV3PayloadGnosis { + using SafeERC20 for IERC20; + + function collateralsUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.CollateralUpdate[] memory) + { + IAaveV3ConfigEngine.CollateralUpdate[] + memory collateralUpdate = new IAaveV3ConfigEngine.CollateralUpdate[](1); + + collateralUpdate[0] = IAaveV3ConfigEngine.CollateralUpdate({ + asset: AaveV3GnosisAssets.GNO_UNDERLYING, + ltv: EngineFlags.KEEP_CURRENT, + liqThreshold: EngineFlags.KEEP_CURRENT, + liqBonus: EngineFlags.KEEP_CURRENT, + debtCeiling: 0, + liqProtocolFee: EngineFlags.KEEP_CURRENT + }); + + return collateralUpdate; + } + function borrowsUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.BorrowUpdate[] memory) + { + IAaveV3ConfigEngine.BorrowUpdate[] + memory borrowUpdates = new IAaveV3ConfigEngine.BorrowUpdate[](1); + + borrowUpdates[0] = IAaveV3ConfigEngine.BorrowUpdate({ + asset: AaveV3GnosisAssets.EURe_UNDERLYING, + enabledToBorrow: EngineFlags.KEEP_CURRENT, + flashloanable: EngineFlags.KEEP_CURRENT, + borrowableInIsolation: EngineFlags.KEEP_CURRENT, + withSiloedBorrowing: EngineFlags.KEEP_CURRENT, + reserveFactor: 10_00 + }); + + return borrowUpdates; + } + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](1); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 2, + ltv: 85_00, + liqThreshold: 87_50, + liqBonus: 5_00, + label: 'sDAI / EURe' + }); + + return eModeUpdates; + } + function assetsEModeUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.AssetEModeUpdate[] memory) + { + IAaveV3ConfigEngine.AssetEModeUpdate[] + memory assetEModeUpdates = new IAaveV3ConfigEngine.AssetEModeUpdate[](2); + + assetEModeUpdates[0] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3GnosisAssets.EURe_UNDERLYING, + eModeCategory: 2, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3GnosisAssets.sDAI_UNDERLYING, + eModeCategory: 2, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + + return assetEModeUpdates; + } +} diff --git a/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol new file mode 100644 index 000000000..37dcb0b19 --- /dev/null +++ b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3Gnosis} from 'aave-address-book/AaveV3Gnosis.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224} from './AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol'; + +/** + * @dev Test for AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224 + * command: FOUNDRY_PROFILE=gnosis forge test --match-path=src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol -vv + */ +contract AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224_Test is ProtocolV3TestBase { + AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('gnosis'), 37810182); + proposal = new AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224', + AaveV3Gnosis.POOL, + address(proposal) + ); + } +} diff --git a/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/config.ts b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/config.ts new file mode 100644 index 000000000..0aa8c2e02 --- /dev/null +++ b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/config.ts @@ -0,0 +1,86 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + configFile: 'src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/config.ts', + force: true, + author: 'Aave-chan Initiative', + pools: ['AaveV3Gnosis'], + title: 'Aave v3 Gnosis Instance Updates', + shortName: 'AaveV3GnosisInstanceUpdates', + date: '20241224', + discussion: 'https://governance.aave.com/t/arfc-aave-v3-gnosis-instance-updates/20334', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x2e93ddd01ba5ec415b0962907b7c65def947d1ed94f1e5b402c5578560b1dddb', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Gnosis: { + configs: { + COLLATERALS_UPDATE: [ + { + asset: 'GNO', + ltv: '', + liqThreshold: '', + liqBonus: '', + debtCeiling: '0', + liqProtocolFee: '', + }, + ], + BORROWS_UPDATE: [ + { + enabledToBorrow: 'KEEP_CURRENT', + flashloanable: 'KEEP_CURRENT', + borrowableInIsolation: 'KEEP_CURRENT', + withSiloedBorrowing: 'KEEP_CURRENT', + reserveFactor: '10', + asset: 'EURe', + }, + ], + EMODES_UPDATES: [ + { + eModeCategory: 2, + ltv: '90', + liqThreshold: '92.5', + liqBonus: '2.5', + label: 'osGNO / GNO', + }, + {eModeCategory: 3, ltv: '85', liqThreshold: '87.5', liqBonus: '5', label: 'sDAI / EURe'}, + ], + EMODES_ASSETS: [ + {asset: 'osGNO', eModeCategory: '2', collateral: 'ENABLED', borrowable: 'DISABLED'}, + {asset: 'GNO', eModeCategory: '2', collateral: 'DISABLED', borrowable: 'ENABLED'}, + {asset: 'EURe', eModeCategory: '3', collateral: 'DISABLED', borrowable: 'ENABLED'}, + {asset: 'sDAI', eModeCategory: '3', collateral: 'ENABLED', borrowable: 'DISABLED'}, + ], + ASSET_LISTING: [ + { + assetSymbol: 'osGNO', + decimals: 18, + priceFeed: '0xbE26c8b354208E898EBd88B1576C4df2e216ed30', + ltv: '0.05', + liqThreshold: '0.1', + liqBonus: '7.5', + debtCeiling: '0', + liqProtocolFee: '10', + enabledToBorrow: 'DISABLED', + flashloanable: 'DISABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '10', + supplyCap: '4750', + borrowCap: '1', + rateStrategyParams: { + optimalUtilizationRate: '50', + baseVariableBorrowRate: '0', + variableRateSlope1: '0', + variableRateSlope2: '0', + }, + asset: '0xF490c80aAE5f2616d3e3BDa2483E30C4CB21d1A0', + admin: '0xac140648435d03f784879cd789130F22Ef588Fcd', + }, + ], + }, + cache: {blockNumber: 37692844}, + }, + }, +}; From d766be0a436d25f59dfffcca896777d8db0dd3f7 Mon Sep 17 00:00:00 2001 From: Cache bot Date: Fri, 3 Jan 2025 16:21:35 +0000 Subject: [PATCH 05/21] fix(cache): automated cache update [skip ci] --- .../AaveV3GnosisInstanceUpdates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md index 50f533f44..26d453118 100644 --- a/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md +++ b/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3GnosisInstanceUpdates.md @@ -39,8 +39,8 @@ The followings E-mode will be created: ## References -- Implementation: [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol) -- Tests: [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol) +- Implementation: [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/0ddae1e40d3255d4a2ff2768ae707bcb99a4aa9e/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.sol) +- Tests: [AaveV3Gnosis](https://github.com/bgd-labs/aave-proposals-v3/blob/0ddae1e40d3255d4a2ff2768ae707bcb99a4aa9e/src/20241224_AaveV3Gnosis_AaveV3GnosisInstanceUpdates/AaveV3Gnosis_AaveV3GnosisInstanceUpdates_20241224.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x2e93ddd01ba5ec415b0962907b7c65def947d1ed94f1e5b402c5578560b1dddb) - [Discussion](https://governance.aave.com/t/arfc-aave-v3-gnosis-instance-updates/20334) From 308146dc34181b49d22282ac967de30ed28a3296 Mon Sep 17 00:00:00 2001 From: Fermin 'Piscu' Carranza Date: Mon, 6 Jan 2025 04:45:00 -0500 Subject: [PATCH 06/21] Gho minter (#554) * feat: initial commit * feat: finish tests * chore: update readme --- ...HOIntoAaveV3LidoInstance_20241229_after.md | 30 ++++++ ...y10MGHOIntoAaveV3LidoInstance_20241229.sol | 47 ++++++++++ ...0MGHOIntoAaveV3LidoInstance_20241229.t.sol | 92 +++++++++++++++++++ .../Deploy10MGHOIntoAaveV3LidoInstance.md | 35 +++++++ ...0MGHOIntoAaveV3LidoInstance_20241229.s.sol | 60 ++++++++++++ .../IGhoDirectMinter.sol | 6 ++ .../config.ts | 16 ++++ src/interfaces/IGhoToken.sol | 19 ++++ 8 files changed, 305 insertions(+) create mode 100644 diffs/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_before_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_after.md create mode 100644 src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol create mode 100644 src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol create mode 100644 src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md create mode 100644 src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance_20241229.s.sol create mode 100644 src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/IGhoDirectMinter.sol create mode 100644 src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/config.ts diff --git a/diffs/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_before_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_after.md b/diffs/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_before_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_after.md new file mode 100644 index 000000000..e7c42860b --- /dev/null +++ b/diffs/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_before_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_after.md @@ -0,0 +1,30 @@ +## Reserve changes + +### Reserves altered + +#### GHO ([0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f](https://etherscan.io/address/0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f)) + +| description | value before | value after | +| --- | --- | --- | +| aTokenUnderlyingBalance | 6,108,315.1602 GHO [6108315160265916496775469] | 16,108,315.1602 GHO [16108315160265916496775469] | +| virtualBalance | 6,108,315.1602 GHO [6108315160265916496775469] | 16,108,315.1602 GHO [16108315160265916496775469] | + + +## Raw diff + +```json +{ + "reserves": { + "0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f": { + "aTokenUnderlyingBalance": { + "from": "6108315160265916496775469", + "to": "16108315160265916496775469" + }, + "virtualBalance": { + "from": "6108315160265916496775469", + "to": "16108315160265916496775469" + } + } + } +} +``` \ No newline at end of file diff --git a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol new file mode 100644 index 000000000..1809b38ee --- /dev/null +++ b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3EthereumLido} from 'aave-address-book/AaveV3EthereumLido.sol'; +import {IAccessControl} from 'openzeppelin-contracts/contracts/access/IAccessControl.sol'; +import {IGhoBucketSteward} from '../interfaces/IGhoBucketSteward.sol'; +import {IGhoToken} from '../interfaces/IGhoToken.sol'; +import {IGhoDirectMinter} from './IGhoDirectMinter.sol'; +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; + +/** + * @title Deploy 10M GHO into Aave v3 Lido Instance + * @author karpatkey_TokenLogic & BGD Labs + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x4af37d6b4a1c9029c7693c4bdfb646931a9815c4a56d9066ac1fbdbef7cd15e5 + * - Discussion: https://governance.aave.com/t/arfc-mint-deploy-10m-gho-into-aave-v3-lido-instance/19700 + */ +contract AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229 is + IProposalGenericExecutor +{ + uint128 public constant GHO_MINT_AMOUNT = 10_000_000e18; + + // https://etherscan.io/address/0x2cE01c87Fec1b71A9041c52CaED46Fc5f4807285 + address public constant FACILITATOR = 0x2cE01c87Fec1b71A9041c52CaED46Fc5f4807285; + + function execute() external { + IAccessControl(address(AaveV3EthereumLido.ACL_MANAGER)).grantRole( + AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(), + FACILITATOR + ); + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).addFacilitator( + FACILITATOR, + 'LidoGhoDirectMinter', + GHO_MINT_AMOUNT + ); + IGhoDirectMinter(FACILITATOR).mintAndSupply(GHO_MINT_AMOUNT); + + // Allow risk council to control the bucket capacity + address[] memory vaults = new address[](1); + vaults[0] = FACILITATOR; + // https://etherscan.io/address/0x46Aa1063e5265b43663E81329333B47c517A5409 + IGhoBucketSteward(0x46Aa1063e5265b43663E81329333B47c517A5409).setControlledFacilitator( + vaults, + true + ); + } +} diff --git a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol new file mode 100644 index 000000000..ef94ac3f9 --- /dev/null +++ b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3EthereumLido, AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol'; +import {IAccessControl} from 'openzeppelin-contracts/contracts/access/IAccessControl.sol'; +import {IGhoBucketSteward} from '../interfaces/IGhoBucketSteward.sol'; +import {IGhoToken} from '../interfaces/IGhoToken.sol'; +import {IGhoDirectMinter} from './IGhoDirectMinter.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; + +import {AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229} from './AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol'; + +/** + * @dev Test for AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol -vv + */ +contract AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229_Test is ProtocolV3TestBase { + AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21516467); + proposal = new AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229', + AaveV3EthereumLido.POOL, + address(proposal) + ); + } + + function test_accessControl() public { + assertEq( + IAccessControl(address(AaveV3EthereumLido.ACL_MANAGER)).hasRole( + AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(), + proposal.FACILITATOR() + ), + false + ); + + address[] memory facilitators = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING) + .getFacilitatorsList(); + uint256 facilitatorsLength = facilitators.length; + + assertNotEq(proposal.FACILITATOR(), facilitators[facilitators.length - 1]); + + executePayload(vm, address(proposal)); + + assertEq( + IAccessControl(address(AaveV3EthereumLido.ACL_MANAGER)).hasRole( + AaveV3EthereumLido.ACL_MANAGER.RISK_ADMIN_ROLE(), + proposal.FACILITATOR() + ), + true + ); + + facilitators = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).getFacilitatorsList(); + + assertEq(proposal.FACILITATOR(), facilitators[facilitators.length - 1]); + assertEq(facilitators.length, facilitatorsLength + 1); + } + + function test_supplyIncreases() public { + uint256 ghoBalanceBefore = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( + AaveV3EthereumLidoAssets.GHO_A_TOKEN + ); + + uint256 aGhoBalanceBefore = IERC20(AaveV3EthereumLidoAssets.GHO_A_TOKEN).balanceOf( + proposal.FACILITATOR() + ); + + executePayload(vm, address(proposal)); + + assertEq( + IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING).balanceOf( + AaveV3EthereumLidoAssets.GHO_A_TOKEN + ), + ghoBalanceBefore + proposal.GHO_MINT_AMOUNT() + ); + + assertEq( + IERC20(AaveV3EthereumLidoAssets.GHO_A_TOKEN).balanceOf(proposal.FACILITATOR()), + aGhoBalanceBefore + proposal.GHO_MINT_AMOUNT() + ); + } +} diff --git a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md new file mode 100644 index 000000000..1113127e9 --- /dev/null +++ b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md @@ -0,0 +1,35 @@ +--- +title: "Deploy 10M GHO into Aave v3 Lido Instance" +author: "karpatkey_TokenLogic & BGD Labs" +discussions: "https://governance.aave.com/t/arfc-mint-deploy-10m-gho-into-aave-v3-lido-instance/19700" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0x4af37d6b4a1c9029c7693c4bdfb646931a9815c4a56d9066ac1fbdbef7cd15e5" +--- + +## Simple Summary + +Following the addition of GHO to the Lido instance of Aave v3 on Ethereum, this publication proposes supporting GHO liquidity by minting 10M units of GHO and depositing into Aave Lido. + +## Motivation + +By providing liquidity on the Lido instance, the Aave DAO shall provide the initial bootstrapping liquidity in a very cost efficient manner and, in doing so, enhance the DAO's revenue generated from the Lido instance. GHO holders will be able to deposit GHO to earn the deposit yield whilst benefit from small than otherwise concentration risk within the reserve due to the DAO providing the bootstrapping liquidity. + +With strong demand for GHO using wstETH as collateral resulting in 23.7% of GHO supply sourced from the Main market being backed by wstETH, this demonstrates that users actively seek to benefit from the capital efficiency of using an LST as collateral. Deploying additional GHO into the Lido instance is expected to further encourage this positive behavior, reinforcing the appeal of wstETH as a reliable, yield-bearing collateral for GHO. + +## Specification + +- Prior to proposal, GhoDirectMinter contract was deployed [here](https://etherscan.io/address/0x2cE01c87Fec1b71A9041c52CaED46Fc5f4807285) +- Grant RISK_ADMIN_ROLE to GhoDirectMinter referenced above +- Add GhoDirectMinter as a GHO token facilitator +- Mint 10M GHO and supply into Aave Lido +- Allow GhoBucketSteward to control GhoDirectMinter + +## References + +- Implementation: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol) +- Tests: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x4af37d6b4a1c9029c7693c4bdfb646931a9815c4a56d9066ac1fbdbef7cd15e5) +- [Discussion](https://governance.aave.com/t/arfc-mint-deploy-10m-gho-into-aave-v3-lido-instance/19700) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance_20241229.s.sol b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance_20241229.s.sol new file mode 100644 index 000000000..62b316566 --- /dev/null +++ b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance_20241229.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_Deploy10MGHOIntoAaveV3LidoInstance_20241229} from './AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance_20241229.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/Deploy10MGHOIntoAaveV3LidoInstance_20241229.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229).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/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance_20241229.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_Deploy10MGHOIntoAaveV3LidoInstance_20241229).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md' + ) + ); + } +} diff --git a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/IGhoDirectMinter.sol b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/IGhoDirectMinter.sol new file mode 100644 index 000000000..b58153dd4 --- /dev/null +++ b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/IGhoDirectMinter.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +interface IGhoDirectMinter { + function mintAndSupply(uint256 amount) external; +} diff --git a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/config.ts b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/config.ts new file mode 100644 index 000000000..834a5dcd2 --- /dev/null +++ b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/config.ts @@ -0,0 +1,16 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3EthereumLido'], + title: 'Deploy 10M GHO into Aave v3 Lido Instance', + shortName: 'Deploy10MGHOIntoAaveV3LidoInstance', + date: '20241229', + author: 'karpatkey_TokenLogic & BGD Labs', + discussion: + 'https://governance.aave.com/t/arfc-mint-deploy-10m-gho-into-aave-v3-lido-instance/19700', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x4af37d6b4a1c9029c7693c4bdfb646931a9815c4a56d9066ac1fbdbef7cd15e5', + votingNetwork: 'POLYGON', + }, + poolOptions: {AaveV3EthereumLido: {configs: {OTHERS: {}}, cache: {blockNumber: 21510730}}}, +}; diff --git a/src/interfaces/IGhoToken.sol b/src/interfaces/IGhoToken.sol index 2c78aeebd..ab21aefbe 100644 --- a/src/interfaces/IGhoToken.sol +++ b/src/interfaces/IGhoToken.sol @@ -8,8 +8,27 @@ interface IGhoToken { string label; } + /** + * @notice Add the facilitator passed with the parameters to the facilitators list. + * @dev Only accounts with `FACILITATOR_MANAGER_ROLE` role can call this function + * @param facilitatorAddress The address of the facilitator to add + * @param facilitatorLabel A human readable identifier for the facilitator + * @param bucketCapacity The upward limit of GHO can be minted by the facilitator + */ + function addFacilitator( + address facilitatorAddress, + string calldata facilitatorLabel, + uint128 bucketCapacity + ) external; + function balanceOf(address user) external returns (uint256); + /** + * @notice Returns the list of the addresses of the active facilitator + * @return The list of the facilitators addresses + */ + function getFacilitatorsList() external view returns (address[] memory); + /** * @notice Set the bucket capacity of the facilitator. * @dev Only accounts with `BUCKET_MANAGER_ROLE` role can call this function From 393fc57dc7eb9e5864691eb3745b11df013a2fb0 Mon Sep 17 00:00:00 2001 From: Cache bot Date: Mon, 6 Jan 2025 09:45:24 +0000 Subject: [PATCH 07/21] fix(cache): automated cache update [skip ci] --- .../Deploy10MGHOIntoAaveV3LidoInstance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md index 1113127e9..228f43375 100644 --- a/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md +++ b/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/Deploy10MGHOIntoAaveV3LidoInstance.md @@ -25,8 +25,8 @@ With strong demand for GHO using wstETH as collateral resulting in 23.7% of GHO ## References -- Implementation: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol) -- Tests: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol) +- Implementation: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/308146dc34181b49d22282ac967de30ed28a3296/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.sol) +- Tests: [AaveV3EthereumLido](https://github.com/bgd-labs/aave-proposals-v3/blob/308146dc34181b49d22282ac967de30ed28a3296/src/20241229_AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance/AaveV3EthereumLido_Deploy10MGHOIntoAaveV3LidoInstance_20241229.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x4af37d6b4a1c9029c7693c4bdfb646931a9815c4a56d9066ac1fbdbef7cd15e5) - [Discussion](https://governance.aave.com/t/arfc-mint-deploy-10m-gho-into-aave-v3-lido-instance/19700) From 29ee27b76f4a86e9f213ed430f4df90547e74c39 Mon Sep 17 00:00:00 2001 From: Ian Flexa <85500650+ianflexa@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:10:28 -0400 Subject: [PATCH 08/21] v3.3 sherlock contest funding (#560) * feat: v3.3 sherlock contest * chore: update sherlock recipient address * fix: naming * - Added Sherlock contest description * feat: add addresses in the specification + fix title --------- Co-authored-by: eboado --- ...33SherlockContestFunding_20250106_after.md | 30 +++++++++ .../AaveV33SherlockContestFunding.md | 45 +++++++++++++ ...veV33SherlockContestFunding_20250106.s.sol | 60 +++++++++++++++++ ...AaveV33SherlockContestFunding_20250106.sol | 41 ++++++++++++ ...veV33SherlockContestFunding_20250106.t.sol | 64 +++++++++++++++++++ .../config.ts | 15 +++++ 6 files changed, 255 insertions(+) create mode 100644 diffs/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_before_AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_after.md create mode 100644 src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md create mode 100644 src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding_20250106.s.sol create mode 100644 src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol create mode 100644 src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol create mode 100644 src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/config.ts diff --git a/diffs/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_before_AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_after.md b/diffs/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_before_AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_after.md new file mode 100644 index 000000000..05db47580 --- /dev/null +++ b/diffs/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_before_AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_after.md @@ -0,0 +1,30 @@ +## Reserve changes + +### Reserves altered + +#### USDC ([0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48](https://etherscan.io/address/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)) + +| description | value before | value after | +| --- | --- | --- | +| aTokenUnderlyingBalance | 376,581,863.4842 USDC [376581863484281] | 376,351,863.4842 USDC [376351863484281] | +| virtualBalance | 376,575,525.5590 USDC [376575525559007] | 376,345,525.5590 USDC [376345525559007] | + + +## Raw diff + +```json +{ + "reserves": { + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": { + "aTokenUnderlyingBalance": { + "from": "376581863484281", + "to": "376351863484281" + }, + "virtualBalance": { + "from": "376575525559007", + "to": "376345525559007" + } + } + } +} +``` \ No newline at end of file diff --git a/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md new file mode 100644 index 000000000..3dc004109 --- /dev/null +++ b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md @@ -0,0 +1,45 @@ +--- +title: "Aave v3.3 Sherlock contest funding" +author: "BGD Labs @bgdlabs" +discussions: "https://governance.aave.com/t/arfc-bgd-aave-v3-3-sherlock-contest/20498" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0x8c04404012d9b74c3e7cebff2ddff3c9d40a280b4cfa7c2fca42be2a59b005ee" +--- + +## Simple Summary + +Proposal for the Aave DAO to host a Sherlock contest for the upcoming [Aave v3.3 upgrade](https://github.com/aave-dao/aave-v3-origin/pull/87), to complement the other security procedures already completed or in progress. + +The total budget will be $230'000, with a $195'000 fixed prize pool and the rest ($35,000) allocated to the platform and judging fees. + +## Motivation + +In the middle of December 2024, we shared with the community a proposal for an Aave v3.3 upgrade, focused on adapting the protocol for the upcoming Umbrella system (a new iteration of the Aave Safety Module), together with doing different improvements mainly on the liquidation engine. + +The reception by the community has been positive, and since then we have been doing internal reviews and different security procedures. In addition to those, and similar to how we proposed back in [Aave v3.1 with Cantina](https://governance.aave.com/t/arfc-bgd-aave-3-1-cantina-competition/17485), we think due to the nature of this upgrade it can be pretty positive to have an open security contest to maximize the numbers of experts looking for any type of problem in the codebase. + +Even if the experience and [outcome](https://github.com/aave-dao/aave-v3-origin/blob/main/audits/02-06-2024-Cantina-contest-AaveV3.1.pdf) with Cantina was pretty positive, part of our security approach is to try different providers, whenever they look solid quality-wise, and/or introduce new mechanics, like in the case of Sherlock. + +## Specification + +The high-level structure of the contest can be found on the Aave governance forum [HERE](https://governance.aave.com/t/arfc-bgd-aave-v3-3-sherlock-contest/20498#p-51858-specification-3). + +This proposal releases the budget required for the contest from the Aave Collector: + +- 30'000 USDC to BGD Labs, to refund the part advanced to Sherlock post-ARFC (transaction [HERE](https://etherscan.io/tx/0x396995576313b6578dad47d0ef7ab454b9840c246262bb812a078a092158b058)). +- 200'000 USDC to Sherlock, to cover the rest of the contest budget. + +| **Entity** | **Recipient Address** | **Value** | +| ---------- | ------------------------------------------ | ------------ | +| BGD Labs | 0xb812d0944f8F581DfAA3a93Dda0d22EcEf51A9CF | 30'000 USDC | +| Sherlock | 0x666B8EbFbF4D5f0CE56962a25635CfF563F13161 | 200'000 USDC | + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x8c04404012d9b74c3e7cebff2ddff3c9d40a280b4cfa7c2fca42be2a59b005ee) +- [Discussion](https://governance.aave.com/t/arfc-bgd-aave-v3-3-sherlock-contest/20498) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding_20250106.s.sol b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding_20250106.s.sol new file mode 100644 index 000000000..1fb2d4bb4 --- /dev/null +++ b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding_20250106.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_AaveV33SherlockContestFunding_20250106} from './AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding_20250106.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/AaveV33SherlockContestFunding_20250106.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_AaveV33SherlockContestFunding_20250106).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/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding_20250106.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_AaveV33SherlockContestFunding_20250106).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md' + ) + ); + } +} diff --git a/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol new file mode 100644 index 000000000..955821dfd --- /dev/null +++ b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol @@ -0,0 +1,41 @@ +// 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 {CollectorUtils, ICollector} from 'aave-helpers/src/CollectorUtils.sol'; +/** + * @title Aave V3.3 Sherlock Contest Funding + * @author BGD Labs @bgdlabs + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x8c04404012d9b74c3e7cebff2ddff3c9d40a280b4cfa7c2fca42be2a59b005ee + * - Discussion: https://governance.aave.com/t/arfc-bgd-aave-v3-3-sherlock-contest/20498 + */ +contract AaveV3Ethereum_AaveV33SherlockContestFunding_20250106 is IProposalGenericExecutor { + using CollectorUtils for ICollector; + + address public constant BGD_RECIPIENT = 0xb812d0944f8F581DfAA3a93Dda0d22EcEf51A9CF; + address public constant SHERLOCK_RECIPIENT = 0x666B8EbFbF4D5f0CE56962a25635CfF563F13161; + + uint256 public constant BGD_USDC_AMOUNT = 30_000e6; + uint256 public constant SHERLOCK_USDC_AMOUNT = 200_000e6; + + function execute() external { + AaveV3Ethereum.COLLECTOR.withdrawFromV3( + CollectorUtils.IOInput({ + pool: address(AaveV3Ethereum.POOL), + underlying: AaveV3EthereumAssets.USDC_UNDERLYING, + amount: BGD_USDC_AMOUNT + }), + BGD_RECIPIENT + ); + + AaveV3Ethereum.COLLECTOR.withdrawFromV3( + CollectorUtils.IOInput({ + pool: address(AaveV3Ethereum.POOL), + underlying: AaveV3EthereumAssets.USDC_UNDERLYING, + amount: SHERLOCK_USDC_AMOUNT + }), + SHERLOCK_RECIPIENT + ); + } +} diff --git a/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol new file mode 100644 index 000000000..8810c3de0 --- /dev/null +++ b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Ethereum_AaveV33SherlockContestFunding_20250106} from './AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; + +/** + * @dev Test for AaveV3Ethereum_AaveV33SherlockContestFunding_20250106 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol -vv + */ +contract AaveV3Ethereum_AaveV33SherlockContestFunding_20250106_Test is ProtocolV3TestBase { + AaveV3Ethereum_AaveV33SherlockContestFunding_20250106 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21566088); + proposal = new AaveV3Ethereum_AaveV33SherlockContestFunding_20250106(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_AaveV33SherlockContestFunding_20250106', + AaveV3Ethereum.POOL, + address(proposal) + ); + } + + function test_consistentBalances() public { + uint256 collectorAUSDCBalanceBefore = IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf( + address(AaveV3Ethereum.COLLECTOR) + ); + uint256 bgdUSDCBalanceBefore = IERC20(AaveV3EthereumAssets.USDC_UNDERLYING).balanceOf( + proposal.BGD_RECIPIENT() + ); + uint256 sherlockUSDCBalanceBefore = IERC20(AaveV3EthereumAssets.USDC_UNDERLYING).balanceOf( + proposal.SHERLOCK_RECIPIENT() + ); + + executePayload(vm, address(proposal)); + + uint256 collectorAUSDCBalanceAfter = IERC20(AaveV3EthereumAssets.USDC_A_TOKEN).balanceOf( + address(AaveV3Ethereum.COLLECTOR) + ); + uint256 bgdUSDCBalanceAfter = IERC20(AaveV3EthereumAssets.USDC_UNDERLYING).balanceOf( + proposal.BGD_RECIPIENT() + ); + uint256 sherlockUSDCBalanceAfter = IERC20(AaveV3EthereumAssets.USDC_UNDERLYING).balanceOf( + proposal.SHERLOCK_RECIPIENT() + ); + + uint256 totalAmount = proposal.BGD_USDC_AMOUNT() + proposal.SHERLOCK_USDC_AMOUNT(); + + assertApproxEqAbs(collectorAUSDCBalanceAfter, collectorAUSDCBalanceBefore - totalAmount, 1); // due to interest accrued + + assertEq(bgdUSDCBalanceAfter, bgdUSDCBalanceBefore + proposal.BGD_USDC_AMOUNT()); + assertEq(sherlockUSDCBalanceAfter, sherlockUSDCBalanceBefore + proposal.SHERLOCK_USDC_AMOUNT()); + } +} diff --git a/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/config.ts b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/config.ts new file mode 100644 index 000000000..04d519c0e --- /dev/null +++ b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/config.ts @@ -0,0 +1,15 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + author: 'BGD Labs @bgdlabs', + pools: ['AaveV3Ethereum'], + title: 'Aave v3.3 Sherlock contest funding', + shortName: 'AaveV33SherlockContestFunding', + date: '20250106', + discussion: 'https://governance.aave.com/t/arfc-bgd-aave-v3-3-sherlock-contest/20498', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x8c04404012d9b74c3e7cebff2ddff3c9d40a280b4cfa7c2fca42be2a59b005ee', + votingNetwork: 'POLYGON', + }, + poolOptions: {AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 21566088}}}, +}; From 402984938a0f2a5153689492b0d7ec80a4527e64 Mon Sep 17 00:00:00 2001 From: Cache bot Date: Tue, 7 Jan 2025 18:10:52 +0000 Subject: [PATCH 09/21] fix(cache): automated cache update [skip ci] --- .../AaveV33SherlockContestFunding.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md index 3dc004109..dc5b78349 100644 --- a/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md +++ b/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV33SherlockContestFunding.md @@ -35,8 +35,8 @@ This proposal releases the budget required for the contest from the Aave Collect ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/29ee27b76f4a86e9f213ed430f4df90547e74c39/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/29ee27b76f4a86e9f213ed430f4df90547e74c39/src/20250106_AaveV3Ethereum_AaveV33SherlockContestFunding/AaveV3Ethereum_AaveV33SherlockContestFunding_20250106.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x8c04404012d9b74c3e7cebff2ddff3c9d40a280b4cfa7c2fca42be2a59b005ee) - [Discussion](https://governance.aave.com/t/arfc-bgd-aave-v3-3-sherlock-contest/20498) From 57aaaa1bfa341a98ac5fbb549aa16941eef69ed7 Mon Sep 17 00:00:00 2001 From: Luigy-Lemon Date: Thu, 9 Jan 2025 11:57:09 +0000 Subject: [PATCH 10/21] Karpatkey-Gho-Growth (#559) * feat: karpatkey funding proposal - gho growth * feat: update md file and merge main * chore: remove unused dependencies * chore: update aave-helper * Update src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md Co-authored-by: Harsh Pandey --------- Co-authored-by: Harsh Pandey --- ...ereum_karpatkeyGhoGrowth_20241231_after.md | 5 + ...V3Ethereum_karpatkeyGhoGrowth_20241231.sol | 47 ++++++++ ...Ethereum_karpatkeyGhoGrowth_20241231.t.sol | 103 ++++++++++++++++++ .../config.ts | 16 +++ .../karpatkeyGhoGrowth.md | 48 ++++++++ .../karpatkeyGhoGrowth_20241231.s.sol | 60 ++++++++++ 6 files changed, 279 insertions(+) create mode 100644 diffs/AaveV3Ethereum_karpatkeyGhoGrowth_20241231_before_AaveV3Ethereum_karpatkeyGhoGrowth_20241231_after.md create mode 100644 src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol create mode 100644 src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol create mode 100644 src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/config.ts create mode 100644 src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md create mode 100644 src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth_20241231.s.sol diff --git a/diffs/AaveV3Ethereum_karpatkeyGhoGrowth_20241231_before_AaveV3Ethereum_karpatkeyGhoGrowth_20241231_after.md b/diffs/AaveV3Ethereum_karpatkeyGhoGrowth_20241231_before_AaveV3Ethereum_karpatkeyGhoGrowth_20241231_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Ethereum_karpatkeyGhoGrowth_20241231_before_AaveV3Ethereum_karpatkeyGhoGrowth_20241231_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol new file mode 100644 index 000000000..7f6d207a9 --- /dev/null +++ b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {CollectorUtils, ICollector} from 'aave-helpers/src/CollectorUtils.sol'; +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3EthereumLido, AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol'; +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; + +/** + * @title karpatkey Gho Growth Service Provider + * @author karpatkey + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x87585d9dcb104d2946ca2def6bcf57708480fafc5e310de4850dc2fbe1820893 + * - Discussion: https://governance.aave.com/t/arfc-karpatkey-as-gho-growth-service-provider/20206 + */ +contract AaveV3Ethereum_karpatkeyGhoGrowth_20241231 is IProposalGenericExecutor { + using CollectorUtils for ICollector; + + uint256 public constant KARPATKEY_STREAM_AMOUNT = 250_000e18; + address public constant KARPATKEY_SAFE = 0x58e6c7ab55Aa9012eAccA16d1ED4c15795669E1C; + uint256 public constant STREAM_DURATION = 180 days; + uint256 public constant STREAM_START_TIME = 1734912000; // Sun Dec 23 2024 12:00 GMT+0000 + uint256 public constant ACTUAL_STREAM_AMOUNT = + (KARPATKEY_STREAM_AMOUNT / STREAM_DURATION) * STREAM_DURATION; + + function execute() external override { + uint256 backDatedAmount = (ACTUAL_STREAM_AMOUNT * (block.timestamp - STREAM_START_TIME)) / + STREAM_DURATION; + + // transfer backend amount + AaveV3Ethereum.COLLECTOR.transfer( + AaveV3EthereumLidoAssets.GHO_A_TOKEN, + KARPATKEY_SAFE, + backDatedAmount + ); + + // stream + AaveV3Ethereum.COLLECTOR.stream( + CollectorUtils.CreateStreamInput({ + underlying: AaveV3EthereumLidoAssets.GHO_A_TOKEN, + receiver: KARPATKEY_SAFE, + amount: ACTUAL_STREAM_AMOUNT - backDatedAmount, + start: block.timestamp, + duration: STREAM_DURATION + STREAM_START_TIME - block.timestamp + }) + ); + } +} diff --git a/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol new file mode 100644 index 000000000..a75c2aa03 --- /dev/null +++ b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3EthereumLido, AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Ethereum_karpatkeyGhoGrowth_20241231} from './AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol'; + +/** + * @dev Test for AaveV3Ethereum_karpatkeyGhoGrowth_20241231 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol -vv + */ +contract AaveV3Ethereum_karpatkeyGhoGrowth_20241231_Test is ProtocolV3TestBase { + AaveV3Ethereum_karpatkeyGhoGrowth_20241231 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21524445); + proposal = new AaveV3Ethereum_karpatkeyGhoGrowth_20241231(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_karpatkeyGhoGrowth_20241231', + AaveV3Ethereum.POOL, + address(proposal) + ); + } + + function test_stream() public { + uint256 backDatedAmount = (proposal.ACTUAL_STREAM_AMOUNT() * + (block.timestamp - proposal.STREAM_START_TIME())) / proposal.STREAM_DURATION(); + + uint256 nextCollectorStreamID = AaveV3Ethereum.COLLECTOR.getNextStreamId(); + + executePayload(vm, address(proposal)); + + ( + address sender, + address recipient, + uint256 deposit, + address tokenAddress, + uint256 startTime, + uint256 stopTime, + uint256 remainingBalance, + + ) = AaveV3Ethereum.COLLECTOR.getStream(nextCollectorStreamID); + + assertEq(sender, address(AaveV3Ethereum.COLLECTOR)); + assertEq(recipient, proposal.KARPATKEY_SAFE()); + assertEq(deposit, proposal.ACTUAL_STREAM_AMOUNT() - backDatedAmount); + assertEq(tokenAddress, AaveV3EthereumLidoAssets.GHO_A_TOKEN); + assertEq(startTime, block.timestamp); + assertEq(stopTime, proposal.STREAM_START_TIME() + proposal.STREAM_DURATION()); + assertEq(remainingBalance, proposal.ACTUAL_STREAM_AMOUNT() - backDatedAmount); + + // Can withdraw during stream + vm.warp(block.timestamp + 30 days); + + uint256 collectorGhoBalanceBefore = IERC20(AaveV3EthereumLidoAssets.GHO_A_TOKEN).balanceOf( + address(AaveV3Ethereum.COLLECTOR) + ); + uint256 receiverGhoBalanceBefore = IERC20(AaveV3EthereumLidoAssets.GHO_A_TOKEN).balanceOf( + proposal.KARPATKEY_SAFE() + ); + + vm.startPrank(proposal.KARPATKEY_SAFE()); + AaveV3Ethereum.COLLECTOR.withdrawFromStream( + nextCollectorStreamID, + proposal.ACTUAL_STREAM_AMOUNT() / 9 + ); + vm.stopPrank(); + + assertGt( + IERC20(AaveV3EthereumLidoAssets.GHO_A_TOKEN).balanceOf(proposal.KARPATKEY_SAFE()), + receiverGhoBalanceBefore + ); + + assertLt( + IERC20(AaveV3EthereumLidoAssets.GHO_A_TOKEN).balanceOf(address(AaveV3Ethereum.COLLECTOR)), + collectorGhoBalanceBefore + ); + + // Can withdraw post stream all remaining funds + vm.warp(block.timestamp + proposal.STREAM_DURATION()); + + (, , , , , , uint256 remaining, ) = AaveV3Ethereum.COLLECTOR.getStream(nextCollectorStreamID); + + vm.startPrank(proposal.KARPATKEY_SAFE()); + AaveV3Ethereum.COLLECTOR.withdrawFromStream(nextCollectorStreamID, remaining); + vm.stopPrank(); + + assertEq( + IERC20(AaveV3EthereumLidoAssets.GHO_A_TOKEN).balanceOf(proposal.KARPATKEY_SAFE()), + receiverGhoBalanceBefore + proposal.ACTUAL_STREAM_AMOUNT() - backDatedAmount + ); + } +} diff --git a/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/config.ts b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/config.ts new file mode 100644 index 000000000..9d3c60ec1 --- /dev/null +++ b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/config.ts @@ -0,0 +1,16 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3Ethereum'], + title: 'karpatkey Gho Growth Service Provider', + shortName: 'karpatkeyGhoGrowthServiceProvider', + date: '20241231', + author: 'karpatkey', + discussion: + ' https://governance.aave.com/t/arfc-karpatkey-as-gho-growth-service-provider/20206', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x87585d9dcb104d2946ca2def6bcf57708480fafc5e310de4850dc2fbe1820893', + votingNetwork: 'POLYGON', + }, + poolOptions: {AaveV3Ethereum: {configs: {}, cache: {blockNumber: 21524445}}}, +}; diff --git a/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md new file mode 100644 index 000000000..ae534551b --- /dev/null +++ b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md @@ -0,0 +1,48 @@ +--- +title: "Funding Proposal: karpatkey as GHO Growth Service Provider" +author: "karpatkey" +discussions: https://governance.aave.com/t/arfc-karpatkey-as-gho-growth-service-provider/20206 +snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x87585d9dcb104d2946ca2def6bcf57708480fafc5e310de4850dc2fbe1820893 +--- + +## Simple Summary + +The AIP proposes appointing karpatkey as a service provider dedicated to drive the growth of GHO for the following 6 months, with an initial focus on opportunities within the Gnosis Chain while also expanding efforts to other chains. + +## Motivation + +Over the past 12 months, karpatkey has been pleased to have supported the AaveDAO in improving treasury management practices and GHO adoption (see Phase 1 and Phase 2 for more details). These efforts have been a significant success, and will continue, with the deployment of the Finance Steward upcoming features. That said, by focusing on increasing GHO’s adoption, we believe karpatkey can provide more value to the Aave ecosystem. + +The stablecoin ecosystem is one of the most dynamic and competitive sectors in the DeFi landscape, and the accelerating adoption of GHO represents a unique opportunity for Aave DAO to solidify its position as a leader in this space. Since its deployment over a year ago, GHO achieved a notable milestone of a 180M supply across mainnet and Arbitrum, market depth in most reputable DEXes with total liquidity ranging between $30M and $50M. This success is a testament to the strength of Aave’s products, user base and brand. + +While GHO’s initial growth has been remarkable, the stablecoin market is evolving rapidly. The demand for reliable, decentralised stablecoins continues to grow, and GHO is well-positioned to capture this market—provided we continue to expand its utility, adoption, and ecosystem integrations proactively. + +karpatkey has the tools and resources to capitalise on the range of opportunities GHO presents. With a proven track record in DeFi innovation and strong partnerships with other DAOs, we are excited to present our new 6-month proposal to Aave DAO, focused on the areas where we can contribute most. + +To drive proactive growth and expansion of GHO across multiple blockchains, we are proposing a phased mandate that includes tailored strategies for each ecosystem, with the initial phase prioritizing the deployment and adoption of GHO on the Gnosis Chain. With an Aave instance holding $100M, Gnosis Chain currently has a total value locked of $410M and a thriving environment of stablecoins, which includes Gnosis Pay, Monerium’s e-money EURe and other RWAs. We plan to focus primarily on opportunities where both AaveDAO and GnosisDAO can mutually benefit from a vibrant GHO ecosystem on Gnosis Chain, taking advantage of karpatkey’s unique position supporting both the DeFi ecosystems of both DAOs. + +## Terms + +- 6-month engagement, December 23th 2024 to June 21th 2025; +- $250k, streamed linearly throughout the engagement; + +## Specification + +Create the following stream allowing TokenLogic to withdraw aEthLidoGHO from the Prime instance. + +- Recipient: karpatkey +- Stream: 250K aEthLidoGHO over 180 days +- Address: `0x58e6c7ab55aa9012eacca16d1ed4c15795669e1c` + +The stream shall commence the next block from when the previous stream finishes. + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol) + [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x87585d9dcb104d2946ca2def6bcf57708480fafc5e310de4850dc2fbe1820893) +- [Discussion](https://governance.aave.com/t/arfc-karpatkey-as-gho-growth-service-provider/20206) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth_20241231.s.sol b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth_20241231.s.sol new file mode 100644 index 000000000..f9544095c --- /dev/null +++ b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth_20241231.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_karpatkeyGhoGrowth_20241231} from './AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_karpatkeyGhoGrowth_20241231).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/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.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_karpatkeyGhoGrowth_20241231).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md' + ) + ); + } +} From 0fe7c09340e03daa54d75384041605cf53e4ad2a Mon Sep 17 00:00:00 2001 From: Cache bot Date: Thu, 9 Jan 2025 11:57:33 +0000 Subject: [PATCH 11/21] fix(cache): automated cache update [skip ci] --- .../karpatkeyGhoGrowth.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md index ae534551b..cf1afba78 100644 --- a/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md +++ b/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/karpatkeyGhoGrowth.md @@ -38,8 +38,8 @@ The stream shall commence the next block from when the previous stream finishes. ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/57aaaa1bfa341a98ac5fbb549aa16941eef69ed7/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/57aaaa1bfa341a98ac5fbb549aa16941eef69ed7/src/20241231_AaveV3Ethereum_karpatkeyGhoGrowth/AaveV3Ethereum_karpatkeyGhoGrowth_20241231.t.sol) [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x87585d9dcb104d2946ca2def6bcf57708480fafc5e310de4850dc2fbe1820893) - [Discussion](https://governance.aave.com/t/arfc-karpatkey-as-gho-growth-service-provider/20206) From 612e087e1b3fff317fd444905d17bbf0e0abcb37 Mon Sep 17 00:00:00 2001 From: Alice <121383428+Rozengarden@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:32:07 +0100 Subject: [PATCH 12/21] [Aavechan] ezeth on arbitrum and base (#561) --- ...ArbitrumAndBaseInstances_20241221_after.md | 172 ++++++++++++++++++ ...ArbitrumAndBaseInstances_20241221_after.md | 168 +++++++++++++++++ ...ETHToArbitrumAndBaseInstances_20241221.sol | 133 ++++++++++++++ ...HToArbitrumAndBaseInstances_20241221.t.sol | 58 ++++++ ...ETHToArbitrumAndBaseInstances_20241221.sol | 125 +++++++++++++ ...HToArbitrumAndBaseInstances_20241221.t.sol | 58 ++++++ .../OnboardEzETHToArbitrumAndBaseInstances.md | 87 +++++++++ ...HToArbitrumAndBaseInstances_20241221.s.sol | 90 +++++++++ .../config.ts | 108 +++++++++++ 9 files changed, 999 insertions(+) create mode 100644 diffs/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md create mode 100644 diffs/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md create mode 100644 src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol create mode 100644 src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol create mode 100644 src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol create mode 100644 src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol create mode 100644 src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md create mode 100644 src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol create mode 100644 src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/config.ts diff --git a/diffs/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md b/diffs/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md new file mode 100644 index 000000000..ca012223a --- /dev/null +++ b/diffs/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md @@ -0,0 +1,172 @@ +## Reserve changes + +### Reserves added + +#### ezETH ([0x2416092f143378750bb29b79eD961ab195CcEea5](https://arbiscan.io/address/0x2416092f143378750bb29b79eD961ab195CcEea5)) + +| description | value | +| --- | --- | +| decimals | 18 | +| isActive | true | +| isFrozen | false | +| supplyCap | 1,750 ezETH | +| borrowCap | 1 ezETH | +| debtCeiling | 0 $ [0] | +| isSiloed | false | +| isFlashloanable | true | +| oracle | [0x8Ed37B72300683c0482A595bfa80fFb793874b15](https://arbiscan.io/address/0x8Ed37B72300683c0482A595bfa80fFb793874b15) | +| oracleDecimals | 8 | +| oracleDescription | Capped ezETH / ETH / USD | +| oracleLatestAnswer | 3787.98660028 | +| usageAsCollateralEnabled | true | +| ltv | 0.05 % [5] | +| liquidationThreshold | 0.1 % [10] | +| liquidationBonus | 7.5 % | +| liquidationProtocolFee | 10 % [1000] | +| reserveFactor | 15 % [1500] | +| aToken | [0xEA1132120ddcDDA2F119e99Fa7A27a0d036F7Ac9](https://arbiscan.io/address/0xEA1132120ddcDDA2F119e99Fa7A27a0d036F7Ac9) | +| aTokenImpl | [0x1Be1798b70aEe431c2986f7ff48d9D1fa350786a](https://arbiscan.io/address/0x1Be1798b70aEe431c2986f7ff48d9D1fa350786a) | +| variableDebtToken | [0x1fFD28689DA7d0148ff0fCB669e9f9f0Fc13a219](https://arbiscan.io/address/0x1fFD28689DA7d0148ff0fCB669e9f9f0Fc13a219) | +| variableDebtTokenImpl | [0x5E76E98E0963EcDC6A065d1435F84065b7523f39](https://arbiscan.io/address/0x5E76E98E0963EcDC6A065d1435F84065b7523f39) | +| borrowingEnabled | false | +| isBorrowableInIsolation | false | +| interestRateStrategy | [0x429F16dBA3B9e1900087Cbaa7b50D38Bc60fB73F](https://arbiscan.io/address/0x429F16dBA3B9e1900087Cbaa7b50D38Bc60fB73F) | +| aTokenName | Aave Arbitrum ezETH | +| aTokenSymbol | aArbezETH | +| aTokenUnderlyingBalance | 0.03 ezETH [30000000000000000] | +| id | 17 | +| isPaused | false | +| variableDebtTokenName | Aave Arbitrum Variable Debt ezETH | +| variableDebtTokenSymbol | variableDebtArbezETH | +| virtualAccountingActive | true | +| virtualBalance | 0.03 ezETH [30000000000000000] | +| 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: Stablecoins(id: 1) + + + +### EMode: ETH correlated(id: 2) + + + +### EMode: ezETH wstETH(id: 3) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | ezETH wstETH | +| eMode.ltv | - | 93 % | +| eMode.liquidationThreshold | - | 95 % | +| eMode.liquidationBonus | - | 1 % | +| eMode.borrowableBitmap | - | wstETH | +| eMode.collateralBitmap | - | ezETH | + + +### EMode: ezETH Stablecoins(id: 4) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | ezETH Stablecoins | +| eMode.ltv | - | 72 % | +| eMode.liquidationThreshold | - | 75 % | +| eMode.liquidationBonus | - | 7.5 % | +| eMode.borrowableBitmap | - | USDT, USDC | +| eMode.collateralBitmap | - | ezETH | + + +## Raw diff + +```json +{ + "eModes": { + "3": { + "from": null, + "to": { + "borrowableBitmap": "256", + "collateralBitmap": "131072", + "eModeCategory": 3, + "label": "ezETH wstETH", + "liquidationBonus": 10100, + "liquidationThreshold": 9500, + "ltv": 9300 + } + }, + "4": { + "from": null, + "to": { + "borrowableBitmap": "4128", + "collateralBitmap": "131072", + "eModeCategory": 4, + "label": "ezETH Stablecoins", + "liquidationBonus": 10750, + "liquidationThreshold": 7500, + "ltv": 7200 + } + } + }, + "reserves": { + "0x2416092f143378750bb29b79eD961ab195CcEea5": { + "from": null, + "to": { + "aToken": "0xEA1132120ddcDDA2F119e99Fa7A27a0d036F7Ac9", + "aTokenImpl": "0x1Be1798b70aEe431c2986f7ff48d9D1fa350786a", + "aTokenName": "Aave Arbitrum ezETH", + "aTokenSymbol": "aArbezETH", + "aTokenUnderlyingBalance": "30000000000000000", + "borrowCap": 1, + "borrowingEnabled": false, + "debtCeiling": 0, + "decimals": 18, + "id": 17, + "interestRateStrategy": "0x429F16dBA3B9e1900087Cbaa7b50D38Bc60fB73F", + "isActive": true, + "isBorrowableInIsolation": false, + "isFlashloanable": true, + "isFrozen": false, + "isPaused": false, + "isSiloed": false, + "liquidationBonus": 10750, + "liquidationProtocolFee": 1000, + "liquidationThreshold": 10, + "ltv": 5, + "oracle": "0x8Ed37B72300683c0482A595bfa80fFb793874b15", + "oracleDecimals": 8, + "oracleDescription": "Capped ezETH / ETH / USD", + "oracleLatestAnswer": "378798660028", + "reserveFactor": 1500, + "supplyCap": 1750, + "symbol": "ezETH", + "underlying": "0x2416092f143378750bb29b79eD961ab195CcEea5", + "usageAsCollateralEnabled": true, + "variableDebtToken": "0x1fFD28689DA7d0148ff0fCB669e9f9f0Fc13a219", + "variableDebtTokenImpl": "0x5E76E98E0963EcDC6A065d1435F84065b7523f39", + "variableDebtTokenName": "Aave Arbitrum Variable Debt ezETH", + "variableDebtTokenSymbol": "variableDebtArbezETH", + "virtualAccountingActive": true, + "virtualBalance": "30000000000000000" + } + } + }, + "strategies": { + "0x2416092f143378750bb29b79eD961ab195CcEea5": { + "from": null, + "to": { + "address": "0x429F16dBA3B9e1900087Cbaa7b50D38Bc60fB73F", + "baseVariableBorrowRate": "0", + "maxVariableBorrowRate": "3070000000000000000000000000", + "optimalUsageRatio": "450000000000000000000000000", + "variableRateSlope1": "70000000000000000000000000", + "variableRateSlope2": "3000000000000000000000000000" + } + } + } +} +``` \ No newline at end of file diff --git a/diffs/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md b/diffs/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md new file mode 100644 index 000000000..6513062b4 --- /dev/null +++ b/diffs/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_before_AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_after.md @@ -0,0 +1,168 @@ +## Reserve changes + +### Reserves added + +#### ezETH ([0x2416092f143378750bb29b79eD961ab195CcEea5](https://basescan.org/address/0x2416092f143378750bb29b79eD961ab195CcEea5)) + +| description | value | +| --- | --- | +| decimals | 18 | +| isActive | true | +| isFrozen | false | +| supplyCap | 1,200 ezETH | +| borrowCap | 1 ezETH | +| debtCeiling | 0 $ [0] | +| isSiloed | false | +| isFlashloanable | true | +| oracle | [0x438e24f5FCDC1A66ecb25D19B5543e0Cb91A44D4](https://basescan.org/address/0x438e24f5FCDC1A66ecb25D19B5543e0Cb91A44D4) | +| oracleDecimals | 8 | +| oracleDescription | Capped ezETH / ETH / USD | +| oracleLatestAnswer | 3785.26748692 | +| usageAsCollateralEnabled | true | +| ltv | 0.05 % [5] | +| liquidationThreshold | 0.1 % [10] | +| liquidationBonus | 7.5 % | +| liquidationProtocolFee | 10 % [1000] | +| reserveFactor | 15 % [1500] | +| aToken | [0xDD5745756C2de109183c6B5bB886F9207bEF114D](https://basescan.org/address/0xDD5745756C2de109183c6B5bB886F9207bEF114D) | +| aTokenImpl | [0x98F409Fc4A42F34AE3c326c7f48ED01ae8cAeC69](https://basescan.org/address/0x98F409Fc4A42F34AE3c326c7f48ED01ae8cAeC69) | +| variableDebtToken | [0xbc4f5631f2843488792e4F1660d0A51Ba489bdBd](https://basescan.org/address/0xbc4f5631f2843488792e4F1660d0A51Ba489bdBd) | +| variableDebtTokenImpl | [0x2425A746911128c2eAA7bEBDc9Bc452eE52208a1](https://basescan.org/address/0x2425A746911128c2eAA7bEBDc9Bc452eE52208a1) | +| borrowingEnabled | false | +| isBorrowableInIsolation | false | +| interestRateStrategy | [0x86AB1C62A8bf868E1b3E1ab87d587Aba6fbCbDC5](https://basescan.org/address/0x86AB1C62A8bf868E1b3E1ab87d587Aba6fbCbDC5) | +| aTokenName | Aave Base ezETH | +| aTokenSymbol | aBasezETH | +| aTokenUnderlyingBalance | 0.03 ezETH [30000000000000000] | +| id | 7 | +| isPaused | false | +| variableDebtTokenName | Aave Base Variable Debt ezETH | +| variableDebtTokenSymbol | variableDebtBasezETH | +| virtualAccountingActive | true | +| virtualBalance | 0.03 ezETH [30000000000000000] | +| 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) + + + +### EMode: ezETH wstETH(id: 2) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | ezETH wstETH | +| eMode.ltv | - | 93 % | +| eMode.liquidationThreshold | - | 95 % | +| eMode.liquidationBonus | - | 1 % | +| eMode.borrowableBitmap | - | wstETH | +| eMode.collateralBitmap | - | ezETH | + + +### EMode: ezETH Stablecoins(id: 3) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | ezETH Stablecoins | +| eMode.ltv | - | 72 % | +| eMode.liquidationThreshold | - | 75 % | +| eMode.liquidationBonus | - | 7.5 % | +| eMode.borrowableBitmap | - | USDC | +| eMode.collateralBitmap | - | ezETH | + + +## Raw diff + +```json +{ + "eModes": { + "2": { + "from": null, + "to": { + "borrowableBitmap": "8", + "collateralBitmap": "128", + "eModeCategory": 2, + "label": "ezETH wstETH", + "liquidationBonus": 10100, + "liquidationThreshold": 9500, + "ltv": 9300 + } + }, + "3": { + "from": null, + "to": { + "borrowableBitmap": "16", + "collateralBitmap": "128", + "eModeCategory": 3, + "label": "ezETH Stablecoins", + "liquidationBonus": 10750, + "liquidationThreshold": 7500, + "ltv": 7200 + } + } + }, + "reserves": { + "0x2416092f143378750bb29b79eD961ab195CcEea5": { + "from": null, + "to": { + "aToken": "0xDD5745756C2de109183c6B5bB886F9207bEF114D", + "aTokenImpl": "0x98F409Fc4A42F34AE3c326c7f48ED01ae8cAeC69", + "aTokenName": "Aave Base ezETH", + "aTokenSymbol": "aBasezETH", + "aTokenUnderlyingBalance": "30000000000000000", + "borrowCap": 1, + "borrowingEnabled": false, + "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": 10, + "ltv": 5, + "oracle": "0x438e24f5FCDC1A66ecb25D19B5543e0Cb91A44D4", + "oracleDecimals": 8, + "oracleDescription": "Capped ezETH / ETH / USD", + "oracleLatestAnswer": "378526748692", + "reserveFactor": 1500, + "supplyCap": 1200, + "symbol": "ezETH", + "underlying": "0x2416092f143378750bb29b79eD961ab195CcEea5", + "usageAsCollateralEnabled": true, + "variableDebtToken": "0xbc4f5631f2843488792e4F1660d0A51Ba489bdBd", + "variableDebtTokenImpl": "0x2425A746911128c2eAA7bEBDc9Bc452eE52208a1", + "variableDebtTokenName": "Aave Base Variable Debt ezETH", + "variableDebtTokenSymbol": "variableDebtBasezETH", + "virtualAccountingActive": true, + "virtualBalance": "30000000000000000" + } + } + }, + "strategies": { + "0x2416092f143378750bb29b79eD961ab195CcEea5": { + "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/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol new file mode 100644 index 000000000..58feeff40 --- /dev/null +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Arbitrum, AaveV3ArbitrumAssets, AaveV3ArbitrumEModes} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {AaveV3PayloadArbitrum} from 'aave-helpers/src/v3-config-engine/AaveV3PayloadArbitrum.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 Arbitrum and Base Instances + * @author Aave Chan Initiative + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0xf9fa710414237636cba11187111773fac04f74eb1a562d2b50fca86cb72a778e + * - Discussion: https://governance.aave.com/t/arfc-onboard-ezeth-to-arbitrum-and-base-instances/19622 + */ +contract AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221 is AaveV3PayloadArbitrum { + using SafeERC20 for IERC20; + + address public constant ezETH = 0x2416092f143378750bb29b79eD961ab195CcEea5; + uint256 public constant ezETH_SEED_AMOUNT = 3e16; + address public constant ezETH_LM_ADMIN = 0xac140648435d03f784879cd789130F22Ef588Fcd; + + function _postExecute() internal override { + IERC20(ezETH).forceApprove(address(AaveV3Arbitrum.POOL), ezETH_SEED_AMOUNT); + AaveV3Arbitrum.POOL.supply(ezETH, ezETH_SEED_AMOUNT, address(AaveV3Arbitrum.COLLECTOR), 0); + + (address aezETH, , ) = AaveV3Arbitrum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + ezETH + ); + IEmissionManager(AaveV3Arbitrum.EMISSION_MANAGER).setEmissionAdmin(ezETH, ezETH_LM_ADMIN); + IEmissionManager(AaveV3Arbitrum.EMISSION_MANAGER).setEmissionAdmin(aezETH, ezETH_LM_ADMIN); + } + + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](2); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 3, + ltv: 93_00, + liqThreshold: 95_00, + liqBonus: 1_00, + label: 'ezETH wstETH' + }); + + eModeUpdates[1] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 4, + ltv: 72_00, + liqThreshold: 75_00, + liqBonus: 7_50, + label: 'ezETH Stablecoins' + }); + + 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: 3, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: ezETH, + eModeCategory: 4, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + assetEModeUpdates[2] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3ArbitrumAssets.wstETH_UNDERLYING, + eModeCategory: 3, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + assetEModeUpdates[3] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3ArbitrumAssets.USDT_UNDERLYING, + eModeCategory: 4, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + assetEModeUpdates[4] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3ArbitrumAssets.USDCn_UNDERLYING, + eModeCategory: 4, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + + return assetEModeUpdates; + } + 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: 0x8Ed37B72300683c0482A595bfa80fFb793874b15, + enabledToBorrow: EngineFlags.DISABLED, + borrowableInIsolation: EngineFlags.DISABLED, + withSiloedBorrowing: EngineFlags.DISABLED, + flashloanable: EngineFlags.ENABLED, + ltv: 5, + liqThreshold: 10, + liqBonus: 7_50, + reserveFactor: 15_00, + supplyCap: 1_750, + borrowCap: 1, + 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/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol new file mode 100644 index 000000000..366e59e33 --- /dev/null +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221} from './AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol'; + +/** + * @dev Test for AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221 + * command: FOUNDRY_PROFILE=arbitrum forge test --match-path=src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol -vv + */ +contract AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221_Test is ProtocolV3TestBase { + AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('arbitrum'), 292661143); + proposal = new AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221', + AaveV3Arbitrum.POOL, + address(proposal) + ); + } + + function test_collectorHasezETHFunds() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aTokenAddress, , ) = AaveV3Arbitrum + .AAVE_PROTOCOL_DATA_PROVIDER + .getReserveTokensAddresses(proposal.ezETH()); + assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3Arbitrum.COLLECTOR)), 3 * 10 ** 16); + } + + function test_ezETHAdmin() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aezETH, , ) = AaveV3Arbitrum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.ezETH() + ); + assertEq( + IEmissionManager(AaveV3Arbitrum.EMISSION_MANAGER).getEmissionAdmin(proposal.ezETH()), + proposal.ezETH_LM_ADMIN() + ); + assertEq( + IEmissionManager(AaveV3Arbitrum.EMISSION_MANAGER).getEmissionAdmin(aezETH), + proposal.ezETH_LM_ADMIN() + ); + } +} diff --git a/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol new file mode 100644 index 000000000..faf7314db --- /dev/null +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Base, AaveV3BaseAssets, AaveV3BaseEModes} from 'aave-address-book/AaveV3Base.sol'; +import {AaveV3PayloadBase} from 'aave-helpers/src/v3-config-engine/AaveV3PayloadBase.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 Arbitrum and Base Instances + * @author Aave Chan Initiative + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0xf9fa710414237636cba11187111773fac04f74eb1a562d2b50fca86cb72a778e + * - Discussion: https://governance.aave.com/t/arfc-onboard-ezeth-to-arbitrum-and-base-instances/19622 + */ +contract AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221 is AaveV3PayloadBase { + using SafeERC20 for IERC20; + + address public constant ezETH = 0x2416092f143378750bb29b79eD961ab195CcEea5; + uint256 public constant ezETH_SEED_AMOUNT = 3e16; + address public constant ezETH_LM_ADMIN = 0xac140648435d03f784879cd789130F22Ef588Fcd; + + function _postExecute() internal override { + IERC20(ezETH).forceApprove(address(AaveV3Base.POOL), ezETH_SEED_AMOUNT); + AaveV3Base.POOL.supply(ezETH, ezETH_SEED_AMOUNT, address(AaveV3Base.COLLECTOR), 0); + + (address aezETH, , ) = AaveV3Base.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(ezETH); + IEmissionManager(AaveV3Base.EMISSION_MANAGER).setEmissionAdmin(ezETH, ezETH_LM_ADMIN); + IEmissionManager(AaveV3Base.EMISSION_MANAGER).setEmissionAdmin(aezETH, ezETH_LM_ADMIN); + } + + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](2); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 2, + ltv: 93_00, + liqThreshold: 95_00, + liqBonus: 1_00, + label: 'ezETH wstETH' + }); + + eModeUpdates[1] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 3, + ltv: 72_00, + liqThreshold: 75_00, + liqBonus: 7_50, + label: 'ezETH Stablecoins' + }); + + return eModeUpdates; + } + function assetsEModeUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.AssetEModeUpdate[] memory) + { + IAaveV3ConfigEngine.AssetEModeUpdate[] + memory assetEModeUpdates = new IAaveV3ConfigEngine.AssetEModeUpdate[](4); + + assetEModeUpdates[0] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3BaseAssets.wstETH_UNDERLYING, + eModeCategory: 2, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: ezETH, + eModeCategory: 3, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + assetEModeUpdates[2] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: ezETH, + eModeCategory: 2, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + assetEModeUpdates[3] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3BaseAssets.USDC_UNDERLYING, + eModeCategory: 3, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + + return assetEModeUpdates; + } + 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: 0x438e24f5FCDC1A66ecb25D19B5543e0Cb91A44D4, + enabledToBorrow: EngineFlags.DISABLED, + borrowableInIsolation: EngineFlags.DISABLED, + withSiloedBorrowing: EngineFlags.DISABLED, + flashloanable: EngineFlags.ENABLED, + ltv: 5, + liqThreshold: 10, + liqBonus: 7_50, + reserveFactor: 15_00, + supplyCap: 1_200, + borrowCap: 1, + 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/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol new file mode 100644 index 000000000..3dd1ef5a8 --- /dev/null +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3Base} from 'aave-address-book/AaveV3Base.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221} from './AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol'; + +/** + * @dev Test for AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221 + * command: FOUNDRY_PROFILE=base forge test --match-path=src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol -vv + */ +contract AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221_Test is ProtocolV3TestBase { + AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('base'), 24698319); + proposal = new AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221', + AaveV3Base.POOL, + address(proposal) + ); + } + + function test_collectorHasezETHFunds() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aTokenAddress, , ) = AaveV3Base.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.ezETH() + ); + assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3Base.COLLECTOR)), 3 * 10 ** 16); + } + + function test_ezETHAdmin() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aezETH, , ) = AaveV3Base.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.ezETH() + ); + assertEq( + IEmissionManager(AaveV3Base.EMISSION_MANAGER).getEmissionAdmin(proposal.ezETH()), + proposal.ezETH_LM_ADMIN() + ); + assertEq( + IEmissionManager(AaveV3Base.EMISSION_MANAGER).getEmissionAdmin(aezETH), + proposal.ezETH_LM_ADMIN() + ); + } +} diff --git a/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md new file mode 100644 index 000000000..3104bca06 --- /dev/null +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md @@ -0,0 +1,87 @@ +--- +title: "Onboard ezETH to Arbitrum and Base Instances" +author: "Aave Chan Initiative" +discussions: "https://governance.aave.com/t/arfc-onboard-ezeth-to-arbitrum-and-base-instances/19622" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0xf9fa710414237636cba11187111773fac04f74eb1a562d2b50fca86cb72a778e" +--- + +## Simple Summary + +This is an AIP for adding borrow/lend support for Renzo Protocol and its Liquid Restaking Token (LRT) ezETH on AAVE V3 Base and Arbitrum. + +## Motivation + +As productive assets, staking tokens are high quality collateral to borrow against. ezETH is one of the largest liquid restaking tokens. With strong demand for ezETH on mainnet this proposal seeks to provide the opportunity for Aave users to make use of ezETH on leading Layer 2 networks: Base and Arbitrum. + +The onboarding of ezETH on these L2s will create increased ezETH demand and increased revenues for both Aave and Renzo Protocol, whilst also bolstering the liquidity and peg stability of ezETH. + +## Specification + +The table below illustrates the configured risk parameters for **ezETH** on Arbitrum and Base instances + +| Parameter | Value | +| --------------------------- | -----------------------------------------: | +| Isolation Mode | false | +| Borrowable | DISABLED | +| Collateral Enabled | true | +| Supply Cap (ezETH) Arbitrum | 1,750 | +| Supply Cap (ezETH) Base | 1,200 | +| Borrow Cap (ezETH) | 1 | +| Debt Ceiling | USD 0 | +| LTV | 0.05 % | +| LT | 0.10 % | +| 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 (Arbitrum) | 0x8Ed37B72300683c0482A595bfa80fFb793874b15 | +| Oracle (Base) | 0x438e24f5FCDC1A66ecb25D19B5543e0Cb91A44D4 | + +The following CAPO and E-Mode parameters are applied to both instances + +### CAPO + +| **maxYearlyRatioGrowthPercent** | **ratioReferenceTime** | **MINIMUM_SNAPSHOT_DELAY** | +| ------------------------------- | ---------------------- | -------------------------- | +| 10.89% | monthly | 14 days | + +### E-mode: ezETH / wstETH + +| Parameter | Value | Value | +| --------------------- | ----- | ------ | +| Asset | ezETH | wstETH | +| Collateral | Yes | No | +| Borrowable | No | Yes | +| Max LTV | 93% | 93% | +| Liquidation Threshold | 95% | 95% | +| Liquidation Penalty | 1.00% | 1.00% | + +### E-mode: ezETH / stablecoin + +| Parameter | Value | Value | Value (only on Arbitrum) | +| --------------------- | ----- | ----- | ------------------------ | +| Asset | ezETH | USDC | USDT | +| Collateral | Yes | No | No | +| Borrowable | No | Yes | Yes | +| Max LTV | 72% | 72% | 72% | +| Liquidation Threshold | 75% | 75% | 75% | +| Liquidation Penalty | 7.50% | 7.50% | 7.50% | + +Additionaly [0xac140648435d03f784879cd789130F22Ef588Fcd](https://debank.com/profile/0xac140648435d03f784879cd789130F22Ef588Fcd) has been set as the emission admin for ezETH and the corresponding aToken on both instances. + +## References + +- Implementation: [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol) +- Tests: [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0xf9fa710414237636cba11187111773fac04f74eb1a562d2b50fca86cb72a778e) +- [Discussion](https://governance.aave.com/t/arfc-onboard-ezeth-to-arbitrum-and-base-instances/19622) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol new file mode 100644 index 000000000..eca2931a7 --- /dev/null +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol @@ -0,0 +1,90 @@ +// 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, BaseScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; +import {AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221} from './AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol'; +import {AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221} from './AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol'; + +/** + * @dev Deploy Arbitrum + * deploy-command: make deploy-ledger contract=src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol:DeployArbitrum chain=arbitrum + * verify-command: FOUNDRY_PROFILE=arbitrum npx catapulta-verify -b broadcast/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol/42161/run-latest.json + */ +contract DeployArbitrum is ArbitrumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221).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/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol:DeployBase chain=base + * verify-command: FOUNDRY_PROFILE=base npx catapulta-verify -b broadcast/OnboardEzETHToArbitrumAndBaseInstances_20241221.s.sol/8453/run-latest.json + */ +contract DeployBase is BaseScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221).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/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances_20241221.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 actionsArbitrum = new IPayloadsControllerCore.ExecutionAction[](1); + actionsArbitrum[0] = GovV3Helpers.buildAction( + type(AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221).creationCode + ); + payloads[0] = GovV3Helpers.buildArbitrumPayload(vm, actionsArbitrum); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsBase = new IPayloadsControllerCore.ExecutionAction[](1); + actionsBase[0] = GovV3Helpers.buildAction( + type(AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221).creationCode + ); + payloads[1] = GovV3Helpers.buildBasePayload(vm, actionsBase); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md' + ) + ); + } +} diff --git a/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/config.ts b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/config.ts new file mode 100644 index 000000000..854abd168 --- /dev/null +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/config.ts @@ -0,0 +1,108 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + configFile: 'src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/config.ts', + author: 'Aave Chan Initiative', + pools: ['AaveV3Arbitrum', 'AaveV3Base'], + title: 'Onboard ezETH to Arbitrum and Base Instances', + shortName: 'OnboardEzETHToArbitrumAndBaseInstances', + date: '20241221', + discussion: + 'https://governance.aave.com/t/arfc-onboard-ezeth-to-arbitrum-and-base-instances/19622', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0xf9fa710414237636cba11187111773fac04f74eb1a562d2b50fca86cb72a778e', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Arbitrum: { + configs: { + EMODES_UPDATES: [ + {eModeCategory: 3, ltv: '93', liqThreshold: '95', liqBonus: '1', label: 'ezETH / wstETH'}, + ], + EMODES_ASSETS: [ + {asset: 'ezETH', eModeCategory: '3', collateral: 'ENABLED', borrowable: 'DISABLED'}, + { + asset: 'ezETH', + eModeCategory: 'AaveV3ArbitrumEModes.ETH_CORRELATED', + collateral: 'ENABLED', + borrowable: 'DISABLED', + }, + {asset: 'wstETH', eModeCategory: '3', collateral: 'DISABLED', borrowable: 'ENABLED'}, + ], + ASSET_LISTING: [ + { + assetSymbol: 'ezETH', + decimals: 18, + priceFeed: '0x8Ed37B72300683c0482A595bfa80fFb793874b15', + ltv: '72', + liqThreshold: '75', + liqBonus: '7.5', + debtCeiling: '0', + liqProtocolFee: '10', + enabledToBorrow: 'ENABLED', + flashloanable: 'ENABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '15', + supplyCap: '1750', + borrowCap: '175', + rateStrategyParams: { + optimalUtilizationRate: '45', + baseVariableBorrowRate: '0', + variableRateSlope1: '7', + variableRateSlope2: '300', + }, + asset: '0x2416092f143378750bb29b79eD961ab195CcEea5', + admin: '0xac140648435d03f784879cd789130F22Ef588Fcd', + }, + ], + }, + cache: {blockNumber: 287111756}, + }, + AaveV3Base: { + configs: { + EMODES_UPDATES: [ + {eModeCategory: 2, ltv: '93', liqThreshold: '95', liqBonus: '1', label: 'ezETH / wstETH'}, + ], + EMODES_ASSETS: [ + {asset: 'wstETH', eModeCategory: '2', collateral: 'DISABLED', borrowable: 'ENABLED'}, + { + asset: 'ezETH', + eModeCategory: 'AaveV3BaseEModes.ETH_CORRELATED', + collateral: 'ENABLED', + borrowable: 'DISABLED', + }, + {asset: 'ezETH', eModeCategory: '2', collateral: 'ENABLED', borrowable: 'DISABLED'}, + ], + ASSET_LISTING: [ + { + assetSymbol: 'ezETH', + decimals: 18, + priceFeed: '0x438e24f5FCDC1A66ecb25D19B5543e0Cb91A44D4', + ltv: '72', + liqThreshold: '75', + liqBonus: '7.5', + debtCeiling: '0', + liqProtocolFee: '10', + enabledToBorrow: 'ENABLED', + flashloanable: 'ENABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '15', + supplyCap: '1200', + borrowCap: '120', + rateStrategyParams: { + optimalUtilizationRate: '45', + baseVariableBorrowRate: '0', + variableRateSlope1: '7', + variableRateSlope2: '300', + }, + asset: '0x2416092f143378750bb29b79ed961ab195cceea5', + admin: '0xac140648435d03f784879cd789130F22Ef588Fcd', + }, + ], + }, + cache: {blockNumber: 24002429}, + }, + }, +}; From 98da29ed179ea867748ca34f5ffb71e5c2ddeb93 Mon Sep 17 00:00:00 2001 From: Cache bot Date: Fri, 10 Jan 2025 15:32:30 +0000 Subject: [PATCH 13/21] fix(cache): automated cache update [skip ci] --- .../OnboardEzETHToArbitrumAndBaseInstances.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md index 3104bca06..78615ed4c 100644 --- a/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md +++ b/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/OnboardEzETHToArbitrumAndBaseInstances.md @@ -77,8 +77,8 @@ Additionaly [0xac140648435d03f784879cd789130F22Ef588Fcd](https://debank.com/prof ## References -- Implementation: [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol) -- Tests: [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol) +- Implementation: [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/612e087e1b3fff317fd444905d17bbf0e0abcb37/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/612e087e1b3fff317fd444905d17bbf0e0abcb37/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.sol) +- Tests: [AaveV3Arbitrum](https://github.com/bgd-labs/aave-proposals-v3/blob/612e087e1b3fff317fd444905d17bbf0e0abcb37/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Arbitrum_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol), [AaveV3Base](https://github.com/bgd-labs/aave-proposals-v3/blob/612e087e1b3fff317fd444905d17bbf0e0abcb37/src/20241221_Multi_OnboardEzETHToArbitrumAndBaseInstances/AaveV3Base_OnboardEzETHToArbitrumAndBaseInstances_20241221.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0xf9fa710414237636cba11187111773fac04f74eb1a562d2b50fca86cb72a778e) - [Discussion](https://governance.aave.com/t/arfc-onboard-ezeth-to-arbitrum-and-base-instances/19622) From f2940b91dd47d700d1d8ec8632c262b1409e71a9 Mon Sep 17 00:00:00 2001 From: Alice <121383428+Rozengarden@users.noreply.github.com> Date: Tue, 14 Jan 2025 14:33:36 +0100 Subject: [PATCH 14/21] [Aavechan] Onboard sUSDe and weETH on zksync (#565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * onboard USDe, sUSDe, weETH AIP * add aip description * make weeth borrowable in emode (to macth mainnet) * 🐛 remove USDe from asset implemented * 🐛 remove USDe + weETH as borrowable asset + weETH from ETH correlated emode + add new weETH correlated emode * ✨ add weETH + ETH to new "weETH correlated" emode * ✅ ran tests * Update src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md Co-authored-by: Harsh Pandey * Update src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md Co-authored-by: Harsh Pandey * Update src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol Co-authored-by: Harsh Pandey --------- Co-authored-by: MartinGbz Co-authored-by: Marc Zeller <21088542+marczeller@users.noreply.github.com> Co-authored-by: Harsh Pandey --- ...AndWeETHToAaveV3OnZkSync_20250110_after.md | 242 ++++++++++++++++++ ...nboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md | 79 ++++++ ...SDeAndWeETHToAaveV3OnZkSync_20250110.s.sol | 44 ++++ .../config.ts | 84 ++++++ ...eUSDeAndWeETHToAaveV3OnZkSync_20250110.sol | 144 +++++++++++ ...SDeAndWeETHToAaveV3OnZkSync_20250110.t.sol | 83 ++++++ ...SDeAndWeETHToAaveV3OnZkSync_20250110.s.sol | 37 +++ 7 files changed, 713 insertions(+) create mode 100644 diffs/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_before_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_after.md create mode 100644 src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md create mode 100644 src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol create mode 100644 src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/config.ts create mode 100644 zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol create mode 100644 zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol create mode 100644 zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol diff --git a/diffs/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_before_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_after.md b/diffs/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_before_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_after.md new file mode 100644 index 000000000..2cf287f03 --- /dev/null +++ b/diffs/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_before_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_after.md @@ -0,0 +1,242 @@ +## Reserve changes + +### Reserve added + +#### sUSDe ([0xAD17Da2f6Ac76746EF261E835C50b2651ce36DA8](https://era.zksync.network//address/0xAD17Da2f6Ac76746EF261E835C50b2651ce36DA8)) + +| description | value | +| --- | --- | +| decimals | 18 | +| isActive | true | +| isFrozen | false | +| supplyCap | 400,000 sUSDe | +| borrowCap | 1 sUSDe | +| debtCeiling | 400,000 $ [40000000] | +| isSiloed | false | +| isFlashloanable | true | +| oracle | [0xDaec4cC3a41E423d678428A8Bb29fa1ADF26869a](https://era.zksync.network//address/0xDaec4cC3a41E423d678428A8Bb29fa1ADF26869a) | +| oracleDecimals | 8 | +| oracleDescription | Capped sUSDe / USDe / USD | +| oracleLatestAnswer | 1.14468351 | +| usageAsCollateralEnabled | true | +| ltv | 65 % [6500] | +| liquidationThreshold | 75 % [7500] | +| liquidationBonus | 8.5 % | +| liquidationProtocolFee | 10 % [1000] | +| reserveFactor | 20 % [2000] | +| aToken | [0xF3c9d58B76AC6Ee6811520021e9A9318c49E4CFa](https://era.zksync.network//address/0xF3c9d58B76AC6Ee6811520021e9A9318c49E4CFa) | +| aTokenImpl | [0x34be365Fd01ac224F21490aaC6dFd65D25434bbB](https://era.zksync.network//address/0x34be365Fd01ac224F21490aaC6dFd65D25434bbB) | +| variableDebtToken | [0xDeBb4ddaaaB1676775214552a7a05D6A13f905Da](https://era.zksync.network//address/0xDeBb4ddaaaB1676775214552a7a05D6A13f905Da) | +| variableDebtTokenImpl | [0x52E97425D1Fa6885fAaC9260B711fA5047A88d06](https://era.zksync.network//address/0x52E97425D1Fa6885fAaC9260B711fA5047A88d06) | +| borrowingEnabled | false | +| isBorrowableInIsolation | false | +| interestRateStrategy | [0x57815Ab06D846d7dECd326Ee541CD06144FED237](https://era.zksync.network//address/0x57815Ab06D846d7dECd326Ee541CD06144FED237) | +| aTokenName | Aave ZkSync sUSDe | +| aTokenSymbol | aZkssUSDe | +| aTokenUnderlyingBalance | 100 sUSDe [100000000000000000000] | +| id | 6 | +| isPaused | false | +| variableDebtTokenName | Aave ZkSync Variable Debt sUSDe | +| variableDebtTokenSymbol | variableDebtZkssUSDe | +| virtualAccountingActive | true | +| virtualBalance | 100 sUSDe [100000000000000000000] | +| optimalUsageRatio | 80 % | +| maxVariableBorrowRate | 84 % | +| baseVariableBorrowRate | 0 % | +| variableRateSlope1 | 9 % | +| variableRateSlope2 | 75 % | +| interestRate | ![ir](https://dash.onaave.com/api/static?variableRateSlope1=90000000000000000000000000&variableRateSlope2=750000000000000000000000000&optimalUsageRatio=800000000000000000000000000&baseVariableBorrowRate=0&maxVariableBorrowRate=840000000000000000000000000) | + + +#### weETH ([0xc1Fa6E2E8667d9bE0Ca938a54c7E0285E9Df924a](https://era.zksync.network//address/0xc1Fa6E2E8667d9bE0Ca938a54c7E0285E9Df924a)) + +| description | value | +| --- | --- | +| decimals | 18 | +| isActive | true | +| isFrozen | false | +| supplyCap | 300 weETH | +| borrowCap | 1 weETH | +| debtCeiling | 0 $ [0] | +| isSiloed | false | +| isFlashloanable | true | +| oracle | [0x32aF9A0a47B332761c8C90E9eC9f53e46e852b2B](https://era.zksync.network//address/0x32aF9A0a47B332761c8C90E9eC9f53e46e852b2B) | +| oracleDecimals | 8 | +| oracleDescription | Capped weETH / eETH(ETH) / USD | +| oracleLatestAnswer | 3441.51796452 | +| usageAsCollateralEnabled | true | +| ltv | 72.5 % [7250] | +| liquidationThreshold | 75 % [7500] | +| liquidationBonus | 7.5 % | +| liquidationProtocolFee | 10 % [1000] | +| reserveFactor | 45 % [4500] | +| aToken | [0xE818A67EE5c0531AFaa31Aa6e20bcAC36227A641](https://era.zksync.network//address/0xE818A67EE5c0531AFaa31Aa6e20bcAC36227A641) | +| aTokenImpl | [0x34be365Fd01ac224F21490aaC6dFd65D25434bbB](https://era.zksync.network//address/0x34be365Fd01ac224F21490aaC6dFd65D25434bbB) | +| variableDebtToken | [0xf31E1599b4480d07Fa96a7248c4f05cA84DA7fa8](https://era.zksync.network//address/0xf31E1599b4480d07Fa96a7248c4f05cA84DA7fa8) | +| variableDebtTokenImpl | [0x52E97425D1Fa6885fAaC9260B711fA5047A88d06](https://era.zksync.network//address/0x52E97425D1Fa6885fAaC9260B711fA5047A88d06) | +| borrowingEnabled | false | +| isBorrowableInIsolation | false | +| interestRateStrategy | [0x57815Ab06D846d7dECd326Ee541CD06144FED237](https://era.zksync.network//address/0x57815Ab06D846d7dECd326Ee541CD06144FED237) | +| aTokenName | Aave ZkSync weETH | +| aTokenSymbol | aZksweETH | +| aTokenUnderlyingBalance | 0.025 weETH [25000000000000000] | +| id | 5 | +| isPaused | false | +| variableDebtTokenName | Aave ZkSync Variable Debt weETH | +| variableDebtTokenSymbol | variableDebtZksweETH | +| virtualAccountingActive | true | +| virtualBalance | 0.025 weETH [25000000000000000] | +| optimalUsageRatio | 30 % | +| maxVariableBorrowRate | 307 % | +| baseVariableBorrowRate | 0 % | +| variableRateSlope1 | 7 % | +| variableRateSlope2 | 300 % | +| interestRate | ![ir](https://dash.onaave.com/api/static?variableRateSlope1=70000000000000000000000000&variableRateSlope2=3000000000000000000000000000&optimalUsageRatio=300000000000000000000000000&baseVariableBorrowRate=0&maxVariableBorrowRate=3070000000000000000000000000) | + + +## Emodes changed + +### EMode: ETH correlated(id: 1) + + + +### EMode: weETH correlated(id: 2) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | weETH correlated | +| eMode.ltv | - | 90 % | +| eMode.liquidationThreshold | - | 93 % | +| eMode.liquidationBonus | - | 1 % | +| eMode.borrowableBitmap | - | WETH | +| eMode.collateralBitmap | - | weETH | + + +## Raw diff + +```json +{ + "eModes": { + "2": { + "from": null, + "to": { + "borrowableBitmap": "4", + "collateralBitmap": "32", + "eModeCategory": 2, + "label": "weETH correlated", + "liquidationBonus": 10100, + "liquidationThreshold": 9300, + "ltv": 9000 + } + } + }, + "reserves": { + "0xAD17Da2f6Ac76746EF261E835C50b2651ce36DA8": { + "from": null, + "to": { + "aToken": "0xF3c9d58B76AC6Ee6811520021e9A9318c49E4CFa", + "aTokenImpl": "0x34be365Fd01ac224F21490aaC6dFd65D25434bbB", + "aTokenName": "Aave ZkSync sUSDe", + "aTokenSymbol": "aZkssUSDe", + "aTokenUnderlyingBalance": "100000000000000000000", + "borrowCap": 1, + "borrowingEnabled": false, + "debtCeiling": 40000000, + "decimals": 18, + "id": 6, + "interestRateStrategy": "0x57815Ab06D846d7dECd326Ee541CD06144FED237", + "isActive": true, + "isBorrowableInIsolation": false, + "isFlashloanable": true, + "isFrozen": false, + "isPaused": false, + "isSiloed": false, + "liquidationBonus": 10850, + "liquidationProtocolFee": 1000, + "liquidationThreshold": 7500, + "ltv": 6500, + "oracle": "0xDaec4cC3a41E423d678428A8Bb29fa1ADF26869a", + "oracleDecimals": 8, + "oracleDescription": "Capped sUSDe / USDe / USD", + "oracleLatestAnswer": "114468351", + "reserveFactor": 2000, + "supplyCap": 400000, + "symbol": "sUSDe", + "underlying": "0xAD17Da2f6Ac76746EF261E835C50b2651ce36DA8", + "usageAsCollateralEnabled": true, + "variableDebtToken": "0xDeBb4ddaaaB1676775214552a7a05D6A13f905Da", + "variableDebtTokenImpl": "0x52E97425D1Fa6885fAaC9260B711fA5047A88d06", + "variableDebtTokenName": "Aave ZkSync Variable Debt sUSDe", + "variableDebtTokenSymbol": "variableDebtZkssUSDe", + "virtualAccountingActive": true, + "virtualBalance": "100000000000000000000" + } + }, + "0xc1Fa6E2E8667d9bE0Ca938a54c7E0285E9Df924a": { + "from": null, + "to": { + "aToken": "0xE818A67EE5c0531AFaa31Aa6e20bcAC36227A641", + "aTokenImpl": "0x34be365Fd01ac224F21490aaC6dFd65D25434bbB", + "aTokenName": "Aave ZkSync weETH", + "aTokenSymbol": "aZksweETH", + "aTokenUnderlyingBalance": "25000000000000000", + "borrowCap": 1, + "borrowingEnabled": false, + "debtCeiling": 0, + "decimals": 18, + "id": 5, + "interestRateStrategy": "0x57815Ab06D846d7dECd326Ee541CD06144FED237", + "isActive": true, + "isBorrowableInIsolation": false, + "isFlashloanable": true, + "isFrozen": false, + "isPaused": false, + "isSiloed": false, + "liquidationBonus": 10750, + "liquidationProtocolFee": 1000, + "liquidationThreshold": 7500, + "ltv": 7250, + "oracle": "0x32aF9A0a47B332761c8C90E9eC9f53e46e852b2B", + "oracleDecimals": 8, + "oracleDescription": "Capped weETH / eETH(ETH) / USD", + "oracleLatestAnswer": "344151796452", + "reserveFactor": 4500, + "supplyCap": 300, + "symbol": "weETH", + "underlying": "0xc1Fa6E2E8667d9bE0Ca938a54c7E0285E9Df924a", + "usageAsCollateralEnabled": true, + "variableDebtToken": "0xf31E1599b4480d07Fa96a7248c4f05cA84DA7fa8", + "variableDebtTokenImpl": "0x52E97425D1Fa6885fAaC9260B711fA5047A88d06", + "variableDebtTokenName": "Aave ZkSync Variable Debt weETH", + "variableDebtTokenSymbol": "variableDebtZksweETH", + "virtualAccountingActive": true, + "virtualBalance": "25000000000000000" + } + } + }, + "strategies": { + "0xAD17Da2f6Ac76746EF261E835C50b2651ce36DA8": { + "from": null, + "to": { + "address": "0x57815Ab06D846d7dECd326Ee541CD06144FED237", + "baseVariableBorrowRate": "0", + "maxVariableBorrowRate": "840000000000000000000000000", + "optimalUsageRatio": "800000000000000000000000000", + "variableRateSlope1": "90000000000000000000000000", + "variableRateSlope2": "750000000000000000000000000" + } + }, + "0xc1Fa6E2E8667d9bE0Ca938a54c7E0285E9Df924a": { + "from": null, + "to": { + "address": "0x57815Ab06D846d7dECd326Ee541CD06144FED237", + "baseVariableBorrowRate": "0", + "maxVariableBorrowRate": "3070000000000000000000000000", + "optimalUsageRatio": "300000000000000000000000000", + "variableRateSlope1": "70000000000000000000000000", + "variableRateSlope2": "3000000000000000000000000000" + } + } + } +} +``` \ No newline at end of file diff --git a/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md new file mode 100644 index 000000000..a7a340256 --- /dev/null +++ b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md @@ -0,0 +1,79 @@ +--- +title: "Onboard sUSDe and weETH to Aave v3 on zkSync" +author: "Aave-chan Initiative" +discussions: "https://governance.aave.com/t/arfc-onboard-susde-usde-and-weeth-to-aave-v3-on-zksync/19204" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0x6709151a1efa71370a6a0f9a7592d983ed401ac0311cce861fba347081384520" +--- + +## Simple Summary + +This proposal aims to onboard sUSDe and weETH to the Aave v3 protocol on zkSync. This follows the original plans for further expansion on the network. + +## Motivation + +The integration of sUSDe, and weETH into Aave v3 on zkSync is following the initial plan for the zkSync network launch. With the successful launch of Aave v3 on zkSync, and some time for monitoring, we believe it is time to start expanding from the initial asset list. + +These onboardings also include partnership with Ethena and EtherFi to add ZK token incentives to each market, which will contribute to Aave’s growth on zkSync. + +## Specification + +The table below illustrates the configured risk parameters for **weETH** + +| Parameter | Value | +| ------------------------- | -----------------------------------------: | +| Isolation Mode | false | +| Borrowable | DISABLED | +| Collateral Enabled | true | +| Supply Cap (weETH) | 300 | +| Borrow Cap (weETH) | 1 | +| Debt Ceiling | USD 0 | +| LTV | 72.5 % | +| LT | 75 % | +| Liquidation Bonus | 7.5 % | +| Liquidation Protocol Fee | 10 % | +| Reserve Factor | 45 % | +| Base Variable Borrow Rate | 0 % | +| Variable Slope 1 | 7 % | +| Variable Slope 2 | 300 % | +| Uoptimal | 30 % | +| Flashloanable | ENABLED | +| Siloed Borrowing | DISABLED | +| Borrowable in Isolation | DISABLED | +| Oracle | 0x32aF9A0a47B332761c8C90E9eC9f53e46e852b2B | + +The table below illustrates the configured risk parameters for **sUSDe** + +| Parameter | Value | +| ------------------------- | -----------------------------------------: | +| Isolation Mode | true | +| Borrowable | DISABLED | +| Collateral Enabled | true | +| Supply Cap (sUSDe) | 400,000 | +| Borrow Cap (sUSDe) | 1 | +| Debt Ceiling | USD 400,000 | +| LTV | 65 % | +| LT | 75 % | +| Liquidation Bonus | 8.5 % | +| Liquidation Protocol Fee | 10 % | +| Reserve Factor | 20 % | +| Base Variable Borrow Rate | 0 % | +| Variable Slope 1 | 9 % | +| Variable Slope 2 | 75 % | +| Uoptimal | 80 % | +| Flashloanable | ENABLED | +| Siloed Borrowing | DISABLED | +| Borrowable in Isolation | DISABLED | +| Oracle | 0xDaec4cC3a41E423d678428A8Bb29fa1ADF26869a | + +Additionally [0x95Cbff6e45C499d45dd8627f3ce179057B5Fbfcc](https://era.zksync.network/address/0x95Cbff6e45C499d45dd8627f3ce179057B5Fbfcc) has been set as the emission admin for weETH, sUSDe and their corresponding aTokens. + +## References + +- Implementation: [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol) +- Tests: [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x6709151a1efa71370a6a0f9a7592d983ed401ac0311cce861fba347081384520) +- [Discussion](https://governance.aave.com/t/arfc-onboard-susde-usde-and-weeth-to-aave-v3-on-zksync/19204) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol new file mode 100644 index 000000000..7ec19a123 --- /dev/null +++ b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers, IPayloadsControllerCore, PayloadsControllerUtils} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3OnZkSync} from 'aave-address-book/AaveV3OnZkSync.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {EthereumScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; + +/** + * @dev Create Proposal + * command: make deploy-ledger contract=src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.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 actionsZkSync = new IPayloadsControllerCore.ExecutionAction[](1); + actionsZkSync[0] = GovV3Helpers.buildActionZkSync( + vm, + 'AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110' + ); + payloads[0] = PayloadsControllerUtils.Payload({ + chain: ChainIds.ZKSYNC, + accessLevel: PayloadsControllerUtils.AccessControl.Level_1, + payloadsController: address(GovernanceV3ZkSync.PAYLOADS_CONTROLLER), + payloadId: 10 + }); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md' + ) + ); + } +} diff --git a/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/config.ts b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/config.ts new file mode 100644 index 000000000..7d5cc73f6 --- /dev/null +++ b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/config.ts @@ -0,0 +1,84 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + configFile: 'src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/config.ts', + author: 'Aave-chan Initiative', + pools: ['AaveV3ZkSync'], + title: 'Onboard sUSDe, USDe and weETH to Aave v3 on zkSync', + shortName: 'OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync', + date: '20250110', + discussion: + 'https://governance.aave.com/t/arfc-onboard-susde-usde-and-weeth-to-aave-v3-on-zksync/19204', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x6709151a1efa71370a6a0f9a7592d983ed401ac0311cce861fba347081384520', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3ZkSync: { + configs: { + EMODES_UPDATES: [ + { + eModeCategory: 2, + ltv: '90', + liqThreshold: '93', + liqBonus: '1', + label: 'weETH correlated', + }, + ], + ASSET_LISTING: [ + { + assetSymbol: 'weETH', + decimals: 18, + priceFeed: '0x32aF9A0a47B332761c8C90E9eC9f53e46e852b2B', + ltv: '72.5', + liqThreshold: '75', + liqBonus: '7.5', + debtCeiling: '0', + liqProtocolFee: '10', + enabledToBorrow: 'DISABLED', + flashloanable: 'ENABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '45', + supplyCap: '300', + borrowCap: '1', + rateStrategyParams: { + optimalUtilizationRate: '30', + baseVariableBorrowRate: '0', + variableRateSlope1: '7', + variableRateSlope2: '300', + }, + asset: '0xc1Fa6E2E8667d9bE0Ca938a54c7E0285E9Df924a', + admin: '0x95Cbff6e45C499d45dd8627f3ce179057B5Fbfcc', + }, + { + assetSymbol: 'sUSDe', + decimals: 18, + priceFeed: '0xDaec4cC3a41E423d678428A8Bb29fa1ADF26869a', + ltv: '65', + liqThreshold: '75', + liqBonus: '8.5', + debtCeiling: '400000', + liqProtocolFee: '10', + enabledToBorrow: 'DISABLED', + flashloanable: 'ENABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '20', + supplyCap: '400000', + borrowCap: '1', + rateStrategyParams: { + optimalUtilizationRate: '80', + baseVariableBorrowRate: '0', + variableRateSlope1: '9', + variableRateSlope2: '75', + }, + asset: '0xAD17Da2f6Ac76746EF261E835C50b2651ce36DA8', + admin: '0x95Cbff6e45C499d45dd8627f3ce179057B5Fbfcc', + }, + ], + }, + cache: {blockNumber: 53365321}, + }, + }, +}; diff --git a/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol new file mode 100644 index 000000000..cba6bbe8f --- /dev/null +++ b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3ZkSync, AaveV3ZkSyncAssets} from 'aave-address-book/AaveV3ZkSync.sol'; +import {AaveV3PayloadZkSync} from 'aave-helpers/src/v3-config-engine/AaveV3PayloadZkSync.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 sUSDe, USDe and weETH to Aave v3 on zkSync + * @author Aave-chan Initiative + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x6709151a1efa71370a6a0f9a7592d983ed401ac0311cce861fba347081384520 + * - Discussion: https://governance.aave.com/t/arfc-onboard-susde-usde-and-weeth-to-aave-v3-on-zksync/19204 + */ +contract AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110 is AaveV3PayloadZkSync { + using SafeERC20 for IERC20; + + address public constant weETH = 0xc1Fa6E2E8667d9bE0Ca938a54c7E0285E9Df924a; + uint256 public constant weETH_SEED_AMOUNT = 25e15; + address public constant weETH_LM_ADMIN = 0x95Cbff6e45C499d45dd8627f3ce179057B5Fbfcc; + + address public constant sUSDe = 0xAD17Da2f6Ac76746EF261E835C50b2651ce36DA8; + uint256 public constant sUSDe_SEED_AMOUNT = 100e18; + address public constant sUSDe_LM_ADMIN = 0x95Cbff6e45C499d45dd8627f3ce179057B5Fbfcc; + + function _postExecute() internal override { + IERC20(weETH).forceApprove(address(AaveV3ZkSync.POOL), weETH_SEED_AMOUNT); + AaveV3ZkSync.POOL.supply(weETH, weETH_SEED_AMOUNT, address(AaveV3ZkSync.COLLECTOR), 0); + + (address aweETH, , ) = AaveV3ZkSync.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + weETH + ); + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).setEmissionAdmin(weETH, weETH_LM_ADMIN); + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).setEmissionAdmin(aweETH, weETH_LM_ADMIN); + + IERC20(sUSDe).forceApprove(address(AaveV3ZkSync.POOL), sUSDe_SEED_AMOUNT); + AaveV3ZkSync.POOL.supply(sUSDe, sUSDe_SEED_AMOUNT, address(AaveV3ZkSync.COLLECTOR), 0); + + (address asUSDe, , ) = AaveV3ZkSync.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + sUSDe + ); + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).setEmissionAdmin(sUSDe, sUSDe_LM_ADMIN); + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).setEmissionAdmin(asUSDe, sUSDe_LM_ADMIN); + } + + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](1); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 2, + ltv: 90_00, + liqThreshold: 93_00, + liqBonus: 1_00, + label: 'weETH 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: weETH, + eModeCategory: 2, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + + assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3ZkSyncAssets.WETH_UNDERLYING, + eModeCategory: 2, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + + return assetEModeUpdates; + } + function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) { + IAaveV3ConfigEngine.Listing[] memory listings = new IAaveV3ConfigEngine.Listing[](2); + + listings[0] = IAaveV3ConfigEngine.Listing({ + asset: weETH, + assetSymbol: 'weETH', + priceFeed: 0x32aF9A0a47B332761c8C90E9eC9f53e46e852b2B, + enabledToBorrow: EngineFlags.DISABLED, + borrowableInIsolation: EngineFlags.DISABLED, + withSiloedBorrowing: EngineFlags.DISABLED, + flashloanable: EngineFlags.ENABLED, + ltv: 72_50, + liqThreshold: 75_00, + liqBonus: 7_50, + reserveFactor: 45_00, + supplyCap: 300, + borrowCap: 1, + debtCeiling: 0, + liqProtocolFee: 10_00, + rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({ + optimalUsageRatio: 30_00, + baseVariableBorrowRate: 0, + variableRateSlope1: 7_00, + variableRateSlope2: 300_00 + }) + }); + listings[1] = IAaveV3ConfigEngine.Listing({ + asset: sUSDe, + assetSymbol: 'sUSDe', + priceFeed: 0xDaec4cC3a41E423d678428A8Bb29fa1ADF26869a, + enabledToBorrow: EngineFlags.DISABLED, + borrowableInIsolation: EngineFlags.DISABLED, + withSiloedBorrowing: EngineFlags.DISABLED, + flashloanable: EngineFlags.ENABLED, + ltv: 65_00, + liqThreshold: 75_00, + liqBonus: 8_50, + reserveFactor: 20_00, + supplyCap: 400_000, + borrowCap: 1, + debtCeiling: 400_000, + liqProtocolFee: 10_00, + rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({ + optimalUsageRatio: 80_00, + baseVariableBorrowRate: 0, + variableRateSlope1: 9_00, + variableRateSlope2: 75_00 + }) + }); + + return listings; + } +} diff --git a/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol new file mode 100644 index 000000000..b5645963b --- /dev/null +++ b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3ZkSync} from 'aave-address-book/AaveV3ZkSync.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/zksync/src/ProtocolV3TestBase.sol'; +import {AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110} from './AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol'; + +/** + * @dev Test for AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110 + * command: FOUNDRY_PROFILE=zksync forge test --zksync --match-path=zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol -vv + */ +contract AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110_Test is ProtocolV3TestBase { + AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110 internal proposal; + + function setUp() public override { + vm.createSelectFork(vm.rpcUrl('zksync'), 53365321); + proposal = new AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110(); + + super.setUp(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110', + AaveV3ZkSync.POOL, + address(proposal) + ); + } + + function test_collectorHasweETHFunds() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aTokenAddress, , ) = AaveV3ZkSync + .AAVE_PROTOCOL_DATA_PROVIDER + .getReserveTokensAddresses(proposal.weETH()); + assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3ZkSync.COLLECTOR)), 25 * 10 ** 15); + } + + function test_weETHAdmin() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aweETH, , ) = AaveV3ZkSync.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.weETH() + ); + assertEq( + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).getEmissionAdmin(proposal.weETH()), + proposal.weETH_LM_ADMIN() + ); + assertEq( + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).getEmissionAdmin(aweETH), + proposal.weETH_LM_ADMIN() + ); + } + + function test_collectorHassUSDeFunds() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aTokenAddress, , ) = AaveV3ZkSync + .AAVE_PROTOCOL_DATA_PROVIDER + .getReserveTokensAddresses(proposal.sUSDe()); + assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3ZkSync.COLLECTOR)), 100 * 10 ** 18); + } + + function test_sUSDeAdmin() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address asUSDe, , ) = AaveV3ZkSync.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.sUSDe() + ); + assertEq( + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).getEmissionAdmin(proposal.sUSDe()), + proposal.sUSDe_LM_ADMIN() + ); + assertEq( + IEmissionManager(AaveV3ZkSync.EMISSION_MANAGER).getEmissionAdmin(asUSDe), + proposal.sUSDe_LM_ADMIN() + ); + } +} diff --git a/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol new file mode 100644 index 000000000..f4e963be5 --- /dev/null +++ b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.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_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110} from './AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol'; + +// @dev wrapper factory contract for deploying the payload +contract Deploy_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110 { + address public immutable PAYLOAD; + + constructor() { + PAYLOAD = GovV3Helpers.deployDeterministicZkSync( + type(AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110).creationCode + ); + } +} + +/** + * @dev Deploy ZkSync + * deploy-command: make deploy-pk FOUNDRY_PROFILE=contract=zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol:DeployZkSync chain=zksync + */ +contract DeployZkSync is ZkSyncScript { + function run() external broadcast { + // deploy payloads + address payload0 = new Deploy_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110() + .PAYLOAD(); + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(payload0); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} From df9a0223e0ddab4c59b5fab251064477e9e1ba8e Mon Sep 17 00:00:00 2001 From: Cache bot Date: Tue, 14 Jan 2025 13:34:05 +0000 Subject: [PATCH 15/21] fix(cache): automated cache update [skip ci] --- .../OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md index a7a340256..fe3de711b 100644 --- a/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md +++ b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync.md @@ -69,8 +69,8 @@ Additionally [0x95Cbff6e45C499d45dd8627f3ce179057B5Fbfcc](https://era.zksync.net ## References -- Implementation: [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol) -- Tests: [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/main/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol) +- Implementation: [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/f2940b91dd47d700d1d8ec8632c262b1409e71a9/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.sol) +- Tests: [AaveV3ZkSync](https://github.com/bgd-labs/aave-proposals-v3/blob/f2940b91dd47d700d1d8ec8632c262b1409e71a9/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x6709151a1efa71370a6a0f9a7592d983ed401ac0311cce861fba347081384520) - [Discussion](https://governance.aave.com/t/arfc-onboard-susde-usde-and-weeth-to-aave-v3-on-zksync/19204) From 763a015e6727e434d5ba33b262b78188040a58f1 Mon Sep 17 00:00:00 2001 From: Harsh Pandey Date: Tue, 14 Jan 2025 19:27:31 +0530 Subject: [PATCH 16/21] chore: fix imports (#567) --- .../OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol | 5 +++-- .../OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol index 7ec19a123..b10629098 100644 --- a/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol +++ b/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {GovV3Helpers, IPayloadsControllerCore, PayloadsControllerUtils} from 'aave-helpers/src/GovV3Helpers.sol'; -import {AaveV3OnZkSync} from 'aave-address-book/AaveV3OnZkSync.sol'; +import {GovV3Helpers, IPayloadsControllerCore, PayloadsControllerUtils, ChainIds} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3ZkSync} from 'aave-address-book/AaveV3ZkSync.sol'; import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {GovernanceV3ZkSync} from 'aave-address-book/GovernanceV3ZkSync.sol'; import {EthereumScript} from 'solidity-utils/contracts/utils/ScriptUtils.sol'; /** diff --git a/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol index f4e963be5..bd454140c 100644 --- a/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol +++ b/zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol @@ -18,7 +18,7 @@ contract Deploy_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110 { /** * @dev Deploy ZkSync - * deploy-command: make deploy-pk FOUNDRY_PROFILE=contract=zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol:DeployZkSync chain=zksync + * deploy-command: make deploy-pk FOUNDRY_PROFILE=zksync contract=zksync/src/20250110_AaveV3ZkSync_OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync/OnboardSUSDeUSDeAndWeETHToAaveV3OnZkSync_20250110.s.sol:DeployZkSync chain=zksync */ contract DeployZkSync is ZkSyncScript { function run() external broadcast { From 47796fcf2b58215518832d4ada2e8ef75c1ee021 Mon Sep 17 00:00:00 2001 From: sendra Date: Tue, 14 Jan 2025 18:40:17 +0100 Subject: [PATCH 17/21] feat: a.DI Ethereum -> Celo path activation (#564) * feat: a.DI Ethereum -> Celo path activation * fix: add cl emergency oracle address * fix: updated addresses of payload with new lz and hl adapters on celo * fix: updated links to correct commit * fix: added forum discussion link * fix: updated aave heleprs --- lib/aave-helpers | 2 +- .../ADICeloPathActivation.md | 59 +++++++++++++++++++ .../ADICeloPathActivation_20250109.s.sol | 55 +++++++++++++++++ .../config.ts | 14 +++++ 4 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation.md create mode 100644 src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation_20250109.s.sol create mode 100644 src/20250109_AaveV3Ethereum_ADICeloPathActivation/config.ts diff --git a/lib/aave-helpers b/lib/aave-helpers index 7cdcda651..fe818aa58 160000 --- a/lib/aave-helpers +++ b/lib/aave-helpers @@ -1 +1 @@ -Subproject commit 7cdcda6516b6c08d3f3cee9e27b541c179e66fe3 +Subproject commit fe818aa58585d3525c827f04751b527310e2439a diff --git a/src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation.md b/src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation.md new file mode 100644 index 000000000..5e950da39 --- /dev/null +++ b/src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation.md @@ -0,0 +1,59 @@ +--- +title: "a.DI Celo path activation" +author: "BGD Labs @bgdlabs" +discussions: https://governance.aave.com/t/technical-maintenance-proposals/15274/61 +snapshot: Direct-to-AIP +--- + +## Simple Summary + +Proposal to register the necessary Celo adapters on a.DI, a technical pre-requirement for an activation vote of Aave v3 Celo. + +## Motivation + +In order to be able to pass messages from Ethereum to Celo via a.DI (Aave Delivery Infrastructure), it is necessary to at least have three valid adapters Ethereum → Celo smart contracts enabled in the system. + +The first case of message passing Ethereum → Celo is the activation proposal for an Aave v3 Celo pool and consequently, to be able to execute on the Celo side the payload, the Aave governance should approve in advance the a.DI adapters smart contracts. + +This procedure mirrors the requirements on previous networks like Scroll or ZkSync. + +## Specification + +The proposal payload simply registers pre-deployed Celo adapters (with the necessary configurations to communicate with the Celo a.DI) on the Ethereum a.DI instance. + +This is done by calling the enableBridgeAdapters() function on the Ethereum Cross-chain Controller smart contract. + +| Network | Hyperlane Adapter | LayerZero Adapter | CCIP Adapter | +| -------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| Ethereum | [0x01dcb90Cf13b82Cde4A0BAcC655585a83Af3cCC1](https://etherscan.io/address/0x01dcb90Cf13b82Cde4A0BAcC655585a83Af3cCC1) | [0x8410d9BD353b420ebA8C48ff1B0518426C280FCC](https://etherscan.io/address/0x8410d9BD353b420ebA8C48ff1B0518426C280FCC) | [0x58489B249BfBCF5ef4B30bdE2792086e83122B6f](https://etherscan.io/address/0x58489B249BfBCF5ef4B30bdE2792086e83122B6f) | +| Celo | [0x7b065E68E70f346B18636Ab86779980287ec73e0](https://celoscan.io/address/0x7b065E68E70f346B18636Ab86779980287ec73e0) | [0x83BC62fbeA15B7Bfe11e8eEE57997afA5451f38C](https://celoscan.io/address/0x83BC62fbeA15B7Bfe11e8eEE57997afA5451f38C) | [0x3d534E8964e7aAcfc702751cc9A2BB6A9fe7d928](https://celoscan.io/address/0x3d534E8964e7aAcfc702751cc9A2BB6A9fe7d928) | + +The new a.DI deployments on Linea network are as follows: + +| Contract | Address | +| -------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| CrossChainController | [0x50F4dAA86F3c747ce15C3C38bD0383200B61d6Dd](https://celoscan.io/address/0x50F4dAA86F3c747ce15C3C38bD0383200B61d6Dd) | +| Granular Guardian | [0xbE815420A63A413BB8D508d8022C0FF150Ea7C39](https://celoscan.io/address/0xbE815420A63A413BB8D508d8022C0FF150Ea7C39) | +| Chainlink Emergency Oracle | [0x91b21900E91CD302EBeD05E45D8f270ddAED944d](https://celoscan.io/address/0x91b21900E91CD302EBeD05E45D8f270ddAED944d) | + +The new Aave Governance deployments on Linea network are as follows: + +| Contract | Address | +| ------------------- | -------------------------------------------------------------------------------------------------------------------- | +| PayloadsController | [0xE48E10834C04E394A04BF22a565D063D40b9FA42](https://celoscan.io/address/0xE48E10834C04E394A04BF22a565D063D40b9FA42) | +| Executor Lvl 1 | [0x1dF462e2712496373A347f8ad10802a5E95f053D](https://celoscan.io/address/0x1dF462e2712496373A347f8ad10802a5E95f053D) | +| Governance Guardian | [0x056E4C4E80D1D14a637ccbD0412CDAAEc5B51F4E](https://celoscan.io/address/0x056E4C4E80D1D14a637ccbD0412CDAAEc5B51F4E) | +| BGD Labs Guardian | [0xfD3a6E65e470a7D7D730FFD5D36a9354E8F9F4Ea](https://celoscan.io/address/0xfD3a6E65e470a7D7D730FFD5D36a9354E8F9F4Ea) | + +## References + +- Adapter Implementations: [HyperLane Bridge Adapters](https://github.com/bgd-labs/aave-delivery-infrastructure/blob/1f1c46af4dd914847849cad4fdd2d26525278821/src/contracts/adapters/hyperLane/HyperLaneAdapter.sol), [LayerZero Bridge Adapters](https://github.com/bgd-labs/aave-delivery-infrastructure/blob/1f1c46af4dd914847849cad4fdd2d26525278821/src/contracts/adapters/layerZero/LayerZeroAdapter.sol), [CCIP Bridge Adapters](https://github.com/bgd-labs/aave-delivery-infrastructure/blob/1f1c46af4dd914847849cad4fdd2d26525278821/src/contracts/adapters/ccip/CCIPAdapter.sol) +- Payload Implementation: [Payload](https://github.com/bgd-labs/adi-deploy/blob/06785fcb243f179425671691099df927876baeb0/src/adapter_payloads/Ethereum_Celo_Path_Payload.sol) +- Payload Tests: [tests](https://github.com/bgd-labs/adi-deploy/blob/06785fcb243f179425671691099df927876baeb0/tests/payloads/ethereum/AddCeloPathTest.t.sol) +- Diffs: [a.DI diffs](https://github.com/bgd-labs/adi-deploy/blob/06785fcb243f179425671691099df927876baeb0/diffs/adi_add_celo_path_to_adiethereum_before_adi_add_celo_path_to_adiethereum_after.md) +- Snapshot: Direct-to-AIP +- [Discussion](https://governance.aave.com/t/technical-maintenance-proposals/15274/61) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation_20250109.s.sol b/src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation_20250109.s.sol new file mode 100644 index 000000000..f743c5df2 --- /dev/null +++ b/src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation_20250109.s.sol @@ -0,0 +1,55 @@ +// 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'; + +address constant PAYLOAD = address(0x8F7E2023686B78E148e65004Ca8AcEf9B2B46922); + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation_20250109.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/ADICeloPathActivation_20250109.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](1); + actions[0] = GovV3Helpers.buildAction(PAYLOAD); + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +/** + * @dev Create Proposal + * command: make deploy-ledger contract=src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation_20250109.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(PAYLOAD); + + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20250109_AaveV3Ethereum_ADICeloPathActivation/ADICeloPathActivation.md' + ) + ); + } +} diff --git a/src/20250109_AaveV3Ethereum_ADICeloPathActivation/config.ts b/src/20250109_AaveV3Ethereum_ADICeloPathActivation/config.ts new file mode 100644 index 000000000..f6f75824d --- /dev/null +++ b/src/20250109_AaveV3Ethereum_ADICeloPathActivation/config.ts @@ -0,0 +1,14 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + author: 'BGD Labs @bgdlabs', + pools: ['AaveV3Ethereum'], + title: 'a.DI Celo path activation', + shortName: 'ADICeloPathActivation', + date: '20250109', + discussion: '', + snapshot: '', + votingNetwork: 'POLYGON', + }, + poolOptions: {AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 21586565}}}, +}; From 134c39ccfc2ef4ed515b5d334c6ae59e6320c66e Mon Sep 17 00:00:00 2001 From: dhairya <55102840+DhairyaSethi@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:01:30 +0530 Subject: [PATCH 18/21] GHO CCIP 1.5.1 Upgrade Eth<>Arb (#557) * Add proposal for Gho Incident Report 20231113 (#1) * chore: add payload and deploy script for update of GHO variable debt token * forge install: gho-core * chore: add gho-core to dependency * test: Add tests for update of gho variable token * test: Add tests for update of gho variable token * fix: add modifier in method of interface * fix: remove gho dependency from repo and fix test * fix: Remove unnecesary dependency * fix: Add latest details --------- Co-authored-by: miguelmtzinf * fix: Make new impl constant (#3) * fix: Amend AIP text (#4) * fix: Make new impl constant * fix: Fix AIP text * test: Tweak default tests with borrow cap update (#5) * fix: lint issue (#6) * test: Add diffs from test running (#7) * fix: Add payload address (#8) * fix: Fix payload address in script (#9) * fix: Remove unneeded diff file (#10) * chore: init aip * feat: ccip 1.5.1 upgrade * tmp: fix ci for aave org, to rm for bdg * tmp: add 1.4 pool with withdrawLiq flattended, to be removed once impl is deployed * tmp: fix ci for aave org * revert: test ci secrets updates * chore: fix comments * chore: rm duplicate check * chore: reorder imports, pending automation * chore: import order * chore: imports order, re-frame comment * feat: update to latest spec: naming changes, setCurrentBridgedAmount, resolve existing pool bridgeAmount comment * chore: consistency, cleanup comment based on latest approach * upd: `directMint` & `directBurn` * feat: add `gho-core` lib temporarily, fix interface * feat: new gho ccip steward * feat: reference existing pool upgrade impl deployment * feat: more verbose and efficient * chore: upd ccip lib * test: cleanup & fuzzify * test: cleanup & fuzzify * chore: use latest deployment of existing remote pool upgrade * doc: snapshot -> direct-to-aip * chore: import gho from EthereumAssets instead of MiscEthereum * chore: cleanup rm ProxyAdmin reference * chore: rename facilitator * chore: rm duplicate check * fix: updates based on upstream changes from https://github.com/bgd-labs/aave-proposals-v3/pull/527 * chore: fix&add comment, use chain selector from helper lib * chore: fix chain selector lib typo * feat: accept existing proxy pool ownership as well * feat: add rate limit * feat: add deployed token pools & stewards * feat: rm `gho-core` & `ccip` * feat: upd arb token pool & steward deployment * feat: notify gho bucket steward * feat: update aave steward * chore: use existing IOwnable * chore: new ccip stewards with timelock getter * feat: use oz v5.1 proxies * doc: add discussion + spec * feat: chainlink pre-req have been performed * Updated GHOCCIP151Upgrade.md * Updated GHOCCIP151Upgrade.md Updated to permalinks * doc: lint and add steward addresses to spec md * doc: expand on bucket steward update --------- Co-authored-by: Parth Patel Co-authored-by: miguelmtzinf Co-authored-by: miguelmtz <36620902+miguelmtzinf@users.noreply.github.com> Co-authored-by: JP <89362795+CanonicalJP@users.noreply.github.com> --- ...bitrum_GHOCCIP151Upgrade_20241209_after.md | 5 + ...hereum_GHOCCIP151Upgrade_20241209_after.md | 5 + ...eV3Arbitrum_GHOCCIP151Upgrade_20241209.sol | 158 ++++ ...3Arbitrum_GHOCCIP151Upgrade_20241209.t.sol | 735 +++++++++++++++++ ...AaveV3E2E_GHOCCIP151Upgrade_20241209.t.sol | 623 +++++++++++++++ ...eV3Ethereum_GHOCCIP151Upgrade_20241209.sol | 123 +++ ...3Ethereum_GHOCCIP151Upgrade_20241209.t.sol | 749 ++++++++++++++++++ .../GHOCCIP151Upgrade.md | 61 ++ .../GHOCCIP151Upgrade_20241209.s.sol | 87 ++ .../config.ts | 17 + .../utils/CCIPUtils.sol | 172 ++++ src/interfaces/IGhoCcipSteward.sol | 6 + src/interfaces/IGhoToken.sol | 28 +- src/interfaces/ccip/IEVM2EVMOffRamp.sol | 21 +- src/interfaces/ccip/IEVM2EVMOnRamp.sol | 18 +- src/interfaces/ccip/IInternal.sol | 27 + src/interfaces/ccip/IPriceRegistry.sol | 44 + src/interfaces/ccip/IRouter.sol | 5 +- src/interfaces/ccip/ITokenAdminRegistry.sol | 50 +- src/interfaces/ccip/tokenPool/IPool.sol | 32 + .../IUpgradeableBurnMintTokenPool.sol | 220 +++++ .../IUpgradeableLockReleaseTokenPool.sol | 268 +++++++ 22 files changed, 3439 insertions(+), 15 deletions(-) create mode 100644 diffs/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_before_AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_after.md create mode 100644 diffs/AaveV3Ethereum_GHOCCIP151Upgrade_20241209_before_AaveV3Ethereum_GHOCCIP151Upgrade_20241209_after.md create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.sol create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.t.sol create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/AaveV3E2E_GHOCCIP151Upgrade_20241209.t.sol create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.sol create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.t.sol create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade.md create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade_20241209.s.sol create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/config.ts create mode 100644 src/20241209_Multi_GHOCCIP151Upgrade/utils/CCIPUtils.sol create mode 100644 src/interfaces/ccip/IPriceRegistry.sol create mode 100644 src/interfaces/ccip/tokenPool/IPool.sol create mode 100644 src/interfaces/ccip/tokenPool/IUpgradeableBurnMintTokenPool.sol create mode 100644 src/interfaces/ccip/tokenPool/IUpgradeableLockReleaseTokenPool.sol diff --git a/diffs/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_before_AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_after.md b/diffs/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_before_AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_before_AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/diffs/AaveV3Ethereum_GHOCCIP151Upgrade_20241209_before_AaveV3Ethereum_GHOCCIP151Upgrade_20241209_after.md b/diffs/AaveV3Ethereum_GHOCCIP151Upgrade_20241209_before_AaveV3Ethereum_GHOCCIP151Upgrade_20241209_after.md new file mode 100644 index 000000000..c15d3e2bc --- /dev/null +++ b/diffs/AaveV3Ethereum_GHOCCIP151Upgrade_20241209_before_AaveV3Ethereum_GHOCCIP151Upgrade_20241209_after.md @@ -0,0 +1,5 @@ +## Raw diff + +```json +{} +``` \ No newline at end of file diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.sol b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.sol new file mode 100644 index 000000000..455a652ee --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.sol @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ITransparentUpgradeableProxy} from 'solidity-utils/contracts/transparent-proxy/TransparentUpgradeableProxy.sol'; +import {IUpgradeableBurnMintTokenPool_1_4, IUpgradeableBurnMintTokenPool_1_5_1} from 'src/interfaces/ccip/tokenPool/IUpgradeableBurnMintTokenPool.sol'; +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {ITokenAdminRegistry} from 'src/interfaces/ccip/ITokenAdminRegistry.sol'; +import {IRateLimiter} from 'src/interfaces/ccip/IRateLimiter.sol'; +import {IProxyPool} from 'src/interfaces/ccip/IProxyPool.sol'; +import {ILegacyProxyAdmin} from 'src/interfaces/ILegacyProxyAdmin.sol'; +import {IGhoBucketSteward} from 'src/interfaces/IGhoBucketSteward.sol'; +import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; +import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {MiscArbitrum} from 'aave-address-book/MiscArbitrum.sol'; +import {GhoArbitrum} from 'aave-address-book/GhoArbitrum.sol'; +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; + +/** + * @title GHO CCIP 1.5.1 Upgrade + * @author Aave Labs + * - Discussion: https://governance.aave.com/t/technical-maintenance-proposals/15274/59 + */ +contract AaveV3Arbitrum_GHOCCIP151Upgrade_20241209 is IProposalGenericExecutor { + uint64 public constant ETH_CHAIN_SELECTOR = 5009297550715157269; + + // https://arbiscan.io/address/0x39AE1032cF4B334a1Ed41cdD0833bdD7c7E7751E + ITokenAdminRegistry public constant TOKEN_ADMIN_REGISTRY = + ITokenAdminRegistry(0x39AE1032cF4B334a1Ed41cdD0833bdD7c7E7751E); + + // https://arbiscan.io/address/0x26329558f08cbb40d6a4CCA0E0C67b29D64A8c50 + IProxyPool public constant EXISTING_PROXY_POOL = + IProxyPool(0x26329558f08cbb40d6a4CCA0E0C67b29D64A8c50); + // https://arbiscan.io/address/0xF168B83598516A532a85995b52504a2Fa058C068 + IUpgradeableBurnMintTokenPool_1_4 public constant EXISTING_TOKEN_POOL = + IUpgradeableBurnMintTokenPool_1_4(GhoArbitrum.GHO_CCIP_TOKEN_POOL); // will be updated in address-book after AIP + // https://arbiscan.io/address/0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB + IUpgradeableBurnMintTokenPool_1_5_1 public constant NEW_TOKEN_POOL = + IUpgradeableBurnMintTokenPool_1_5_1(0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB); + + // https://arbiscan.io/address/0xcd04d93bea13921dad05240d577090b5ac36dfca + address public constant EXISTING_GHO_AAVE_STEWARD = 0xCd04D93bEA13921DaD05240D577090b5AC36DfCA; + // https://arbiscan.io/address/0xd2D586f849620ef042FE3aF52eAa10e9b78bf7De + address public constant NEW_GHO_AAVE_STEWARD = 0xd2D586f849620ef042FE3aF52eAa10e9b78bf7De; + // https://arbiscan.io/address/0xa9afaE6A53E90f9E4CE0717162DF5Bc3d9aBe7B2 + IGhoBucketSteward public constant GHO_BUCKET_STEWARD = + IGhoBucketSteward(0xa9afaE6A53E90f9E4CE0717162DF5Bc3d9aBe7B2); + // https://arbiscan.io/address/0xCd5ab470AaC5c13e1063ee700503f3346b7C90Db + address public constant NEW_GHO_CCIP_STEWARD = 0xCd5ab470AaC5c13e1063ee700503f3346b7C90Db; + + // https://etherscan.io/address/0x9Ec9F9804733df96D1641666818eFb5198eC50f0 + address public constant EXISTING_REMOTE_POOL_ETH = 0x9Ec9F9804733df96D1641666818eFb5198eC50f0; // ProxyPool on ETH + // https://etherscan.io/address/0x06179f7C1be40863405f374E7f5F8806c728660A + address public constant NEW_REMOTE_POOL_ETH = 0x06179f7C1be40863405f374E7f5F8806c728660A; + + // https://arbiscan.io/address/0xA5Ba213867E175A182a5dd6A9193C6158738105A + // https://github.com/aave/ccip/commit/ca73ec8c4f7dc0f6a99ae1ea0acde43776c7b9bb + address public constant EXISTING_TOKEN_POOL_UPGRADE_IMPL = + 0xA5Ba213867E175A182a5dd6A9193C6158738105A; + + // https://arbiscan.io/address/0x7dfF72693f6A4149b17e7C6314655f6A9F7c8B33 + IGhoToken public constant GHO = IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING); + + // Token Rate Limit Capacity: 300_000 GHO + uint128 public constant CCIP_RATE_LIMIT_CAPACITY = 300_000e18; + // Token Rate Limit Refill Rate: 60 GHO per second (=> 216_000 GHO per hour) + uint128 public constant CCIP_RATE_LIMIT_REFILL_RATE = 60e18; + + function execute() external { + _acceptOwnership(); + _migrateLiquidity(); + _setupAndRegisterNewPool(); + _updateStewards(); + } + + // pre-req - chainlink transfers gho token pool ownership on token admin registry + function _acceptOwnership() internal { + EXISTING_PROXY_POOL.acceptOwnership(); + NEW_TOKEN_POOL.acceptOwnership(); + TOKEN_ADMIN_REGISTRY.acceptAdminRole(AaveV3ArbitrumAssets.GHO_UNDERLYING); + } + + function _migrateLiquidity() internal { + // bucketLevel === bridgedAmount + (uint256 bucketCapacity, uint256 bucketLevel) = GHO.getFacilitatorBucket( + address(EXISTING_TOKEN_POOL) + ); + + GHO.addFacilitator(address(NEW_TOKEN_POOL), 'CCIP TokenPool v1.5.1', uint128(bucketCapacity)); + NEW_TOKEN_POOL.directMint(address(EXISTING_TOKEN_POOL), bucketLevel); // increase facilitator level + + _upgradeExistingTokenPool(); // introduce `directBurn` method + EXISTING_TOKEN_POOL.directBurn(bucketLevel); // decrease facilitator level + + GHO.removeFacilitator(address(EXISTING_TOKEN_POOL)); + } + + function _setupAndRegisterNewPool() internal { + IRateLimiter.Config memory rateLimiterConfig = IRateLimiter.Config({ + isEnabled: true, + capacity: CCIP_RATE_LIMIT_CAPACITY, + rate: CCIP_RATE_LIMIT_REFILL_RATE + }); + + IUpgradeableBurnMintTokenPool_1_5_1.ChainUpdate[] + memory chains = new IUpgradeableBurnMintTokenPool_1_5_1.ChainUpdate[](1); + + bytes[] memory remotePoolAddresses = new bytes[](2); + remotePoolAddresses[0] = abi.encode(EXISTING_REMOTE_POOL_ETH); + remotePoolAddresses[1] = abi.encode(NEW_REMOTE_POOL_ETH); + + chains[0] = IUpgradeableBurnMintTokenPool_1_5_1.ChainUpdate({ + remoteChainSelector: ETH_CHAIN_SELECTOR, + remotePoolAddresses: remotePoolAddresses, + remoteTokenAddress: abi.encode(AaveV3EthereumAssets.GHO_UNDERLYING), + outboundRateLimiterConfig: rateLimiterConfig, + inboundRateLimiterConfig: rateLimiterConfig + }); + + // setup new pool + NEW_TOKEN_POOL.applyChainUpdates({ + remoteChainSelectorsToRemove: new uint64[](0), + chainsToAdd: chains + }); + + // register new pool + TOKEN_ADMIN_REGISTRY.setPool(address(GHO), address(NEW_TOKEN_POOL)); + } + + function _updateStewards() internal { + // Gho Aave Steward + AaveV3Arbitrum.ACL_MANAGER.revokeRole( + AaveV3Arbitrum.ACL_MANAGER.RISK_ADMIN_ROLE(), + EXISTING_GHO_AAVE_STEWARD + ); + AaveV3Arbitrum.ACL_MANAGER.grantRole( + AaveV3Arbitrum.ACL_MANAGER.RISK_ADMIN_ROLE(), + NEW_GHO_AAVE_STEWARD + ); + + // Gho Bucket Steward + address[] memory facilitatorList = new address[](1); + facilitatorList[0] = address(EXISTING_TOKEN_POOL); + GHO_BUCKET_STEWARD.setControlledFacilitator({facilitatorList: facilitatorList, approve: false}); + facilitatorList[0] = address(NEW_TOKEN_POOL); + GHO_BUCKET_STEWARD.setControlledFacilitator({facilitatorList: facilitatorList, approve: true}); + + // Gho Ccip Steward + NEW_TOKEN_POOL.setRateLimitAdmin(NEW_GHO_CCIP_STEWARD); + } + + function _upgradeExistingTokenPool() internal { + ILegacyProxyAdmin(MiscArbitrum.PROXY_ADMIN).upgrade( + ITransparentUpgradeableProxy(payable(address(EXISTING_TOKEN_POOL))), + EXISTING_TOKEN_POOL_UPGRADE_IMPL + ); + } +} diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.t.sol b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.t.sol new file mode 100644 index 000000000..faca9a22f --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.t.sol @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {IUpgradeableBurnMintTokenPool_1_4, IUpgradeableBurnMintTokenPool_1_5_1} from 'src/interfaces/ccip/tokenPool/IUpgradeableBurnMintTokenPool.sol'; +import {IPool as IPool_CCIP} from 'src/interfaces/ccip/tokenPool/IPool.sol'; +import {IClient} from 'src/interfaces/ccip/IClient.sol'; +import {IInternal} from 'src/interfaces/ccip/IInternal.sol'; +import {IRouter} from 'src/interfaces/ccip/IRouter.sol'; +import {IRateLimiter} from 'src/interfaces/ccip/IRateLimiter.sol'; +import {IProxyPool} from 'src/interfaces/ccip/IProxyPool.sol'; +import {IEVM2EVMOnRamp} from 'src/interfaces/ccip/IEVM2EVMOnRamp.sol'; +import {ITypeAndVersion} from 'src/interfaces/ccip/ITypeAndVersion.sol'; +import {IEVM2EVMOffRamp_1_5} from 'src/interfaces/ccip/IEVM2EVMOffRamp.sol'; +import {ITokenAdminRegistry} from 'src/interfaces/ccip/ITokenAdminRegistry.sol'; +import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; +import {IGhoAaveSteward} from 'src/interfaces/IGhoAaveSteward.sol'; +import {IGhoBucketSteward} from 'src/interfaces/IGhoBucketSteward.sol'; +import {IGhoCcipSteward} from 'src/interfaces/IGhoCcipSteward.sol'; +import {IOwnable} from 'aave-address-book/common/IOwnable.sol'; +import {DataTypes, IDefaultInterestRateStrategyV2} from 'aave-address-book/AaveV3.sol'; + +import {ReserveConfiguration} from 'aave-v3-origin/contracts/protocol/libraries/configuration/ReserveConfiguration.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {GhoArbitrum} from 'aave-address-book/GhoArbitrum.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; + +import {ProxyAdmin} from 'solidity-utils/contracts/transparent-proxy/ProxyAdmin.sol'; +import {CCIPUtils} from './utils/CCIPUtils.sol'; +import {AaveV3Arbitrum_GHOCCIP151Upgrade_20241209} from './AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.sol'; + +/** + * @dev Test for AaveV3Arbitrum_GHOCCIP151Upgrade_20241209 + * command: FOUNDRY_PROFILE=arbitrum forge test --match-path=src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.t.sol -vv + */ +contract AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_Base is ProtocolV3TestBase { + struct CCIPSendParams { + address sender; + uint256 amount; + CCIPUtils.PoolVersion poolVersion; + } + + uint64 internal constant ETH_CHAIN_SELECTOR = CCIPUtils.ETH_CHAIN_SELECTOR; + uint64 internal constant ARB_CHAIN_SELECTOR = CCIPUtils.ARB_CHAIN_SELECTOR; + uint256 internal constant CCIP_RATE_LIMIT_CAPACITY = 300_000e18; + uint256 internal constant CCIP_RATE_LIMIT_REFILL_RATE = 60e18; + + IGhoToken internal constant GHO = IGhoToken(AaveV3ArbitrumAssets.GHO_UNDERLYING); + ITokenAdminRegistry internal constant TOKEN_ADMIN_REGISTRY = + ITokenAdminRegistry(0x39AE1032cF4B334a1Ed41cdD0833bdD7c7E7751E); + address internal constant ETH_PROXY_POOL = 0x9Ec9F9804733df96D1641666818eFb5198eC50f0; + IEVM2EVMOnRamp internal constant ON_RAMP = + IEVM2EVMOnRamp(0x67761742ac8A21Ec4D76CA18cbd701e5A6F3Bef3); + IEVM2EVMOffRamp_1_5 internal constant OFF_RAMP = + IEVM2EVMOffRamp_1_5(0x91e46cc5590A4B9182e47f40006140A7077Dec31); + IRouter internal constant ROUTER = IRouter(0x141fa059441E0ca23ce184B6A78bafD2A517DdE8); + + IGhoAaveSteward public constant EXISTING_GHO_AAVE_STEWARD = + IGhoAaveSteward(0xCd04D93bEA13921DaD05240D577090b5AC36DfCA); + IGhoAaveSteward public constant NEW_GHO_AAVE_STEWARD = + IGhoAaveSteward(0xd2D586f849620ef042FE3aF52eAa10e9b78bf7De); + IGhoBucketSteward internal constant GHO_BUCKET_STEWARD = + IGhoBucketSteward(0xa9afaE6A53E90f9E4CE0717162DF5Bc3d9aBe7B2); + IGhoCcipSteward internal constant EXISTING_GHO_CCIP_STEWARD = + IGhoCcipSteward(0xb329CEFF2c362F315900d245eC88afd24C4949D5); + IGhoCcipSteward internal constant NEW_GHO_CCIP_STEWARD = + IGhoCcipSteward(0xCd5ab470AaC5c13e1063ee700503f3346b7C90Db); + + IProxyPool internal constant EXISTING_PROXY_POOL = + IProxyPool(0x26329558f08cbb40d6a4CCA0E0C67b29D64A8c50); + IUpgradeableBurnMintTokenPool_1_4 internal constant EXISTING_TOKEN_POOL = + IUpgradeableBurnMintTokenPool_1_4(0xF168B83598516A532a85995b52504a2Fa058C068); // GhoArbitrum.GHO_CCIP_TOKEN_POOL; will be updated in address-book after AIP + IUpgradeableBurnMintTokenPool_1_5_1 internal constant NEW_TOKEN_POOL = + IUpgradeableBurnMintTokenPool_1_5_1(0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB); + address internal constant NEW_REMOTE_POOL_ETH = 0x06179f7C1be40863405f374E7f5F8806c728660A; + + AaveV3Arbitrum_GHOCCIP151Upgrade_20241209 internal proposal; + + address internal alice = makeAddr('alice'); + address internal bob = makeAddr('bob'); + address internal carol = makeAddr('carol'); + + event Burned(address indexed sender, uint256 amount); + event Minted(address indexed sender, address indexed recipient, uint256 amount); + event CCIPSendRequested(IInternal.EVM2EVMMessage message); + + function setUp() public virtual { + vm.createSelectFork(vm.rpcUrl('arbitrum'), 293994020); + proposal = new AaveV3Arbitrum_GHOCCIP151Upgrade_20241209(); + _validateConstants(); + } + + function _validateConstants() private view { + assertEq(address(proposal.TOKEN_ADMIN_REGISTRY()), address(TOKEN_ADMIN_REGISTRY)); + assertEq(proposal.ETH_CHAIN_SELECTOR(), ETH_CHAIN_SELECTOR); + assertEq(address(proposal.EXISTING_PROXY_POOL()), address(EXISTING_PROXY_POOL)); + assertEq(address(proposal.EXISTING_TOKEN_POOL()), address(EXISTING_TOKEN_POOL)); + assertEq(address(proposal.EXISTING_REMOTE_POOL_ETH()), ETH_PROXY_POOL); + assertEq(address(proposal.NEW_TOKEN_POOL()), address(NEW_TOKEN_POOL)); + assertEq(address(proposal.EXISTING_GHO_AAVE_STEWARD()), address(EXISTING_GHO_AAVE_STEWARD)); + assertEq(address(proposal.NEW_GHO_AAVE_STEWARD()), address(NEW_GHO_AAVE_STEWARD)); + assertEq(address(proposal.GHO_BUCKET_STEWARD()), address(GHO_BUCKET_STEWARD)); + assertEq(address(proposal.NEW_GHO_CCIP_STEWARD()), address(NEW_GHO_CCIP_STEWARD)); + assertEq(address(proposal.NEW_REMOTE_POOL_ETH()), NEW_REMOTE_POOL_ETH); + assertEq(proposal.CCIP_RATE_LIMIT_CAPACITY(), CCIP_RATE_LIMIT_CAPACITY); + assertEq(proposal.CCIP_RATE_LIMIT_REFILL_RATE(), CCIP_RATE_LIMIT_REFILL_RATE); + + assertEq(address(proposal.EXISTING_PROXY_POOL()), EXISTING_TOKEN_POOL.getProxyPool()); + + assertEq(TOKEN_ADMIN_REGISTRY.typeAndVersion(), 'TokenAdminRegistry 1.5.0'); + assertEq(NEW_TOKEN_POOL.typeAndVersion(), 'BurnMintTokenPool 1.5.1'); + assertEq(EXISTING_TOKEN_POOL.typeAndVersion(), 'BurnMintTokenPool 1.4.0'); + assertEq( + ITypeAndVersion(EXISTING_TOKEN_POOL.getProxyPool()).typeAndVersion(), + 'BurnMintTokenPoolAndProxy 1.5.0' + ); + assertEq(ON_RAMP.typeAndVersion(), 'EVM2EVMOnRamp 1.5.0'); + assertEq(OFF_RAMP.typeAndVersion(), 'EVM2EVMOffRamp 1.5.0'); + + assertEq(ROUTER.typeAndVersion(), 'Router 1.2.0'); + assertEq(EXISTING_TOKEN_POOL.getRouter(), address(ROUTER)); + + assertEq(ROUTER.getOnRamp(ETH_CHAIN_SELECTOR), address(ON_RAMP)); + assertTrue(ROUTER.isOffRamp(ETH_CHAIN_SELECTOR, address(OFF_RAMP))); + + assertEq(EXISTING_TOKEN_POOL.getRateLimitAdmin(), address(EXISTING_GHO_CCIP_STEWARD)); + + assertTrue( + AaveV3Arbitrum.ACL_MANAGER.hasRole( + AaveV3Arbitrum.ACL_MANAGER.RISK_ADMIN_ROLE(), + address(EXISTING_GHO_AAVE_STEWARD) + ) + ); + + assertTrue(GHO.hasRole(GHO.BUCKET_MANAGER_ROLE(), address(GHO_BUCKET_STEWARD))); + + assertEq(IOwnable(address(NEW_GHO_AAVE_STEWARD)).owner(), GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq( + NEW_GHO_AAVE_STEWARD.POOL_ADDRESSES_PROVIDER(), + address(AaveV3Arbitrum.POOL_ADDRESSES_PROVIDER) + ); + assertEq( + NEW_GHO_AAVE_STEWARD.POOL_DATA_PROVIDER(), + address(AaveV3Arbitrum.AAVE_PROTOCOL_DATA_PROVIDER) + ); + assertEq(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL(), EXISTING_GHO_AAVE_STEWARD.RISK_COUNCIL()); + assertEq( + NEW_GHO_AAVE_STEWARD.getBorrowRateConfig(), + IGhoAaveSteward.BorrowRateConfig({ + optimalUsageRatioMaxChange: 5_00, + baseVariableBorrowRateMaxChange: 5_00, + variableRateSlope1MaxChange: 5_00, + variableRateSlope2MaxChange: 5_00 + }) + ); + + assertEq(EXISTING_TOKEN_POOL.getRateLimitAdmin(), address(EXISTING_GHO_CCIP_STEWARD)); + assertEq(NEW_GHO_CCIP_STEWARD.RISK_COUNCIL(), EXISTING_GHO_CCIP_STEWARD.RISK_COUNCIL()); + assertEq(NEW_GHO_CCIP_STEWARD.GHO_TOKEN(), AaveV3ArbitrumAssets.GHO_UNDERLYING); + assertEq(NEW_GHO_CCIP_STEWARD.GHO_TOKEN_POOL(), address(NEW_TOKEN_POOL)); + assertFalse(NEW_GHO_CCIP_STEWARD.BRIDGE_LIMIT_ENABLED()); // *not present* on remote token pool, only on eth + assertEq( + abi.encode(NEW_GHO_CCIP_STEWARD.getCcipTimelocks()), + abi.encode(IGhoCcipSteward.CcipDebounce(0, 0)) + ); + + assertEq(_getProxyAdmin(address(NEW_TOKEN_POOL)).UPGRADE_INTERFACE_VERSION(), '5.0.0'); + } + + function _getTokenMessage( + CCIPSendParams memory params + ) internal returns (IClient.EVM2AnyMessage memory, IInternal.EVM2EVMMessage memory) { + IClient.EVM2AnyMessage memory message = CCIPUtils.generateMessage(params.sender, 1); + message.tokenAmounts[0] = IClient.EVMTokenAmount({ + token: AaveV3ArbitrumAssets.GHO_UNDERLYING, + amount: params.amount + }); + + uint256 feeAmount = ROUTER.getFee(ETH_CHAIN_SELECTOR, message); + deal(params.sender, feeAmount); + + IInternal.EVM2EVMMessage memory eventArg = CCIPUtils.messageToEvent( + CCIPUtils.MessageToEventParams({ + message: message, + router: ROUTER, + sourceChainSelector: ARB_CHAIN_SELECTOR, + feeTokenAmount: feeAmount, + originalSender: params.sender, + sourceToken: AaveV3ArbitrumAssets.GHO_UNDERLYING, + destinationToken: AaveV3EthereumAssets.GHO_UNDERLYING, + poolVersion: params.poolVersion + }) + ); + + return (message, eventArg); + } + + function _getStaticParams(address tokenPool) internal view returns (bytes memory) { + IUpgradeableBurnMintTokenPool_1_4 ghoTokenPool = IUpgradeableBurnMintTokenPool_1_4(tokenPool); + return + abi.encode( + ghoTokenPool.getToken(), + ghoTokenPool.getAllowListEnabled(), + ghoTokenPool.getAllowList(), + ghoTokenPool.getRouter() + ); + } + + function _getDynamicParams(address tokenPool) internal view returns (bytes memory) { + IUpgradeableBurnMintTokenPool_1_4 ghoTokenPool = IUpgradeableBurnMintTokenPool_1_4(tokenPool); + return abi.encode(ghoTokenPool.owner(), ghoTokenPool.getSupportedChains()); + } + + function _tokenBucketToConfig( + IRateLimiter.TokenBucket memory bucket + ) internal pure returns (IRateLimiter.Config memory) { + return + IRateLimiter.Config({ + isEnabled: bucket.isEnabled, + capacity: bucket.capacity, + rate: bucket.rate + }); + } + + function _getDisabledConfig() internal pure returns (IRateLimiter.Config memory) { + return IRateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}); + } + + function _getImplementation(address proxy) internal view returns (address) { + bytes32 slot = bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1); + return address(uint160(uint256(vm.load(proxy, slot)))); + } + + function _getProxyAdmin(address proxy) internal view returns (ProxyAdmin) { + bytes32 slot = bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1); + return ProxyAdmin(address(uint160(uint256(vm.load(proxy, slot))))); + } + + function _readInitialized(address proxy) internal view returns (uint8) { + return uint8(uint256(vm.load(proxy, bytes32(0)))); + } + + function _getRateLimiterConfig() internal view returns (IRateLimiter.Config memory) { + return + IRateLimiter.Config({ + isEnabled: true, + capacity: proposal.CCIP_RATE_LIMIT_CAPACITY(), + rate: proposal.CCIP_RATE_LIMIT_REFILL_RATE() + }); + } + + function _getOutboundRefillTime(uint256 amount) internal pure returns (uint256) { + return (amount / CCIP_RATE_LIMIT_REFILL_RATE) + 1; // account for rounding + } + + function _getInboundRefillTime(uint256 amount) internal pure returns (uint256) { + return amount / CCIP_RATE_LIMIT_REFILL_RATE + 1; // account for rounding + } + + function _min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + function _isDifferenceLowerThanMax( + uint256 from, + uint256 to, + uint256 max + ) internal pure returns (bool) { + return from < to ? to - from <= max : from - to <= max; + } + + function assertEq( + IDefaultInterestRateStrategyV2.InterestRateData memory a, + IDefaultInterestRateStrategyV2.InterestRateData memory b + ) internal pure { + assertEq(a.optimalUsageRatio, b.optimalUsageRatio); + assertEq(a.baseVariableBorrowRate, b.baseVariableBorrowRate); + assertEq(a.variableRateSlope1, b.variableRateSlope1); + assertEq(a.variableRateSlope2, b.variableRateSlope2); + assertEq(abi.encode(a), abi.encode(b)); // sanity check + } + + function assertEq( + IGhoAaveSteward.BorrowRateConfig memory a, + IGhoAaveSteward.BorrowRateConfig memory b + ) internal pure { + assertEq(a.optimalUsageRatioMaxChange, b.optimalUsageRatioMaxChange); + assertEq(a.baseVariableBorrowRateMaxChange, b.baseVariableBorrowRateMaxChange); + assertEq(a.variableRateSlope1MaxChange, b.variableRateSlope1MaxChange); + assertEq(a.variableRateSlope2MaxChange, b.variableRateSlope2MaxChange); + assertEq(abi.encode(a), abi.encode(b)); // sanity check + } + + function assertEq( + IRateLimiter.TokenBucket memory bucket, + IRateLimiter.Config memory config + ) internal pure { + assertEq(bucket.isEnabled, config.isEnabled); + assertEq(bucket.capacity, config.capacity); + assertEq(bucket.rate, config.rate); + assertEq(abi.encode(_tokenBucketToConfig(bucket)), abi.encode(config)); // sanity check + } +} + +contract AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_SetupAndProposalActions is + AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_Base +{ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Arbitrum_GHOCCIP151Upgrade_20241209', + AaveV3Arbitrum.POOL, + address(proposal) + ); + } + + function test_tokenPoolOwnershipTransfer() public { + assertFalse( + TOKEN_ADMIN_REGISTRY.isAdministrator(address(GHO), GovernanceV3Arbitrum.EXECUTOR_LVL_1) + ); + ITokenAdminRegistry.TokenConfig memory tokenConfig = TOKEN_ADMIN_REGISTRY.getTokenConfig( + address(GHO) + ); + assertNotEq(tokenConfig.administrator, GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq(tokenConfig.pendingAdministrator, GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq(tokenConfig.tokenPool, address(EXISTING_PROXY_POOL)); + + assertEq(EXISTING_TOKEN_POOL.owner(), GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq(EXISTING_PROXY_POOL.owner(), TOKEN_ADMIN_REGISTRY.owner()); + assertEq(NEW_TOKEN_POOL.owner(), address(0)); + + executePayload(vm, address(proposal)); + + tokenConfig = TOKEN_ADMIN_REGISTRY.getTokenConfig(address(GHO)); + assertEq(tokenConfig.administrator, GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq(tokenConfig.pendingAdministrator, address(0)); + assertEq(tokenConfig.tokenPool, address(NEW_TOKEN_POOL)); + + assertEq(EXISTING_TOKEN_POOL.owner(), GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq(EXISTING_PROXY_POOL.owner(), GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq(NEW_TOKEN_POOL.owner(), GovernanceV3Arbitrum.EXECUTOR_LVL_1); + } + + function test_tokenPoolLiquidityMigration() public { + IGhoToken.Facilitator memory existingFacilitator = GHO.getFacilitator( + address(EXISTING_TOKEN_POOL) + ); + IGhoToken.Facilitator memory newFacilitator = GHO.getFacilitator(address(NEW_TOKEN_POOL)); + + assertEq(bytes(newFacilitator.label).length, 0); + assertEq(newFacilitator.bucketCapacity, 0); + assertEq(newFacilitator.bucketLevel, 0); + + assertEq(existingFacilitator.label, 'CCIP TokenPool'); + assertGt(existingFacilitator.bucketCapacity, 0); + assertGt(existingFacilitator.bucketLevel, 0); + + executePayload(vm, address(proposal)); + + newFacilitator = GHO.getFacilitator(address(NEW_TOKEN_POOL)); + + assertEq(newFacilitator.label, 'CCIP TokenPool v1.5.1'); + assertEq(newFacilitator.bucketCapacity, existingFacilitator.bucketCapacity); + assertEq(newFacilitator.bucketLevel, existingFacilitator.bucketLevel); + + existingFacilitator = GHO.getFacilitator(address(EXISTING_TOKEN_POOL)); + + assertEq(bytes(existingFacilitator.label).length, 0); + assertEq(existingFacilitator.bucketCapacity, 0); + assertEq(existingFacilitator.bucketLevel, 0); + } + + function test_newTokenPoolSetupAndRegistration() public { + bytes memory staticParams = _getStaticParams(address(EXISTING_TOKEN_POOL)); + bytes memory dynamicParams = _getDynamicParams(address(EXISTING_TOKEN_POOL)); + assertEq(EXISTING_TOKEN_POOL.getRateLimitAdmin(), address(EXISTING_GHO_CCIP_STEWARD)); + + assertEq(TOKEN_ADMIN_REGISTRY.getPool(address(GHO)), EXISTING_TOKEN_POOL.getProxyPool()); + + executePayload(vm, address(proposal)); + + assertEq(staticParams, _getStaticParams(address(NEW_TOKEN_POOL))); + assertEq(dynamicParams, _getDynamicParams(address(NEW_TOKEN_POOL))); + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(NEW_GHO_CCIP_STEWARD)); + + assertEq(TOKEN_ADMIN_REGISTRY.getPool(address(GHO)), address(NEW_TOKEN_POOL)); + + assertEq(NEW_TOKEN_POOL.getRemotePools(ETH_CHAIN_SELECTOR).length, 2); + assertTrue(NEW_TOKEN_POOL.isRemotePool(ETH_CHAIN_SELECTOR, abi.encode(ETH_PROXY_POOL))); + assertTrue(NEW_TOKEN_POOL.isRemotePool(ETH_CHAIN_SELECTOR, abi.encode(NEW_REMOTE_POOL_ETH))); + assertEq( + NEW_TOKEN_POOL.getRemoteToken(ETH_CHAIN_SELECTOR), + abi.encode(AaveV3EthereumAssets.GHO_UNDERLYING) + ); + assertEq(NEW_TOKEN_POOL.getSupportedChains().length, 1); + assertTrue(NEW_TOKEN_POOL.isSupportedChain(ETH_CHAIN_SELECTOR)); + + assertEq( + NEW_TOKEN_POOL.getCurrentInboundRateLimiterState(ETH_CHAIN_SELECTOR), + _getRateLimiterConfig() + ); + assertEq( + NEW_TOKEN_POOL.getCurrentOutboundRateLimiterState(ETH_CHAIN_SELECTOR), + _getRateLimiterConfig() + ); + } + + function test_newTokenPoolInitialization() public { + vm.expectRevert('Initializable: contract is already initialized'); + NEW_TOKEN_POOL.initialize(makeAddr('owner'), new address[](0), makeAddr('router')); + assertEq(_readInitialized(address(NEW_TOKEN_POOL)), 1); + assertEq(_readInitialized(_getImplementation(address(NEW_TOKEN_POOL))), 255); + } + + function test_stewardRolesAndConfig() public { + bytes32 RISK_ADMIN_ROLE = AaveV3Arbitrum.ACL_MANAGER.RISK_ADMIN_ROLE(); + assertTrue( + AaveV3Arbitrum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(EXISTING_GHO_AAVE_STEWARD)) + ); + assertFalse(AaveV3Arbitrum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(NEW_GHO_AAVE_STEWARD))); + + assertEq(GHO_BUCKET_STEWARD.getControlledFacilitators().length, 1); + assertTrue(GHO_BUCKET_STEWARD.isControlledFacilitator(address(EXISTING_TOKEN_POOL))); + assertFalse(GHO_BUCKET_STEWARD.isControlledFacilitator(address(NEW_TOKEN_POOL))); + + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(0)); + + executePayload(vm, address(proposal)); + + assertFalse( + AaveV3Arbitrum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(EXISTING_GHO_AAVE_STEWARD)) + ); + assertTrue(AaveV3Arbitrum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(NEW_GHO_AAVE_STEWARD))); + + assertEq(GHO_BUCKET_STEWARD.getControlledFacilitators().length, 1); + assertFalse(GHO_BUCKET_STEWARD.isControlledFacilitator(address(EXISTING_TOKEN_POOL))); + assertTrue(GHO_BUCKET_STEWARD.isControlledFacilitator(address(NEW_TOKEN_POOL))); + + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(NEW_GHO_CCIP_STEWARD)); + } +} + +contract AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_PostUpgrade is + AaveV3Arbitrum_GHOCCIP151Upgrade_20241209_Base +{ + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + + function setUp() public override { + super.setUp(); + + executePayload(vm, address(proposal)); + } + + function test_sendMessageSucceedsAndRoutesViaNewPool(uint256 amount) public { + uint256 bridgeableAmount = _min( + GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel, + CCIP_RATE_LIMIT_CAPACITY + ); + amount = bound(amount, 1, bridgeableAmount); + skip(_getOutboundRefillTime(amount)); // wait for the rate limiter to refill + + deal(address(GHO), alice, amount); + vm.prank(alice); + GHO.approve(address(ROUTER), amount); + + uint256 aliceBalance = GHO.balanceOf(alice); + uint256 bucketLevel = GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel; + + ( + IClient.EVM2AnyMessage memory message, + IInternal.EVM2EVMMessage memory eventArg + ) = _getTokenMessage( + CCIPSendParams({amount: amount, sender: alice, poolVersion: CCIPUtils.PoolVersion.V1_5_1}) + ); + + vm.expectEmit(address(NEW_TOKEN_POOL)); // new token pool + emit Burned(address(ON_RAMP), amount); + + vm.expectEmit(address(ON_RAMP)); + emit CCIPSendRequested(eventArg); + + vm.prank(alice); + ROUTER.ccipSend{value: eventArg.feeTokenAmount}(ETH_CHAIN_SELECTOR, message); + + assertEq(GHO.balanceOf(alice), aliceBalance - amount); + assertEq(GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel, bucketLevel - amount); + } + + // existing pool can no longer on ramp + function test_lockOrBurnRevertsOnExistingPool() public { + uint256 amount = 100_000e18; + skip(_getOutboundRefillTime(amount)); + + // router pulls tokens from the user & sends to the token pool during onRamps + deal(address(GHO), address(EXISTING_TOKEN_POOL), amount); + + vm.prank(EXISTING_TOKEN_POOL.getProxyPool()); + // underflow expected at GHO.burn() => bucketLevel - amount + vm.expectRevert(stdError.arithmeticError); + EXISTING_TOKEN_POOL.lockOrBurn( + alice, + abi.encode(alice), + amount, + ETH_CHAIN_SELECTOR, + new bytes(0) + ); + } + + // on-ramp via new pool + function test_lockOrBurnSucceedsOnNewPool(uint256 amount) public { + uint256 bridgeableAmount = _min( + GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel, + CCIP_RATE_LIMIT_CAPACITY + ); + amount = bound(amount, 1, bridgeableAmount); + skip(_getOutboundRefillTime(amount)); + + // router pulls tokens from the user & sends to the token pool during onRamps + deal(address(GHO), address(NEW_TOKEN_POOL), amount); + + uint256 bucketLevel = GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel; + + vm.expectEmit(address(NEW_TOKEN_POOL)); + emit Burned(address(ON_RAMP), amount); + + vm.prank(address(ON_RAMP)); + NEW_TOKEN_POOL.lockOrBurn( + IPool_CCIP.LockOrBurnInV1({ + receiver: abi.encode(alice), + remoteChainSelector: ETH_CHAIN_SELECTOR, + originalSender: alice, + amount: amount, + localToken: address(GHO) + }) + ); + + assertEq(GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel, bucketLevel - amount); + assertEq(GHO.balanceOf(address(NEW_TOKEN_POOL)), 0); // dealt amount is burned + } + + // existing pool can no longer off ramp + function test_releaseOrMintRevertsOnExistingPool() public { + uint256 amount = 100_000e18; + skip(_getInboundRefillTime(amount)); // wait for the rate limiter to refill + + vm.prank(EXISTING_TOKEN_POOL.getProxyPool()); + vm.expectRevert('FACILITATOR_BUCKET_CAPACITY_EXCEEDED'); + EXISTING_TOKEN_POOL.releaseOrMint( + abi.encode(alice), + alice, + amount, + ETH_CHAIN_SELECTOR, + new bytes(0) + ); + } + + // off-ramp messages sent from new eth token pool (v1.5.1) + function test_releaseOrMintSucceedsOnNewPoolOffRampedViaNewTokenPoolEth(uint256 amount) public { + (uint256 bucketCapacity, uint256 bucketLevel) = GHO.getFacilitatorBucket( + address(NEW_TOKEN_POOL) + ); + uint256 mintAbleAmount = _min(bucketCapacity - bucketLevel, CCIP_RATE_LIMIT_CAPACITY); + amount = bound(amount, 1, mintAbleAmount); + skip(_getInboundRefillTime(amount)); // wait for the rate limiter to refill + + uint256 aliceBalance = GHO.balanceOf(alice); + + vm.expectEmit(address(NEW_TOKEN_POOL)); + emit Minted(address(OFF_RAMP), alice, amount); + + vm.prank(address(OFF_RAMP)); + NEW_TOKEN_POOL.releaseOrMint( + IPool_CCIP.ReleaseOrMintInV1({ + originalSender: abi.encode(alice), + remoteChainSelector: ETH_CHAIN_SELECTOR, + receiver: alice, + amount: amount, + localToken: address(GHO), + sourcePoolAddress: abi.encode(address(NEW_REMOTE_POOL_ETH)), + sourcePoolData: new bytes(0), + offchainTokenData: new bytes(0) + }) + ); + + assertEq(GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel, bucketLevel + amount); + assertEq(GHO.balanceOf(alice), aliceBalance + amount); + } + + // off-ramp messages sent from existing eth token pool (v1.4) ie ProxyPool + function test_releaseOrMintSucceedsOnNewPoolOffRampedViaExistingTokenPoolEth( + uint256 amount + ) public { + (uint256 bucketCapacity, uint256 bucketLevel) = GHO.getFacilitatorBucket( + address(NEW_TOKEN_POOL) + ); + uint256 mintAbleAmount = _min(bucketCapacity - bucketLevel, CCIP_RATE_LIMIT_CAPACITY); + amount = bound(amount, 1, mintAbleAmount); + skip(_getInboundRefillTime(amount)); + + uint256 aliceBalance = GHO.balanceOf(alice); + + vm.expectEmit(address(NEW_TOKEN_POOL)); + emit Minted(address(OFF_RAMP), alice, amount); + + vm.prank(address(OFF_RAMP)); + NEW_TOKEN_POOL.releaseOrMint( + IPool_CCIP.ReleaseOrMintInV1({ + originalSender: abi.encode(alice), + remoteChainSelector: ETH_CHAIN_SELECTOR, + receiver: alice, + amount: amount, + localToken: address(GHO), + sourcePoolAddress: abi.encode(address(ETH_PROXY_POOL)), + sourcePoolData: new bytes(0), + offchainTokenData: new bytes(0) + }) + ); + + assertEq(GHO.getFacilitator(address(NEW_TOKEN_POOL)).bucketLevel, bucketLevel + amount); + assertEq(GHO.balanceOf(alice), aliceBalance + amount); + } + + function test_ccipStewardCanChangeAndDisableRateLimit() public { + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(NEW_GHO_CCIP_STEWARD)); // sanity + + IRateLimiter.Config memory outboundConfig = IRateLimiter.Config({ + isEnabled: true, + capacity: 500_000e18, + rate: 100e18 + }); + IRateLimiter.Config memory inboundConfig = IRateLimiter.Config({ + isEnabled: true, + capacity: 100_000e18, + rate: 50e18 + }); + + // we assert the steward can change the rate limit + vm.prank(NEW_GHO_CCIP_STEWARD.RISK_COUNCIL()); + NEW_GHO_CCIP_STEWARD.updateRateLimit( + ETH_CHAIN_SELECTOR, + outboundConfig.isEnabled, + outboundConfig.capacity, + outboundConfig.rate, + inboundConfig.isEnabled, + inboundConfig.capacity, + inboundConfig.rate + ); + + assertEq(NEW_TOKEN_POOL.getCurrentOutboundRateLimiterState(ETH_CHAIN_SELECTOR), outboundConfig); + assertEq(NEW_TOKEN_POOL.getCurrentInboundRateLimiterState(ETH_CHAIN_SELECTOR), inboundConfig); + assertEq(NEW_GHO_CCIP_STEWARD.getCcipTimelocks().rateLimitLastUpdate, vm.getBlockTimestamp()); + + skip(NEW_GHO_CCIP_STEWARD.MINIMUM_DELAY() + 1); + + // now we assert the steward can disable the rate limit + vm.prank(NEW_GHO_CCIP_STEWARD.RISK_COUNCIL()); + NEW_GHO_CCIP_STEWARD.updateRateLimit(ETH_CHAIN_SELECTOR, false, 0, 0, false, 0, 0); + + assertEq( + NEW_TOKEN_POOL.getCurrentOutboundRateLimiterState(ETH_CHAIN_SELECTOR), + _getDisabledConfig() + ); + assertEq( + NEW_TOKEN_POOL.getCurrentInboundRateLimiterState(ETH_CHAIN_SELECTOR), + _getDisabledConfig() + ); + assertEq(NEW_GHO_CCIP_STEWARD.getCcipTimelocks().rateLimitLastUpdate, vm.getBlockTimestamp()); + } + + function test_aaveStewardCanUpdateBorrowRate() public { + IDefaultInterestRateStrategyV2 irStrategy = IDefaultInterestRateStrategyV2( + AaveV3Arbitrum.AAVE_PROTOCOL_DATA_PROVIDER.getInterestRateStrategyAddress(address(GHO)) + ); + + IDefaultInterestRateStrategyV2.InterestRateData + memory currentRateData = IDefaultInterestRateStrategyV2.InterestRateData({ + optimalUsageRatio: 90_00, + baseVariableBorrowRate: 0, + variableRateSlope1: 12_50, + variableRateSlope2: 40_00 + }); + + assertEq(irStrategy.getInterestRateDataBps(address(GHO)), currentRateData); + + currentRateData.variableRateSlope1 -= 4_00; + currentRateData.variableRateSlope2 -= 3_00; + + vm.prank(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL()); + NEW_GHO_AAVE_STEWARD.updateGhoBorrowRate( + currentRateData.optimalUsageRatio, + currentRateData.baseVariableBorrowRate, + currentRateData.variableRateSlope1, + currentRateData.variableRateSlope2 + ); + + assertEq(irStrategy.getInterestRateDataBps(address(GHO)), currentRateData); + assertEq( + NEW_GHO_AAVE_STEWARD.getGhoTimelocks().ghoBorrowRateLastUpdate, + vm.getBlockTimestamp() + ); + } + + function test_aaveStewardCanUpdateBorrowCap(uint256 newBorrowCap) public { + uint256 currentBorrowCap = AaveV3Arbitrum.POOL.getConfiguration(address(GHO)).getBorrowCap(); + assertEq(currentBorrowCap, 22_500_000); + vm.assume( + newBorrowCap != currentBorrowCap && + _isDifferenceLowerThanMax(currentBorrowCap, newBorrowCap, currentBorrowCap) + ); + + vm.prank(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL()); + NEW_GHO_AAVE_STEWARD.updateGhoBorrowCap(newBorrowCap); + + assertEq(AaveV3Arbitrum.POOL.getConfiguration(address(GHO)).getBorrowCap(), newBorrowCap); + assertEq(NEW_GHO_AAVE_STEWARD.getGhoTimelocks().ghoBorrowCapLastUpdate, vm.getBlockTimestamp()); + } + + function test_aaveStewardCanUpdateSupplyCap(uint256 newSupplyCap) public { + uint256 currentSupplyCap = AaveV3Arbitrum.POOL.getConfiguration(address(GHO)).getSupplyCap(); + assertEq(currentSupplyCap, 25_000_000); + vm.assume( + currentSupplyCap != newSupplyCap && + _isDifferenceLowerThanMax(currentSupplyCap, newSupplyCap, currentSupplyCap) + ); + + vm.prank(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL()); + NEW_GHO_AAVE_STEWARD.updateGhoSupplyCap(newSupplyCap); + + assertEq(AaveV3Arbitrum.POOL.getConfiguration(address(GHO)).getSupplyCap(), newSupplyCap); + assertEq(NEW_GHO_AAVE_STEWARD.getGhoTimelocks().ghoSupplyCapLastUpdate, vm.getBlockTimestamp()); + } +} diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3E2E_GHOCCIP151Upgrade_20241209.t.sol b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3E2E_GHOCCIP151Upgrade_20241209.t.sol new file mode 100644 index 000000000..b0695928d --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3E2E_GHOCCIP151Upgrade_20241209.t.sol @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {IUpgradeableLockReleaseTokenPool_1_4, IUpgradeableLockReleaseTokenPool_1_5_1} from 'src/interfaces/ccip/tokenPool/IUpgradeableLockReleaseTokenPool.sol'; +import {IUpgradeableBurnMintTokenPool_1_4, IUpgradeableBurnMintTokenPool_1_5_1} from 'src/interfaces/ccip/tokenPool/IUpgradeableBurnMintTokenPool.sol'; +import {IClient} from 'src/interfaces/ccip/IClient.sol'; +import {IInternal} from 'src/interfaces/ccip/IInternal.sol'; +import {IRouter} from 'src/interfaces/ccip/IRouter.sol'; +import {IEVM2EVMOnRamp} from 'src/interfaces/ccip/IEVM2EVMOnRamp.sol'; +import {IEVM2EVMOffRamp_1_5} from 'src/interfaces/ccip/IEVM2EVMOffRamp.sol'; +import {ITokenAdminRegistry} from 'src/interfaces/ccip/ITokenAdminRegistry.sol'; +import {IPriceRegistry} from 'src/interfaces/ccip/IPriceRegistry.sol'; +import {IProxyPool} from 'src/interfaces/ccip/IProxyPool.sol'; +import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; +import {IGhoCcipSteward} from 'src/interfaces/IGhoCcipSteward.sol'; + +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol'; +import {MiscArbitrum} from 'aave-address-book/MiscArbitrum.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {GovernanceV3Arbitrum} from 'aave-address-book/GovernanceV3Arbitrum.sol'; + +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; + +import {CCIPUtils} from './utils/CCIPUtils.sol'; +import {AaveV3Ethereum_GHOCCIP151Upgrade_20241209} from './AaveV3Ethereum_GHOCCIP151Upgrade_20241209.sol'; +import {AaveV3Arbitrum_GHOCCIP151Upgrade_20241209} from './AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.sol'; + +/** + * @dev Test for AaveV3E2E_GHOCCIP151Upgrade_20241209 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241209_Multi_GHOCCIP151Upgrade/AaveV3E2E_GHOCCIP151Upgrade_20241209.t.sol -vv + */ +contract AaveV3E2E_GHOCCIP151Upgrade_20241209_Base is ProtocolV3TestBase { + struct CCIPSendParams { + IRouter router; + IGhoToken token; + uint256 amount; + uint64 sourceChainSelector; + uint64 destinationChainSelector; + address sender; + CCIPUtils.PoolVersion poolVersion; + } + + struct Common { + IRouter router; + IGhoToken token; + IEVM2EVMOnRamp EVM2EVMOnRamp; + IEVM2EVMOffRamp_1_5 EVM2EVMOffRamp; + ITokenAdminRegistry tokenAdminRegistry; + IGhoCcipSteward newGhoCcipSteward; + IPriceRegistry priceRegistry; + address proxyPool; + uint64 chainSelector; + uint256 forkId; + } + + struct L1 { + AaveV3Ethereum_GHOCCIP151Upgrade_20241209 proposal; + IUpgradeableLockReleaseTokenPool_1_5_1 newTokenPool; + IUpgradeableLockReleaseTokenPool_1_4 existingTokenPool; + Common c; + } + + struct L2 { + AaveV3Arbitrum_GHOCCIP151Upgrade_20241209 proposal; + IUpgradeableBurnMintTokenPool_1_5_1 newTokenPool; + IUpgradeableBurnMintTokenPool_1_4 existingTokenPool; + Common c; + } + + L1 internal l1; + L2 internal l2; + + address internal alice = makeAddr('alice'); + address internal bob = makeAddr('bob'); + address internal carol = makeAddr('carol'); + + uint256 internal constant CCIP_RATE_LIMIT_CAPACITY = 300_000e18; + uint256 internal constant CCIP_RATE_LIMIT_REFILL_RATE = 60e18; + + event CCIPSendRequested(IInternal.EVM2EVMMessage message); + event Locked(address indexed sender, uint256 amount); + event Burned(address indexed sender, uint256 amount); + event Released(address indexed sender, address indexed recipient, uint256 amount); + event Minted(address indexed sender, address indexed recipient, uint256 amount); + + function setUp() public virtual { + l1.c.forkId = vm.createFork(vm.rpcUrl('mainnet'), 21594804); + l2.c.forkId = vm.createFork(vm.rpcUrl('arbitrum'), 293994020); + + vm.selectFork(l1.c.forkId); + l1.proposal = new AaveV3Ethereum_GHOCCIP151Upgrade_20241209(); + l1.existingTokenPool = IUpgradeableLockReleaseTokenPool_1_4( + 0x5756880B6a1EAba0175227bf02a7E87c1e02B28C + ); // MiscEthereum.GHO_CCIP_TOKEN_POOL; will be updated in address-book after AIP + l1.newTokenPool = IUpgradeableLockReleaseTokenPool_1_5_1( + 0x06179f7C1be40863405f374E7f5F8806c728660A + ); + l1.c.router = IRouter(l1.existingTokenPool.getRouter()); + l2.c.chainSelector = l1.existingTokenPool.getSupportedChains()[0]; + l1.c.token = IGhoToken(address(l1.existingTokenPool.getToken())); + l1.c.EVM2EVMOnRamp = IEVM2EVMOnRamp(l1.c.router.getOnRamp(l2.c.chainSelector)); + l1.c.EVM2EVMOffRamp = IEVM2EVMOffRamp_1_5(0xdf615eF8D4C64d0ED8Fd7824BBEd2f6a10245aC9); // new offramp + l1.c.tokenAdminRegistry = ITokenAdminRegistry(0xb22764f98dD05c789929716D677382Df22C05Cb6); + l1.c.priceRegistry = IPriceRegistry(l1.c.EVM2EVMOnRamp.getDynamicConfig().priceRegistry); + l1.c.proxyPool = l1.existingTokenPool.getProxyPool(); + + vm.selectFork(l2.c.forkId); + l2.proposal = new AaveV3Arbitrum_GHOCCIP151Upgrade_20241209(); + l2.existingTokenPool = IUpgradeableBurnMintTokenPool_1_4( + 0xF168B83598516A532a85995b52504a2Fa058C068 + ); // MiscArbitrum.GHO_CCIP_TOKEN_POOL; will be updated in address-book after AIP + l2.newTokenPool = IUpgradeableBurnMintTokenPool_1_5_1( + 0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB + ); + l2.c.router = IRouter(l2.existingTokenPool.getRouter()); + l1.c.chainSelector = l2.existingTokenPool.getSupportedChains()[0]; + l2.c.token = IGhoToken(address(l2.existingTokenPool.getToken())); + l2.c.EVM2EVMOnRamp = IEVM2EVMOnRamp(l2.c.router.getOnRamp(l1.c.chainSelector)); + l2.c.EVM2EVMOffRamp = IEVM2EVMOffRamp_1_5(0x91e46cc5590A4B9182e47f40006140A7077Dec31); // new offramp + l2.c.tokenAdminRegistry = ITokenAdminRegistry(0x39AE1032cF4B334a1Ed41cdD0833bdD7c7E7751E); + l2.c.priceRegistry = IPriceRegistry(l2.c.EVM2EVMOnRamp.getDynamicConfig().priceRegistry); + l2.c.proxyPool = l2.existingTokenPool.getProxyPool(); + + _validateConfig({upgraded: false}); + } + + function _getTokenMessage( + CCIPSendParams memory params + ) internal returns (IClient.EVM2AnyMessage memory, IInternal.EVM2EVMMessage memory) { + IClient.EVM2AnyMessage memory message = CCIPUtils.generateMessage(params.sender, 1); + message.tokenAmounts[0] = IClient.EVMTokenAmount({ + token: address(params.token), + amount: params.amount + }); + + uint256 feeAmount = params.router.getFee(params.destinationChainSelector, message); + deal(params.sender, feeAmount); + + IInternal.EVM2EVMMessage memory eventArg = CCIPUtils.messageToEvent( + CCIPUtils.MessageToEventParams({ + message: message, + router: params.router, + sourceChainSelector: params.sourceChainSelector, + feeTokenAmount: feeAmount, + originalSender: params.sender, + sourceToken: address(params.token), + destinationToken: address(params.token == l1.c.token ? l2.c.token : l1.c.token), + poolVersion: params.poolVersion + }) + ); + + return (message, eventArg); + } + + function _validateConfig(bool upgraded) internal { + vm.selectFork(l1.c.forkId); + assertEq(l1.c.chainSelector, 5009297550715157269); + assertEq(address(l1.c.token), AaveV3EthereumAssets.GHO_UNDERLYING); + assertEq(l1.c.router.typeAndVersion(), 'Router 1.2.0'); + assertEq(l1.c.EVM2EVMOnRamp.typeAndVersion(), 'EVM2EVMOnRamp 1.5.0'); + assertEq(l1.c.EVM2EVMOffRamp.typeAndVersion(), 'EVM2EVMOffRamp 1.5.0'); + assertEq(l1.existingTokenPool.typeAndVersion(), 'LockReleaseTokenPool 1.4.0'); + assertEq(l1.newTokenPool.typeAndVersion(), 'LockReleaseTokenPool 1.5.1'); + assertEq(l1.c.tokenAdminRegistry.typeAndVersion(), 'TokenAdminRegistry 1.5.0'); + assertEq(l1.c.priceRegistry.typeAndVersion(), 'PriceRegistry 1.2.0'); + assertEq( + l1.c.EVM2EVMOnRamp.getDynamicConfig().priceRegistry, + l1.c.EVM2EVMOffRamp.getDynamicConfig().priceRegistry + ); + assertTrue(l1.c.router.isOffRamp(l2.c.chainSelector, address(l1.c.EVM2EVMOffRamp))); + assertEq(l1.c.router.getOnRamp(l2.c.chainSelector), address(l1.c.EVM2EVMOnRamp)); + + // proposal constants + assertEq(address(l1.proposal.TOKEN_ADMIN_REGISTRY()), address(l1.c.tokenAdminRegistry)); + assertEq(l1.proposal.ARB_CHAIN_SELECTOR(), l2.c.chainSelector); + assertEq(address(l1.proposal.EXISTING_PROXY_POOL()), l1.c.proxyPool); + assertEq(address(l1.proposal.EXISTING_TOKEN_POOL()), address(l1.existingTokenPool)); + assertEq(address(l1.proposal.EXISTING_REMOTE_POOL_ARB()), l2.c.proxyPool); + assertEq(address(l1.proposal.NEW_TOKEN_POOL()), address(l1.newTokenPool)); + assertEq(address(l1.proposal.NEW_REMOTE_POOL_ARB()), address(l2.newTokenPool)); + assertEq(l1.proposal.CCIP_RATE_LIMIT_CAPACITY(), CCIP_RATE_LIMIT_CAPACITY); + assertEq(l1.proposal.CCIP_RATE_LIMIT_REFILL_RATE(), CCIP_RATE_LIMIT_REFILL_RATE); + + if (upgraded) { + assertEq(IProxyPool(l1.c.proxyPool).owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq(l1.newTokenPool.owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1); + + assertEq(l1.c.tokenAdminRegistry.getPool(address(l1.c.token)), address(l1.newTokenPool)); + + assertEq(l1.c.token.balanceOf(address(l1.existingTokenPool)), 0); + // we are not resetting currentBridgedAmount on the existing pool, the pool is deprecated by + // resetting the bridge limit + assertNotEq(l1.existingTokenPool.getCurrentBridgedAmount(), 0); + assertEq(l1.existingTokenPool.getBridgeLimit(), 0); + + assertGt(l1.c.token.balanceOf(address(l1.newTokenPool)), 0); + assertGt(l1.newTokenPool.getCurrentBridgedAmount(), 0); + assertGt(l1.newTokenPool.getBridgeLimit(), 0); + } else { + assertEq(l1.c.tokenAdminRegistry.getPool(address(l1.c.token)), l1.c.proxyPool); + + assertGt(l1.c.token.balanceOf(address(l1.existingTokenPool)), 0); + assertGt(l1.existingTokenPool.getCurrentBridgedAmount(), 0); + assertGt(l1.existingTokenPool.getBridgeLimit(), 0); + + assertEq(l1.c.token.balanceOf(address(l1.newTokenPool)), 0); + assertEq(l1.newTokenPool.getCurrentBridgedAmount(), 0); + assertGt(l1.newTokenPool.getBridgeLimit(), 0); + } + + vm.selectFork(l2.c.forkId); + assertEq(l2.c.chainSelector, 4949039107694359620); + assertEq(address(l2.c.token), AaveV3ArbitrumAssets.GHO_UNDERLYING); + assertEq(l2.c.router.typeAndVersion(), 'Router 1.2.0'); + assertEq(l2.c.EVM2EVMOnRamp.typeAndVersion(), 'EVM2EVMOnRamp 1.5.0'); + assertEq(l2.c.EVM2EVMOffRamp.typeAndVersion(), 'EVM2EVMOffRamp 1.5.0'); + assertEq(l2.existingTokenPool.typeAndVersion(), 'BurnMintTokenPool 1.4.0'); + assertEq(l2.newTokenPool.typeAndVersion(), 'BurnMintTokenPool 1.5.1'); + assertEq(l2.c.tokenAdminRegistry.typeAndVersion(), 'TokenAdminRegistry 1.5.0'); + assertEq(l2.c.priceRegistry.typeAndVersion(), 'PriceRegistry 1.2.0'); + assertEq( + l2.c.EVM2EVMOnRamp.getDynamicConfig().priceRegistry, + l2.c.EVM2EVMOffRamp.getDynamicConfig().priceRegistry + ); + assertTrue(l2.c.router.isOffRamp(l1.c.chainSelector, address(l2.c.EVM2EVMOffRamp))); + assertEq(l2.c.router.getOnRamp(l1.c.chainSelector), address(l2.c.EVM2EVMOnRamp)); + + // proposal constants + assertEq(address(l2.proposal.TOKEN_ADMIN_REGISTRY()), address(l2.c.tokenAdminRegistry)); + assertEq(l2.proposal.ETH_CHAIN_SELECTOR(), l1.c.chainSelector); + assertEq(address(l2.proposal.EXISTING_PROXY_POOL()), l2.c.proxyPool); + assertEq(address(l2.proposal.EXISTING_TOKEN_POOL()), address(l2.existingTokenPool)); + assertEq(address(l2.proposal.EXISTING_REMOTE_POOL_ETH()), l1.c.proxyPool); + assertEq(address(l2.proposal.NEW_TOKEN_POOL()), address(l2.newTokenPool)); + assertEq(address(l2.proposal.NEW_REMOTE_POOL_ETH()), address(l1.newTokenPool)); + assertEq(l2.proposal.CCIP_RATE_LIMIT_CAPACITY(), CCIP_RATE_LIMIT_CAPACITY); + assertEq(l2.proposal.CCIP_RATE_LIMIT_REFILL_RATE(), CCIP_RATE_LIMIT_REFILL_RATE); + + if (upgraded) { + assertEq(IProxyPool(l2.c.proxyPool).owner(), GovernanceV3Arbitrum.EXECUTOR_LVL_1); + assertEq(l2.newTokenPool.owner(), GovernanceV3Arbitrum.EXECUTOR_LVL_1); + + assertEq(l2.c.tokenAdminRegistry.getPool(address(l2.c.token)), address(l2.newTokenPool)); + assertEq(bytes(l2.c.token.getFacilitator(address(l2.existingTokenPool)).label).length, 0); + assertEq(l2.c.token.getFacilitator(address(l2.newTokenPool)).label, 'CCIP TokenPool v1.5.1'); + } else { + assertEq(l2.c.tokenAdminRegistry.getPool(address(l2.c.token)), l2.c.proxyPool); + assertEq(l2.c.token.getFacilitator(address(l2.existingTokenPool)).label, 'CCIP TokenPool'); + assertEq(bytes(l2.c.token.getFacilitator(address(l2.newTokenPool)).label).length, 0); + } + } + + function _executeUpgradeAIP() internal { + vm.selectFork(l1.c.forkId); + executePayload(vm, address(l1.proposal)); + vm.selectFork(l2.c.forkId); + executePayload(vm, address(l2.proposal)); + } + + function _getOutboundRefillTime(uint256 amount) internal pure returns (uint256) { + return (amount / CCIP_RATE_LIMIT_REFILL_RATE) + 1; // account for rounding + } + + function _getInboundRefillTime(uint256 amount) internal pure returns (uint256) { + return (amount / CCIP_RATE_LIMIT_REFILL_RATE) + 1; // account for rounding + } + + function _min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + // @dev refresh token prices to the last stored such that price is not stale + // @dev assumed c.forkId is already active + function _refreshGasAndTokenPrices(Common memory c) internal { + uint64 destChainSelector = c.forkId == l1.c.forkId ? l2.c.chainSelector : l1.c.chainSelector; + address bridgeToken = address(c.token); + address feeToken = c.router.getWrappedNative(); // needed as we do tests with wrapped native as fee token + address linkToken = c.EVM2EVMOnRamp.getStaticConfig().linkToken; // needed as feeTokenAmount is converted to linkTokenAmount + IInternal.TokenPriceUpdate[] memory tokenPriceUpdates = new IInternal.TokenPriceUpdate[](3); + IInternal.GasPriceUpdate[] memory gasPriceUpdates = new IInternal.GasPriceUpdate[](1); + + tokenPriceUpdates[0] = IInternal.TokenPriceUpdate({ + sourceToken: bridgeToken, + usdPerToken: c.priceRegistry.getTokenPrice(bridgeToken).value + }); + tokenPriceUpdates[1] = IInternal.TokenPriceUpdate({ + sourceToken: feeToken, + usdPerToken: c.priceRegistry.getTokenPrice(feeToken).value + }); + tokenPriceUpdates[2] = IInternal.TokenPriceUpdate({ + sourceToken: linkToken, + usdPerToken: c.priceRegistry.getTokenPrice(linkToken).value + }); + + gasPriceUpdates[0] = IInternal.GasPriceUpdate({ + destChainSelector: destChainSelector, + usdPerUnitGas: c.priceRegistry.getDestinationChainGasPrice(destChainSelector).value + }); + + vm.prank(c.priceRegistry.owner()); + c.priceRegistry.updatePrices( + IInternal.PriceUpdates({ + tokenPriceUpdates: tokenPriceUpdates, + gasPriceUpdates: gasPriceUpdates + }) + ); + } + + // post upgrade + function _runEthToArb(address user, uint256 amount) internal { + vm.selectFork(l1.c.forkId); + + skip(_getOutboundRefillTime(amount)); // wait for the rate limiter to refill + _refreshGasAndTokenPrices(l1.c); + + vm.prank(user); + l1.c.token.approve(address(l1.c.router), amount); + + uint256 tokenPoolBalance = l1.c.token.balanceOf(address(l1.newTokenPool)); + uint256 userBalance = l1.c.token.balanceOf(user); + uint256 bridgedAmount = l1.newTokenPool.getCurrentBridgedAmount(); + + ( + IClient.EVM2AnyMessage memory message, + IInternal.EVM2EVMMessage memory eventArg + ) = _getTokenMessage( + CCIPSendParams({ + router: l1.c.router, + token: l1.c.token, + amount: amount, + sourceChainSelector: l1.c.chainSelector, + destinationChainSelector: l2.c.chainSelector, + sender: user, + poolVersion: CCIPUtils.PoolVersion.V1_5_1 + }) + ); + + vm.expectEmit(address(l1.newTokenPool)); + emit Locked(address(l1.c.EVM2EVMOnRamp), amount); + + vm.expectEmit(address(l1.c.EVM2EVMOnRamp)); + emit CCIPSendRequested(eventArg); + vm.prank(user); + l1.c.router.ccipSend{value: eventArg.feeTokenAmount}(l2.c.chainSelector, message); + + assertEq(l1.c.token.balanceOf(address(l1.newTokenPool)), tokenPoolBalance + amount); + assertEq(l1.c.token.balanceOf(user), userBalance - amount); + assertEq(l1.newTokenPool.getCurrentBridgedAmount(), bridgedAmount + amount); + + // ARB executeMessage + vm.selectFork(l2.c.forkId); + + skip(_getInboundRefillTime(amount)); + _refreshGasAndTokenPrices(l2.c); + + userBalance = l2.c.token.balanceOf(user); + uint256 bucketLevel = l2.c.token.getFacilitator(address(l2.newTokenPool)).bucketLevel; + + vm.expectEmit(address(l2.newTokenPool)); + emit Minted(address(l2.c.EVM2EVMOffRamp), user, amount); + vm.prank(address(l2.c.EVM2EVMOffRamp)); + l2.c.EVM2EVMOffRamp.executeSingleMessage( + eventArg, + new bytes[](message.tokenAmounts.length), + new uint32[](0) + ); + + assertEq(l2.c.token.balanceOf(user), userBalance + amount); + assertEq(l2.c.token.getFacilitator(address(l2.newTokenPool)).bucketLevel, bucketLevel + amount); + } + + // post upgrade + function _runArbToEth(address user, uint256 amount) internal { + vm.selectFork(l2.c.forkId); + + skip(_getOutboundRefillTime(amount)); + _refreshGasAndTokenPrices(l2.c); + + vm.prank(user); + l2.c.token.approve(address(l2.c.router), amount); + + uint256 userBalance = l2.c.token.balanceOf(user); + uint256 bucketLevel = l2.c.token.getFacilitator(address(l2.newTokenPool)).bucketLevel; + + ( + IClient.EVM2AnyMessage memory message, + IInternal.EVM2EVMMessage memory eventArg + ) = _getTokenMessage( + CCIPSendParams({ + router: l2.c.router, + token: l2.c.token, + amount: amount, + sourceChainSelector: l2.c.chainSelector, + destinationChainSelector: l1.c.chainSelector, + sender: user, + poolVersion: CCIPUtils.PoolVersion.V1_5_1 + }) + ); + + vm.expectEmit(address(l2.newTokenPool)); + emit Burned(address(l2.c.EVM2EVMOnRamp), amount); + + vm.expectEmit(address(l2.c.EVM2EVMOnRamp)); + emit CCIPSendRequested(eventArg); + vm.prank(user); + l2.c.router.ccipSend{value: eventArg.feeTokenAmount}(l1.c.chainSelector, message); + + assertEq(l2.c.token.balanceOf(user), userBalance - amount); + assertEq(l2.c.token.getFacilitator(address(l2.newTokenPool)).bucketLevel, bucketLevel - amount); + + // ETH executeMessage + vm.selectFork(l1.c.forkId); + + skip(_getInboundRefillTime(amount)); + _refreshGasAndTokenPrices(l1.c); + + uint256 tokenPoolBalance = l1.c.token.balanceOf(address(l1.newTokenPool)); + uint256 bridgedAmount = l1.newTokenPool.getCurrentBridgedAmount(); + userBalance = l1.c.token.balanceOf(user); + + vm.expectEmit(address(l1.newTokenPool)); + emit Released(address(l1.c.EVM2EVMOffRamp), user, amount); + vm.prank(address(l1.c.EVM2EVMOffRamp)); + l1.c.EVM2EVMOffRamp.executeSingleMessage( + eventArg, + new bytes[](message.tokenAmounts.length), + new uint32[](1) // tokenGasOverrides + ); + + assertEq(l1.c.token.balanceOf(address(l1.newTokenPool)), tokenPoolBalance - amount); + assertEq(l1.newTokenPool.getCurrentBridgedAmount(), bridgedAmount - amount); + assertEq(l1.c.token.balanceOf(user), userBalance + amount); + } +} + +contract AaveV3E2E_GHOCCIP151Upgrade_20241209_PostUpgrade is + AaveV3E2E_GHOCCIP151Upgrade_20241209_Base +{ + function setUp() public override { + super.setUp(); + + _executeUpgradeAIP(); + + _validateConfig({upgraded: true}); + } + + function test_E2E_FromEth(uint256 amount) public { + vm.selectFork(l1.c.forkId); + uint256 bridgeableAmount = _min( + l1.newTokenPool.getBridgeLimit() - l1.newTokenPool.getCurrentBridgedAmount(), + CCIP_RATE_LIMIT_CAPACITY + ); + amount = bound(amount, 1, bridgeableAmount); + deal(address(l1.c.token), alice, amount); + + _runEthToArb(alice, amount); + _runArbToEth(alice, amount); + } + + function test_E2E_FromArb(uint256 amount) public { + vm.selectFork(l2.c.forkId); + uint256 bridgeableAmount = _min( + l2.c.token.getFacilitator(address(l2.newTokenPool)).bucketLevel, + CCIP_RATE_LIMIT_CAPACITY + ); + + amount = bound(amount, 1, bridgeableAmount); + deal(address(l2.c.token), alice, amount); + + _runArbToEth(alice, amount); + _runEthToArb(alice, amount); + } +} + +// sendMsg => upgrade => executeMsg +contract AaveV3E2E_GHOCCIP151Upgrade_20241209_InFlightUpgrade is + AaveV3E2E_GHOCCIP151Upgrade_20241209_Base +{ + function test_E2E_InFlightMsg_FromEth() public { + vm.selectFork(l1.c.forkId); + + uint256 amount = 100_000e18; + deal(address(l1.c.token), alice, amount); + skip(_getOutboundRefillTime(amount)); + + vm.prank(alice); + l1.c.token.approve(address(l1.c.router), amount); + + uint256 tokenPoolBalance = l1.c.token.balanceOf(address(l1.existingTokenPool)); + uint256 aliceBalance = l1.c.token.balanceOf(alice); + uint256 bridgedAmount = l1.existingTokenPool.getCurrentBridgedAmount(); + + ( + IClient.EVM2AnyMessage memory message, + IInternal.EVM2EVMMessage memory eventArg + ) = _getTokenMessage( + CCIPSendParams({ + router: l1.c.router, + token: l1.c.token, + amount: amount, + sourceChainSelector: l1.c.chainSelector, + destinationChainSelector: l2.c.chainSelector, + sender: alice, + poolVersion: CCIPUtils.PoolVersion.V1_5_0 // existing token pool + }) + ); + + // message sent from existing token pool, pre-AIP-execution + vm.expectEmit(address(l1.existingTokenPool)); + emit Locked(l1.c.proxyPool, amount); + vm.expectEmit(l1.c.proxyPool); + emit Locked(address(l1.c.EVM2EVMOnRamp), amount); + + vm.expectEmit(address(l1.c.EVM2EVMOnRamp)); + emit CCIPSendRequested(eventArg); + vm.prank(alice); + l1.c.router.ccipSend{value: eventArg.feeTokenAmount}(l2.c.chainSelector, message); + + assertEq(l1.c.token.balanceOf(address(l1.existingTokenPool)), tokenPoolBalance + amount); + assertEq(l1.c.token.balanceOf(alice), aliceBalance - amount); + assertEq(l1.existingTokenPool.getCurrentBridgedAmount(), bridgedAmount + amount); + + _executeUpgradeAIP(); // token pools upgraded + + // ARB executeMessage + vm.selectFork(l2.c.forkId); + + skip(_getInboundRefillTime(amount)); + aliceBalance = l2.c.token.balanceOf(alice); + uint256 bucketLevel = l2.c.token.getFacilitator(address(l2.newTokenPool)).bucketLevel; + + vm.expectEmit(address(l2.newTokenPool)); + emit Minted(address(l2.c.EVM2EVMOffRamp), alice, amount); + vm.prank(address(l2.c.EVM2EVMOffRamp)); + l2.c.EVM2EVMOffRamp.executeSingleMessage( + eventArg, // pre-upgrade message + new bytes[](message.tokenAmounts.length), + new uint32[](0) + ); + + assertEq(l2.c.token.balanceOf(alice), aliceBalance + amount); + assertEq(l2.c.token.getFacilitator(address(l2.newTokenPool)).bucketLevel, bucketLevel + amount); + + // send tokens back to eth + _runArbToEth(alice, amount); + } + + function test_E2E_InFlightMsg_FromArb() public { + vm.selectFork(l2.c.forkId); + + uint256 amount = 100_000e18; + deal(address(l2.c.token), alice, amount); + skip(_getOutboundRefillTime(amount)); + + vm.prank(alice); + l2.c.token.approve(address(l2.c.router), amount); + + uint256 aliceBalance = l2.c.token.balanceOf(alice); + uint256 bucketLevel = l2.c.token.getFacilitator(address(l2.existingTokenPool)).bucketLevel; + + ( + IClient.EVM2AnyMessage memory message, + IInternal.EVM2EVMMessage memory eventArg + ) = _getTokenMessage( + CCIPSendParams({ + router: l2.c.router, + token: l2.c.token, + amount: amount, + sourceChainSelector: l2.c.chainSelector, + destinationChainSelector: l1.c.chainSelector, + sender: alice, + poolVersion: CCIPUtils.PoolVersion.V1_5_0 // existing token pool + }) + ); + + // message sent from existing token pool, pre-AIP-execution + vm.expectEmit(address(l2.existingTokenPool)); + emit Burned(l2.c.proxyPool, amount); + vm.expectEmit(l2.c.proxyPool); + emit Burned(address(l2.c.EVM2EVMOnRamp), amount); + + vm.expectEmit(address(l2.c.EVM2EVMOnRamp)); + emit CCIPSendRequested(eventArg); + vm.prank(alice); + l2.c.router.ccipSend{value: eventArg.feeTokenAmount}(l1.c.chainSelector, message); + + assertEq(l2.c.token.balanceOf(alice), aliceBalance - amount); + assertEq( + l2.c.token.getFacilitator(address(l2.existingTokenPool)).bucketLevel, + bucketLevel - amount + ); + + _executeUpgradeAIP(); // token pools upgraded + + // ETH executeMessage + vm.selectFork(l1.c.forkId); + + skip(_getInboundRefillTime(amount)); + uint256 tokenPoolBalance = l1.c.token.balanceOf(address(l1.newTokenPool)); + uint256 bridgedAmount = l1.newTokenPool.getCurrentBridgedAmount(); + aliceBalance = l1.c.token.balanceOf(alice); + + vm.expectEmit(address(l1.newTokenPool)); + emit Released(address(l1.c.EVM2EVMOffRamp), alice, amount); + vm.prank(address(l1.c.EVM2EVMOffRamp)); + l1.c.EVM2EVMOffRamp.executeSingleMessage( + eventArg, // pre-upgrade message + new bytes[](message.tokenAmounts.length), + new uint32[](1) // tokenGasOverrides + ); + + assertEq(l1.c.token.balanceOf(address(l1.newTokenPool)), tokenPoolBalance - amount); + assertEq(l1.newTokenPool.getCurrentBridgedAmount(), bridgedAmount - amount); + assertEq(l1.c.token.balanceOf(alice), aliceBalance + amount); + + // send tokens back to arb + _runEthToArb(alice, amount); + } +} diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.sol b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.sol new file mode 100644 index 000000000..c06228b86 --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IUpgradeableLockReleaseTokenPool_1_4, IUpgradeableLockReleaseTokenPool_1_5_1} from 'src/interfaces/ccip/tokenPool/IUpgradeableLockReleaseTokenPool.sol'; +import {IProposalGenericExecutor} from 'aave-helpers/src/interfaces/IProposalGenericExecutor.sol'; +import {ITokenAdminRegistry} from 'src/interfaces/ccip/ITokenAdminRegistry.sol'; +import {IProxyPool} from 'src/interfaces/ccip/IProxyPool.sol'; +import {IRateLimiter} from 'src/interfaces/ccip/IRateLimiter.sol'; +import {GhoEthereum} from 'aave-address-book/GhoEthereum.sol'; +import {AaveV3EthereumAssets, AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; + +/** + * @title GHO CCIP 1.5.1 Upgrade + * @author Aave Labs + * - Discussion: https://governance.aave.com/t/technical-maintenance-proposals/15274/59 + */ +contract AaveV3Ethereum_GHOCCIP151Upgrade_20241209 is IProposalGenericExecutor { + uint64 public constant ARB_CHAIN_SELECTOR = 4949039107694359620; + + // https://etherscan.io/address/0xb22764f98dD05c789929716D677382Df22C05Cb6 + ITokenAdminRegistry public constant TOKEN_ADMIN_REGISTRY = + ITokenAdminRegistry(0xb22764f98dD05c789929716D677382Df22C05Cb6); + + // https://etherscan.io/address/0x9Ec9F9804733df96D1641666818eFb5198eC50f0 + IProxyPool public constant EXISTING_PROXY_POOL = + IProxyPool(0x9Ec9F9804733df96D1641666818eFb5198eC50f0); + // https://etherscan.io/address/0x5756880B6a1EAba0175227bf02a7E87c1e02B28C + IUpgradeableLockReleaseTokenPool_1_4 public constant EXISTING_TOKEN_POOL = + IUpgradeableLockReleaseTokenPool_1_4(GhoEthereum.GHO_CCIP_TOKEN_POOL); // will be updated in address-book after AIP + // https://etherscan.io/address/0x06179f7C1be40863405f374E7f5F8806c728660A + IUpgradeableLockReleaseTokenPool_1_5_1 public constant NEW_TOKEN_POOL = + IUpgradeableLockReleaseTokenPool_1_5_1(0x06179f7C1be40863405f374E7f5F8806c728660A); + + // https://etherscan.io/address/0xFEb4e54591660F42288312AE8eB59e9f2B746b66 + address public constant EXISTING_GHO_AAVE_STEWARD = 0xFEb4e54591660F42288312AE8eB59e9f2B746b66; + // https://etherscan.io/address/0x98217A06721Ebf727f2C8d9aD7718ec28b7aAe34 + address public constant NEW_GHO_AAVE_STEWARD = 0x98217A06721Ebf727f2C8d9aD7718ec28b7aAe34; + // https://etherscan.io/address/0xC5BcC58BE6172769ca1a78B8A45752E3C5059c39 + address public constant NEW_GHO_CCIP_STEWARD = 0xC5BcC58BE6172769ca1a78B8A45752E3C5059c39; + + // https://arbiscan.io/address/0x26329558f08cbb40d6a4CCA0E0C67b29D64A8c50 + address public constant EXISTING_REMOTE_POOL_ARB = 0x26329558f08cbb40d6a4CCA0E0C67b29D64A8c50; // ProxyPool on Arb + // https://arbiscan.io/address/0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB + address public constant NEW_REMOTE_POOL_ARB = 0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB; + + // Token Rate Limit Capacity: 300_000 GHO + uint128 public constant CCIP_RATE_LIMIT_CAPACITY = 300_000e18; + // Token Rate Limit Refill Rate: 60 GHO per second (=> 216_000 GHO per hour) + uint128 public constant CCIP_RATE_LIMIT_REFILL_RATE = 60e18; + + function execute() external { + _acceptOwnership(); + _migrateLiquidity(); + _setupAndRegisterNewPool(); + _updateStewards(); + } + + // pre-req - chainlink transfers gho token pool ownership on token admin registry + function _acceptOwnership() internal { + EXISTING_PROXY_POOL.acceptOwnership(); + NEW_TOKEN_POOL.acceptOwnership(); + TOKEN_ADMIN_REGISTRY.acceptAdminRole(AaveV3EthereumAssets.GHO_UNDERLYING); + } + + function _migrateLiquidity() internal { + EXISTING_TOKEN_POOL.setRebalancer(address(NEW_TOKEN_POOL)); + uint256 bridgeAmount = EXISTING_TOKEN_POOL.getCurrentBridgedAmount(); + NEW_TOKEN_POOL.transferLiquidity(address(EXISTING_TOKEN_POOL), bridgeAmount); + NEW_TOKEN_POOL.setCurrentBridgedAmount(bridgeAmount); + + // disable existing pool + EXISTING_TOKEN_POOL.setBridgeLimit(0); + } + + function _setupAndRegisterNewPool() internal { + IRateLimiter.Config memory rateLimiterConfig = IRateLimiter.Config({ + isEnabled: true, + capacity: CCIP_RATE_LIMIT_CAPACITY, + rate: CCIP_RATE_LIMIT_REFILL_RATE + }); + + IUpgradeableLockReleaseTokenPool_1_5_1.ChainUpdate[] + memory chains = new IUpgradeableLockReleaseTokenPool_1_5_1.ChainUpdate[](1); + + bytes[] memory remotePoolAddresses = new bytes[](2); + remotePoolAddresses[0] = abi.encode(EXISTING_REMOTE_POOL_ARB); + remotePoolAddresses[1] = abi.encode(NEW_REMOTE_POOL_ARB); + + chains[0] = IUpgradeableLockReleaseTokenPool_1_5_1.ChainUpdate({ + remoteChainSelector: ARB_CHAIN_SELECTOR, + remotePoolAddresses: remotePoolAddresses, + remoteTokenAddress: abi.encode(AaveV3ArbitrumAssets.GHO_UNDERLYING), + outboundRateLimiterConfig: rateLimiterConfig, + inboundRateLimiterConfig: rateLimiterConfig + }); + + // setup new pool + NEW_TOKEN_POOL.applyChainUpdates({ + remoteChainSelectorsToRemove: new uint64[](0), + chainsToAdd: chains + }); + + // register new pool + TOKEN_ADMIN_REGISTRY.setPool(AaveV3EthereumAssets.GHO_UNDERLYING, address(NEW_TOKEN_POOL)); + } + + function _updateStewards() internal { + // Gho Aave Steward + AaveV3Ethereum.ACL_MANAGER.revokeRole( + AaveV3Ethereum.ACL_MANAGER.RISK_ADMIN_ROLE(), + EXISTING_GHO_AAVE_STEWARD + ); + AaveV3Ethereum.ACL_MANAGER.grantRole( + AaveV3Ethereum.ACL_MANAGER.RISK_ADMIN_ROLE(), + NEW_GHO_AAVE_STEWARD + ); + + // Gho Ccip Steward + NEW_TOKEN_POOL.setRateLimitAdmin(NEW_GHO_CCIP_STEWARD); + NEW_TOKEN_POOL.setBridgeLimitAdmin(NEW_GHO_CCIP_STEWARD); + } +} diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.t.sol b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.t.sol new file mode 100644 index 000000000..3e6fde3f3 --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.t.sol @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import 'forge-std/Test.sol'; + +import {IUpgradeableLockReleaseTokenPool_1_4, IUpgradeableLockReleaseTokenPool_1_5_1} from 'src/interfaces/ccip/tokenPool/IUpgradeableLockReleaseTokenPool.sol'; +import {IPool as IPool_CCIP} from 'src/interfaces/ccip/tokenPool/IPool.sol'; +import {IClient} from 'src/interfaces/ccip/IClient.sol'; +import {IInternal} from 'src/interfaces/ccip/IInternal.sol'; +import {IRouter} from 'src/interfaces/ccip/IRouter.sol'; +import {IRateLimiter} from 'src/interfaces/ccip/IRateLimiter.sol'; +import {IProxyPool} from 'src/interfaces/ccip/IProxyPool.sol'; +import {IEVM2EVMOnRamp} from 'src/interfaces/ccip/IEVM2EVMOnRamp.sol'; +import {ITypeAndVersion} from 'src/interfaces/ccip/ITypeAndVersion.sol'; +import {IEVM2EVMOffRamp_1_5} from 'src/interfaces/ccip/IEVM2EVMOffRamp.sol'; +import {ITokenAdminRegistry} from 'src/interfaces/ccip/ITokenAdminRegistry.sol'; +import {IGhoToken} from 'src/interfaces/IGhoToken.sol'; +import {IGhoAaveSteward} from 'src/interfaces/IGhoAaveSteward.sol'; +import {IGhoCcipSteward} from 'src/interfaces/IGhoCcipSteward.sol'; +import {IOwnable} from 'aave-address-book/common/IOwnable.sol'; +import {DataTypes, IDefaultInterestRateStrategyV2} from 'aave-address-book/AaveV3.sol'; + +import {ReserveConfiguration} from 'aave-v3-origin/contracts/protocol/libraries/configuration/ReserveConfiguration.sol'; +import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; +import {GhoEthereum} from 'aave-address-book/GhoEthereum.sol'; +import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol'; +import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol'; +import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol'; +import {AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol'; + +import {ProxyAdmin} from 'solidity-utils/contracts/transparent-proxy/ProxyAdmin.sol'; +import {CCIPUtils} from './utils/CCIPUtils.sol'; +import {AaveV3Ethereum_GHOCCIP151Upgrade_20241209} from './AaveV3Ethereum_GHOCCIP151Upgrade_20241209.sol'; + +/** + * @dev Test for AaveV3Ethereum_GHOCCIP151Upgrade_20241209 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241209_Multi_GHOCCIP151Upgrade/AaveV3Ethereum_GHOCCIP151Upgrade_20241209.t.sol -vv + */ +contract AaveV3Ethereum_GHOCCIP151Upgrade_20241209_Base is ProtocolV3TestBase { + struct CCIPSendParams { + address sender; + uint256 amount; + CCIPUtils.PoolVersion poolVersion; + } + + uint64 internal constant ETH_CHAIN_SELECTOR = CCIPUtils.ETH_CHAIN_SELECTOR; + uint64 internal constant ARB_CHAIN_SELECTOR = CCIPUtils.ARB_CHAIN_SELECTOR; + uint256 internal constant CCIP_RATE_LIMIT_CAPACITY = 300_000e18; + uint256 internal constant CCIP_RATE_LIMIT_REFILL_RATE = 60e18; + + IGhoToken internal constant GHO = IGhoToken(AaveV3EthereumAssets.GHO_UNDERLYING); + ITokenAdminRegistry internal constant TOKEN_ADMIN_REGISTRY = + ITokenAdminRegistry(0xb22764f98dD05c789929716D677382Df22C05Cb6); + address internal constant ARB_PROXY_POOL = 0x26329558f08cbb40d6a4CCA0E0C67b29D64A8c50; + IEVM2EVMOnRamp internal constant ON_RAMP = + IEVM2EVMOnRamp(0x69eCC4E2D8ea56E2d0a05bF57f4Fd6aEE7f2c284); + IEVM2EVMOffRamp_1_5 internal constant OFF_RAMP = + IEVM2EVMOffRamp_1_5(0xdf615eF8D4C64d0ED8Fd7824BBEd2f6a10245aC9); + IRouter internal constant ROUTER = IRouter(0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D); + + IGhoAaveSteward public constant EXISTING_GHO_AAVE_STEWARD = + IGhoAaveSteward(0xFEb4e54591660F42288312AE8eB59e9f2B746b66); + IGhoAaveSteward public constant NEW_GHO_AAVE_STEWARD = + IGhoAaveSteward(0x98217A06721Ebf727f2C8d9aD7718ec28b7aAe34); + IGhoCcipSteward internal constant EXISTING_GHO_CCIP_STEWARD = + IGhoCcipSteward(0x101Efb7b9Beb073B1219Cd5473a7C8A2f2EB84f4); + IGhoCcipSteward internal constant NEW_GHO_CCIP_STEWARD = + IGhoCcipSteward(0xC5BcC58BE6172769ca1a78B8A45752E3C5059c39); + + IProxyPool internal constant EXISTING_PROXY_POOL = + IProxyPool(0x9Ec9F9804733df96D1641666818eFb5198eC50f0); + IUpgradeableLockReleaseTokenPool_1_4 internal constant EXISTING_TOKEN_POOL = + IUpgradeableLockReleaseTokenPool_1_4(0x5756880B6a1EAba0175227bf02a7E87c1e02B28C); // GhoEthereum.GHO_CCIP_TOKEN_POOL; will be updated in address-book after AIP + IUpgradeableLockReleaseTokenPool_1_5_1 internal constant NEW_TOKEN_POOL = + IUpgradeableLockReleaseTokenPool_1_5_1(0x06179f7C1be40863405f374E7f5F8806c728660A); + address internal constant NEW_REMOTE_POOL_ARB = 0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB; + + AaveV3Ethereum_GHOCCIP151Upgrade_20241209 internal proposal; + + address internal alice = makeAddr('alice'); + address internal bob = makeAddr('bob'); + address internal carol = makeAddr('carol'); + + event Locked(address indexed sender, uint256 amount); + event Released(address indexed sender, address indexed recipient, uint256 amount); + event CCIPSendRequested(IInternal.EVM2EVMMessage message); + + error BridgeLimitExceeded(uint256 limit); + + function setUp() public virtual { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21594804); + proposal = new AaveV3Ethereum_GHOCCIP151Upgrade_20241209(); + _validateConstants(); + } + + function _validateConstants() private view { + assertEq(address(proposal.TOKEN_ADMIN_REGISTRY()), address(TOKEN_ADMIN_REGISTRY)); + assertEq(proposal.ARB_CHAIN_SELECTOR(), ARB_CHAIN_SELECTOR); + assertEq(address(proposal.EXISTING_PROXY_POOL()), address(EXISTING_PROXY_POOL)); + assertEq(address(proposal.EXISTING_TOKEN_POOL()), address(EXISTING_TOKEN_POOL)); + assertEq(address(proposal.EXISTING_REMOTE_POOL_ARB()), ARB_PROXY_POOL); + assertEq(address(proposal.NEW_TOKEN_POOL()), address(NEW_TOKEN_POOL)); + assertEq(address(proposal.EXISTING_GHO_AAVE_STEWARD()), address(EXISTING_GHO_AAVE_STEWARD)); + assertEq(address(proposal.NEW_GHO_AAVE_STEWARD()), address(NEW_GHO_AAVE_STEWARD)); + assertEq(address(proposal.NEW_GHO_CCIP_STEWARD()), address(NEW_GHO_CCIP_STEWARD)); + assertEq(address(proposal.NEW_REMOTE_POOL_ARB()), NEW_REMOTE_POOL_ARB); + assertEq(proposal.CCIP_RATE_LIMIT_CAPACITY(), CCIP_RATE_LIMIT_CAPACITY); + assertEq(proposal.CCIP_RATE_LIMIT_REFILL_RATE(), CCIP_RATE_LIMIT_REFILL_RATE); + + assertEq(address(proposal.EXISTING_PROXY_POOL()), EXISTING_TOKEN_POOL.getProxyPool()); + + assertEq(TOKEN_ADMIN_REGISTRY.typeAndVersion(), 'TokenAdminRegistry 1.5.0'); + assertEq(NEW_TOKEN_POOL.typeAndVersion(), 'LockReleaseTokenPool 1.5.1'); + assertEq(EXISTING_TOKEN_POOL.typeAndVersion(), 'LockReleaseTokenPool 1.4.0'); + assertEq( + ITypeAndVersion(EXISTING_TOKEN_POOL.getProxyPool()).typeAndVersion(), + 'LockReleaseTokenPoolAndProxy 1.5.0' + ); + assertEq(ON_RAMP.typeAndVersion(), 'EVM2EVMOnRamp 1.5.0'); + assertEq(OFF_RAMP.typeAndVersion(), 'EVM2EVMOffRamp 1.5.0'); + + assertEq(ROUTER.typeAndVersion(), 'Router 1.2.0'); + assertEq(EXISTING_TOKEN_POOL.getRouter(), address(ROUTER)); + + assertEq(ROUTER.getOnRamp(ARB_CHAIN_SELECTOR), address(ON_RAMP)); + assertTrue(ROUTER.isOffRamp(ARB_CHAIN_SELECTOR, address(OFF_RAMP))); + + assertTrue( + AaveV3Ethereum.ACL_MANAGER.hasRole( + AaveV3Ethereum.ACL_MANAGER.RISK_ADMIN_ROLE(), + address(EXISTING_GHO_AAVE_STEWARD) + ) + ); + assertEq(IOwnable(address(NEW_GHO_AAVE_STEWARD)).owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq( + NEW_GHO_AAVE_STEWARD.POOL_ADDRESSES_PROVIDER(), + address(AaveV3Ethereum.POOL_ADDRESSES_PROVIDER) + ); + assertEq( + NEW_GHO_AAVE_STEWARD.POOL_DATA_PROVIDER(), + address(AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER) + ); + assertEq(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL(), EXISTING_GHO_AAVE_STEWARD.RISK_COUNCIL()); + assertEq( + NEW_GHO_AAVE_STEWARD.getBorrowRateConfig(), + IGhoAaveSteward.BorrowRateConfig({ + optimalUsageRatioMaxChange: 5_00, + baseVariableBorrowRateMaxChange: 5_00, + variableRateSlope1MaxChange: 5_00, + variableRateSlope2MaxChange: 5_00 + }) + ); + + assertEq(EXISTING_TOKEN_POOL.getRateLimitAdmin(), address(EXISTING_GHO_CCIP_STEWARD)); + assertEq(NEW_GHO_CCIP_STEWARD.RISK_COUNCIL(), EXISTING_GHO_CCIP_STEWARD.RISK_COUNCIL()); + assertEq(NEW_GHO_CCIP_STEWARD.GHO_TOKEN(), AaveV3EthereumAssets.GHO_UNDERLYING); + assertEq(NEW_GHO_CCIP_STEWARD.GHO_TOKEN_POOL(), address(NEW_TOKEN_POOL)); + assertTrue(NEW_GHO_CCIP_STEWARD.BRIDGE_LIMIT_ENABLED()); // *present* on eth token pool + assertEq( + abi.encode(NEW_GHO_CCIP_STEWARD.getCcipTimelocks()), + abi.encode(IGhoCcipSteward.CcipDebounce(0, 0)) + ); + + assertEq(_getProxyAdmin(address(NEW_TOKEN_POOL)).UPGRADE_INTERFACE_VERSION(), '5.0.0'); + } + + function _getTokenMessage( + CCIPSendParams memory params + ) internal returns (IClient.EVM2AnyMessage memory, IInternal.EVM2EVMMessage memory) { + IClient.EVM2AnyMessage memory message = CCIPUtils.generateMessage(params.sender, 1); + message.tokenAmounts[0] = IClient.EVMTokenAmount({ + token: AaveV3EthereumAssets.GHO_UNDERLYING, + amount: params.amount + }); + + uint256 feeAmount = ROUTER.getFee(ARB_CHAIN_SELECTOR, message); + deal(params.sender, feeAmount); + + IInternal.EVM2EVMMessage memory eventArg = CCIPUtils.messageToEvent( + CCIPUtils.MessageToEventParams({ + message: message, + router: ROUTER, + sourceChainSelector: ETH_CHAIN_SELECTOR, + feeTokenAmount: feeAmount, + originalSender: params.sender, + sourceToken: AaveV3EthereumAssets.GHO_UNDERLYING, + destinationToken: AaveV3ArbitrumAssets.GHO_UNDERLYING, + poolVersion: params.poolVersion + }) + ); + + return (message, eventArg); + } + + function _getStaticParams(address tokenPool) internal view returns (bytes memory) { + IUpgradeableLockReleaseTokenPool_1_4 ghoTokenPool = IUpgradeableLockReleaseTokenPool_1_4( + tokenPool + ); + return + abi.encode( + ghoTokenPool.getToken(), + ghoTokenPool.getAllowListEnabled(), + ghoTokenPool.getAllowList(), + ghoTokenPool.canAcceptLiquidity(), + ghoTokenPool.getRouter() + ); + } + + function _getDynamicParams(address tokenPool) internal view returns (bytes memory) { + IUpgradeableLockReleaseTokenPool_1_4 ghoTokenPool = IUpgradeableLockReleaseTokenPool_1_4( + tokenPool + ); + return + abi.encode( + ghoTokenPool.owner(), + ghoTokenPool.getSupportedChains(), + ghoTokenPool.getRebalancer(), + ghoTokenPool.getBridgeLimit() + ); + } + + function _tokenBucketToConfig( + IRateLimiter.TokenBucket memory bucket + ) internal pure returns (IRateLimiter.Config memory) { + return + IRateLimiter.Config({ + isEnabled: bucket.isEnabled, + capacity: bucket.capacity, + rate: bucket.rate + }); + } + + function _getDisabledConfig() internal pure returns (IRateLimiter.Config memory) { + return IRateLimiter.Config({isEnabled: false, capacity: 0, rate: 0}); + } + + function _getImplementation(address proxy) internal view returns (address) { + bytes32 slot = bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1); + return address(uint160(uint256(vm.load(proxy, slot)))); + } + + function _readInitialized(address proxy) internal view returns (uint8) { + return uint8(uint256(vm.load(proxy, bytes32(0)))); + } + function _getProxyAdmin(address proxy) internal view returns (ProxyAdmin) { + bytes32 slot = bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1); + return ProxyAdmin(address(uint160(uint256(vm.load(proxy, slot))))); + } + + function _getRateLimiterConfig() internal view returns (IRateLimiter.Config memory) { + return + IRateLimiter.Config({ + isEnabled: true, + capacity: proposal.CCIP_RATE_LIMIT_CAPACITY(), + rate: proposal.CCIP_RATE_LIMIT_REFILL_RATE() + }); + } + + function _getOutboundRefillTime(uint256 amount) internal pure returns (uint256) { + return amount / CCIP_RATE_LIMIT_REFILL_RATE + 1; // account for rounding + } + + function _getInboundRefillTime(uint256 amount) internal pure returns (uint256) { + return amount / CCIP_RATE_LIMIT_REFILL_RATE + 1; // account for rounding + } + + function _min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + function _isDifferenceLowerThanMax( + uint256 from, + uint256 to, + uint256 max + ) internal pure returns (bool) { + return from < to ? to - from <= max : from - to <= max; + } + + function assertEq( + IDefaultInterestRateStrategyV2.InterestRateData memory a, + IDefaultInterestRateStrategyV2.InterestRateData memory b + ) internal pure { + assertEq(a.optimalUsageRatio, b.optimalUsageRatio); + assertEq(a.baseVariableBorrowRate, b.baseVariableBorrowRate); + assertEq(a.variableRateSlope1, b.variableRateSlope1); + assertEq(a.variableRateSlope2, b.variableRateSlope2); + assertEq(abi.encode(a), abi.encode(b)); // sanity check + } + + function assertEq( + IGhoAaveSteward.BorrowRateConfig memory a, + IGhoAaveSteward.BorrowRateConfig memory b + ) internal pure { + assertEq(a.optimalUsageRatioMaxChange, b.optimalUsageRatioMaxChange); + assertEq(a.baseVariableBorrowRateMaxChange, b.baseVariableBorrowRateMaxChange); + assertEq(a.variableRateSlope1MaxChange, b.variableRateSlope1MaxChange); + assertEq(a.variableRateSlope2MaxChange, b.variableRateSlope2MaxChange); + assertEq(abi.encode(a), abi.encode(b)); // sanity check + } + + function assertEq( + IRateLimiter.TokenBucket memory bucket, + IRateLimiter.Config memory config + ) internal pure { + assertEq(bucket.isEnabled, config.isEnabled); + assertEq(bucket.capacity, config.capacity); + assertEq(bucket.rate, config.rate); + assertEq(abi.encode(_tokenBucketToConfig(bucket)), abi.encode(config)); // sanity check + } +} + +contract AaveV3Ethereum_GHOCCIP151Upgrade_20241209_SetupAndProposalActions is + AaveV3Ethereum_GHOCCIP151Upgrade_20241209_Base +{ + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_GHOCCIP151Upgrade_20241209', + AaveV3Ethereum.POOL, + address(proposal) + ); + } + + function test_tokenPoolOwnershipTransfer() public { + assertFalse( + TOKEN_ADMIN_REGISTRY.isAdministrator(address(GHO), GovernanceV3Ethereum.EXECUTOR_LVL_1) + ); + ITokenAdminRegistry.TokenConfig memory tokenConfig = TOKEN_ADMIN_REGISTRY.getTokenConfig( + address(GHO) + ); + assertNotEq(tokenConfig.administrator, GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq(tokenConfig.pendingAdministrator, GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq(tokenConfig.tokenPool, address(EXISTING_PROXY_POOL)); + + assertEq(EXISTING_TOKEN_POOL.owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq(EXISTING_PROXY_POOL.owner(), TOKEN_ADMIN_REGISTRY.owner()); + assertEq(NEW_TOKEN_POOL.owner(), address(0)); + + executePayload(vm, address(proposal)); + + tokenConfig = TOKEN_ADMIN_REGISTRY.getTokenConfig(address(GHO)); + assertEq(tokenConfig.administrator, GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq(tokenConfig.pendingAdministrator, address(0)); + assertEq(tokenConfig.tokenPool, address(NEW_TOKEN_POOL)); + + assertEq(EXISTING_TOKEN_POOL.owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq(EXISTING_PROXY_POOL.owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1); + assertEq(NEW_TOKEN_POOL.owner(), GovernanceV3Ethereum.EXECUTOR_LVL_1); + } + + function test_tokenPoolLiquidityMigration() public { + assertEq(EXISTING_TOKEN_POOL.getRebalancer(), address(0)); + uint256 balance = GHO.balanceOf(address(EXISTING_TOKEN_POOL)); + uint256 bridgedAmount = EXISTING_TOKEN_POOL.getCurrentBridgedAmount(); + + assertGt(balance, 0); + assertGt(bridgedAmount, 0); + + assertEq(GHO.balanceOf(address(NEW_TOKEN_POOL)), 0); + assertEq(NEW_TOKEN_POOL.getCurrentBridgedAmount(), 0); + + assertEq(bridgedAmount, balance); // bridgedAmountInvariant + + executePayload(vm, address(proposal)); + + assertEq(EXISTING_TOKEN_POOL.getRebalancer(), address(NEW_TOKEN_POOL)); + + assertEq(GHO.balanceOf(address(EXISTING_TOKEN_POOL)), 0); + // we do not reset bridgedAmount in the existing token pool, since bridge limit is reset + assertNotEq(EXISTING_TOKEN_POOL.getCurrentBridgedAmount(), 0); + assertEq(EXISTING_TOKEN_POOL.getBridgeLimit(), 0); + + assertEq(GHO.balanceOf(address(NEW_TOKEN_POOL)), balance); + assertEq(NEW_TOKEN_POOL.getCurrentBridgedAmount(), bridgedAmount); + } + + function test_newTokenPoolSetupAndRegistration() public { + bytes memory staticParams = _getStaticParams(address(EXISTING_TOKEN_POOL)); + bytes memory dynamicParams = _getDynamicParams(address(EXISTING_TOKEN_POOL)); + assertEq(EXISTING_TOKEN_POOL.getRateLimitAdmin(), address(EXISTING_GHO_CCIP_STEWARD)); + + assertEq(TOKEN_ADMIN_REGISTRY.getPool(address(GHO)), EXISTING_TOKEN_POOL.getProxyPool()); + + executePayload(vm, address(proposal)); + + assertEq(staticParams, _getStaticParams(address(NEW_TOKEN_POOL))); + assertEq(dynamicParams, _getDynamicParams(address(NEW_TOKEN_POOL))); + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(NEW_GHO_CCIP_STEWARD)); + + assertEq(TOKEN_ADMIN_REGISTRY.getPool(address(GHO)), address(NEW_TOKEN_POOL)); + + assertEq(NEW_TOKEN_POOL.getRemotePools(ARB_CHAIN_SELECTOR).length, 2); + assertTrue(NEW_TOKEN_POOL.isRemotePool(ARB_CHAIN_SELECTOR, abi.encode(ARB_PROXY_POOL))); + assertTrue(NEW_TOKEN_POOL.isRemotePool(ARB_CHAIN_SELECTOR, abi.encode(NEW_REMOTE_POOL_ARB))); + assertEq( + NEW_TOKEN_POOL.getRemoteToken(ARB_CHAIN_SELECTOR), + abi.encode(AaveV3ArbitrumAssets.GHO_UNDERLYING) + ); + assertEq(NEW_TOKEN_POOL.getSupportedChains().length, 1); + assertTrue(NEW_TOKEN_POOL.isSupportedChain(ARB_CHAIN_SELECTOR)); + + assertEq( + NEW_TOKEN_POOL.getCurrentInboundRateLimiterState(ARB_CHAIN_SELECTOR), + _getRateLimiterConfig() + ); + assertEq( + NEW_TOKEN_POOL.getCurrentOutboundRateLimiterState(ARB_CHAIN_SELECTOR), + _getRateLimiterConfig() + ); + } + + function test_newTokenPoolInitialization() public { + vm.expectRevert('Initializable: contract is already initialized'); + NEW_TOKEN_POOL.initialize(makeAddr('owner'), new address[](0), makeAddr('router'), 13e7); + assertEq(_readInitialized(address(NEW_TOKEN_POOL)), 1); + assertEq(_readInitialized(_getImplementation(address(NEW_TOKEN_POOL))), 255); + } + + function test_stewardRolesAndConfig() public { + bytes32 RISK_ADMIN_ROLE = AaveV3Ethereum.ACL_MANAGER.RISK_ADMIN_ROLE(); + assertTrue( + AaveV3Ethereum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(EXISTING_GHO_AAVE_STEWARD)) + ); + assertFalse(AaveV3Ethereum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(NEW_GHO_AAVE_STEWARD))); + + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(0)); + assertEq(NEW_TOKEN_POOL.getBridgeLimitAdmin(), address(0)); + + executePayload(vm, address(proposal)); + + assertFalse( + AaveV3Ethereum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(EXISTING_GHO_AAVE_STEWARD)) + ); + assertTrue(AaveV3Ethereum.ACL_MANAGER.hasRole(RISK_ADMIN_ROLE, address(NEW_GHO_AAVE_STEWARD))); + + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(NEW_GHO_CCIP_STEWARD)); + assertEq(NEW_TOKEN_POOL.getBridgeLimitAdmin(), address(NEW_GHO_CCIP_STEWARD)); + } +} + +contract AaveV3Ethereum_GHOCCIP151Upgrade_20241209_PostUpgrade is + AaveV3Ethereum_GHOCCIP151Upgrade_20241209_Base +{ + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + + function setUp() public override { + super.setUp(); + + executePayload(vm, address(proposal)); + } + + function test_sendMessageSucceedsAndRoutesViaNewPool(uint256 amount) public { + uint256 bridgeableAmount = _min( + NEW_TOKEN_POOL.getBridgeLimit() - NEW_TOKEN_POOL.getCurrentBridgedAmount(), + CCIP_RATE_LIMIT_CAPACITY + ); + amount = bound(amount, 1, bridgeableAmount); + skip(_getOutboundRefillTime(amount)); // wait for the rate limiter to refill + + deal(address(GHO), alice, amount); + vm.prank(alice); + GHO.approve(address(ROUTER), amount); + + uint256 aliceBalance = GHO.balanceOf(alice); + uint256 tokenPoolBalance = GHO.balanceOf(address(NEW_TOKEN_POOL)); + uint256 bridgedAmount = NEW_TOKEN_POOL.getCurrentBridgedAmount(); + + ( + IClient.EVM2AnyMessage memory message, + IInternal.EVM2EVMMessage memory eventArg + ) = _getTokenMessage( + CCIPSendParams({amount: amount, sender: alice, poolVersion: CCIPUtils.PoolVersion.V1_5_1}) + ); + + vm.expectEmit(address(NEW_TOKEN_POOL)); // new token pool + emit Locked(address(ON_RAMP), amount); + + vm.expectEmit(address(ON_RAMP)); + emit CCIPSendRequested(eventArg); + + vm.prank(alice); + ROUTER.ccipSend{value: eventArg.feeTokenAmount}(ARB_CHAIN_SELECTOR, message); + + assertEq(GHO.balanceOf(alice), aliceBalance - amount); + assertEq(GHO.balanceOf(address(NEW_TOKEN_POOL)), tokenPoolBalance + amount); + assertEq(NEW_TOKEN_POOL.getCurrentBridgedAmount(), bridgedAmount + amount); + } + + // existing pool can no longer on ramp + function test_lockOrBurnRevertsOnExistingPool() public { + uint256 amount = 100_000e18; + skip(_getOutboundRefillTime(amount)); + + // router pulls tokens from the user & sends to the token pool during onRamps + deal(address(GHO), address(EXISTING_TOKEN_POOL), amount); + + vm.prank(EXISTING_TOKEN_POOL.getProxyPool()); + // since we disable the bridge by resetting the bridge limit to 0 + vm.expectRevert(abi.encodeWithSelector(BridgeLimitExceeded.selector, 0)); + EXISTING_TOKEN_POOL.lockOrBurn( + alice, + abi.encode(alice), + amount, + ARB_CHAIN_SELECTOR, + new bytes(0) + ); + } + + // on-ramp via new pool + function test_lockOrBurnSucceedsOnNewPool(uint256 amount) public { + uint256 bridgeableAmount = _min( + NEW_TOKEN_POOL.getBridgeLimit() - NEW_TOKEN_POOL.getCurrentBridgedAmount(), + CCIP_RATE_LIMIT_CAPACITY + ); + amount = bound(amount, 1, bridgeableAmount); + skip(_getOutboundRefillTime(amount)); // wait for the rate limiter to refill + + // router pulls tokens from the user & sends to the token pool during onRamps + // we don't override NEW_TOKEN_POOL balance here & instead transfer because we want + // to check the invariant GHO.balanceOf(tokenPool) >= tokenPool.currentBridgedAmount() + deal(address(GHO), address(alice), amount); + vm.prank(alice); + GHO.transfer(address(NEW_TOKEN_POOL), amount); + + uint256 bridgedAmount = NEW_TOKEN_POOL.getCurrentBridgedAmount(); + + vm.expectEmit(address(NEW_TOKEN_POOL)); + emit Locked(address(ON_RAMP), amount); + + vm.prank(address(ON_RAMP)); + NEW_TOKEN_POOL.lockOrBurn( + IPool_CCIP.LockOrBurnInV1({ + receiver: abi.encode(alice), + remoteChainSelector: ARB_CHAIN_SELECTOR, + originalSender: alice, + amount: amount, + localToken: address(GHO) + }) + ); + + assertEq(GHO.balanceOf(address(NEW_TOKEN_POOL)), NEW_TOKEN_POOL.getCurrentBridgedAmount()); + assertEq(NEW_TOKEN_POOL.getCurrentBridgedAmount(), bridgedAmount + amount); + } + + // existing pool can no longer off ramp + function test_releaseOrMintRevertsOnExistingPool() public { + uint256 amount = 100_000e18; + skip(_getInboundRefillTime(amount)); + + assertEq(GHO.balanceOf(address(EXISTING_TOKEN_POOL)), 0); + + vm.prank(EXISTING_TOKEN_POOL.getProxyPool()); + // underflow expected at tokenPool.GHO.transfer() since existing + // token pool does not hold any gho + vm.expectRevert(stdError.arithmeticError); + EXISTING_TOKEN_POOL.releaseOrMint( + abi.encode(alice), + alice, + amount, + ARB_CHAIN_SELECTOR, + new bytes(0) + ); + } + + // off-ramp messages sent from new eth token pool (v1.5.1) + function test_releaseOrMintSucceedsOnNewPoolOffRampedViaNewTokenPoolEth(uint256 amount) public { + uint256 bridgeableAmount = _min( + NEW_TOKEN_POOL.getCurrentBridgedAmount(), + CCIP_RATE_LIMIT_CAPACITY + ); + amount = bound(amount, 1, bridgeableAmount); + skip(_getInboundRefillTime(amount)); + + uint256 aliceBalance = GHO.balanceOf(alice); + uint256 tokenPoolBalance = GHO.balanceOf(address(NEW_TOKEN_POOL)); + uint256 bridgedAmount = NEW_TOKEN_POOL.getCurrentBridgedAmount(); + + vm.expectEmit(address(NEW_TOKEN_POOL)); + emit Released(address(OFF_RAMP), alice, amount); + + vm.prank(address(OFF_RAMP)); + NEW_TOKEN_POOL.releaseOrMint( + IPool_CCIP.ReleaseOrMintInV1({ + originalSender: abi.encode(alice), + remoteChainSelector: ARB_CHAIN_SELECTOR, + receiver: alice, + amount: amount, + localToken: address(GHO), + sourcePoolAddress: abi.encode(address(NEW_REMOTE_POOL_ARB)), + sourcePoolData: new bytes(0), + offchainTokenData: new bytes(0) + }) + ); + + assertEq(GHO.balanceOf(alice), aliceBalance + amount); + assertEq(GHO.balanceOf(address(NEW_TOKEN_POOL)), tokenPoolBalance - amount); + assertEq(NEW_TOKEN_POOL.getCurrentBridgedAmount(), bridgedAmount - amount); + } + + // off-ramp messages sent from existing arb token pool (v1.4) ie ProxyPool + function test_releaseOrMintSucceedsOnNewPoolOffRampedViaExistingTokenPoolArb( + uint256 amount + ) public { + uint256 bridgeableAmount = _min( + NEW_TOKEN_POOL.getCurrentBridgedAmount(), + CCIP_RATE_LIMIT_CAPACITY + ); + amount = bound(amount, 1, bridgeableAmount); + skip(_getInboundRefillTime(amount)); + + uint256 aliceBalance = GHO.balanceOf(alice); + uint256 tokenPoolBalance = GHO.balanceOf(address(NEW_TOKEN_POOL)); + uint256 bridgedAmount = NEW_TOKEN_POOL.getCurrentBridgedAmount(); + + vm.expectEmit(address(NEW_TOKEN_POOL)); + emit Released(address(OFF_RAMP), alice, amount); + + vm.prank(address(OFF_RAMP)); + NEW_TOKEN_POOL.releaseOrMint( + IPool_CCIP.ReleaseOrMintInV1({ + originalSender: abi.encode(alice), + remoteChainSelector: ARB_CHAIN_SELECTOR, + receiver: alice, + amount: amount, + localToken: address(GHO), + sourcePoolAddress: abi.encode(address(ARB_PROXY_POOL)), + sourcePoolData: new bytes(0), + offchainTokenData: new bytes(0) + }) + ); + + assertEq(GHO.balanceOf(alice), aliceBalance + amount); + assertEq(GHO.balanceOf(address(NEW_TOKEN_POOL)), tokenPoolBalance - amount); + assertEq(NEW_TOKEN_POOL.getCurrentBridgedAmount(), bridgedAmount - amount); + } + + function test_ccipStewardCanChangeAndDisableRateLimit() public { + assertEq(NEW_TOKEN_POOL.getRateLimitAdmin(), address(NEW_GHO_CCIP_STEWARD)); // sanity + + IRateLimiter.Config memory outboundConfig = IRateLimiter.Config({ + isEnabled: true, + capacity: 500_000e18, + rate: 100e18 + }); + IRateLimiter.Config memory inboundConfig = IRateLimiter.Config({ + isEnabled: true, + capacity: 100_000e18, + rate: 50e18 + }); + + // we assert the new steward can change the rate limit + vm.prank(NEW_GHO_CCIP_STEWARD.RISK_COUNCIL()); + NEW_GHO_CCIP_STEWARD.updateRateLimit( + ARB_CHAIN_SELECTOR, + outboundConfig.isEnabled, + outboundConfig.capacity, + outboundConfig.rate, + inboundConfig.isEnabled, + inboundConfig.capacity, + inboundConfig.rate + ); + + assertEq(NEW_TOKEN_POOL.getCurrentOutboundRateLimiterState(ARB_CHAIN_SELECTOR), outboundConfig); + assertEq(NEW_TOKEN_POOL.getCurrentInboundRateLimiterState(ARB_CHAIN_SELECTOR), inboundConfig); + assertEq(NEW_GHO_CCIP_STEWARD.getCcipTimelocks().rateLimitLastUpdate, vm.getBlockTimestamp()); + + skip(NEW_GHO_CCIP_STEWARD.MINIMUM_DELAY() + 1); + + // now we assert the new steward can disable the rate limit + vm.prank(NEW_GHO_CCIP_STEWARD.RISK_COUNCIL()); + NEW_GHO_CCIP_STEWARD.updateRateLimit(ARB_CHAIN_SELECTOR, false, 0, 0, false, 0, 0); + + assertEq( + NEW_TOKEN_POOL.getCurrentOutboundRateLimiterState(ARB_CHAIN_SELECTOR), + _getDisabledConfig() + ); + assertEq( + NEW_TOKEN_POOL.getCurrentInboundRateLimiterState(ARB_CHAIN_SELECTOR), + _getDisabledConfig() + ); + assertEq(NEW_GHO_CCIP_STEWARD.getCcipTimelocks().rateLimitLastUpdate, vm.getBlockTimestamp()); + } + + function test_ccipStewardCanSetBridgeLimit(uint256 newBridgeLimit) public { + uint256 currentBridgeLimit = NEW_TOKEN_POOL.getBridgeLimit(); + vm.assume( + newBridgeLimit != currentBridgeLimit && + _isDifferenceLowerThanMax(currentBridgeLimit, newBridgeLimit, currentBridgeLimit) + ); + vm.prank(NEW_GHO_CCIP_STEWARD.RISK_COUNCIL()); + NEW_GHO_CCIP_STEWARD.updateBridgeLimit(newBridgeLimit); + + assertEq(NEW_TOKEN_POOL.getBridgeLimit(), newBridgeLimit); + assertEq(NEW_GHO_CCIP_STEWARD.getCcipTimelocks().bridgeLimitLastUpdate, vm.getBlockTimestamp()); + } + + function test_aaveStewardCanUpdateBorrowRate() public { + IDefaultInterestRateStrategyV2 irStrategy = IDefaultInterestRateStrategyV2( + AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getInterestRateStrategyAddress(address(GHO)) + ); + + IDefaultInterestRateStrategyV2.InterestRateData + memory currentRateData = IDefaultInterestRateStrategyV2.InterestRateData({ + optimalUsageRatio: 99_00, + baseVariableBorrowRate: 11_50, + variableRateSlope1: 0, + variableRateSlope2: 0 + }); + + assertEq(irStrategy.getInterestRateDataBps(address(GHO)), currentRateData); + + currentRateData.baseVariableBorrowRate += 4_00; + + vm.prank(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL()); + NEW_GHO_AAVE_STEWARD.updateGhoBorrowRate( + currentRateData.optimalUsageRatio, + currentRateData.baseVariableBorrowRate, + currentRateData.variableRateSlope1, + currentRateData.variableRateSlope2 + ); + + assertEq(irStrategy.getInterestRateDataBps(address(GHO)), currentRateData); + assertEq( + NEW_GHO_AAVE_STEWARD.getGhoTimelocks().ghoBorrowRateLastUpdate, + vm.getBlockTimestamp() + ); + } + + function test_aaveStewardCanUpdateBorrowCap(uint256 newBorrowCap) public { + uint256 currentBorrowCap = AaveV3Ethereum.POOL.getConfiguration(address(GHO)).getBorrowCap(); + assertEq(currentBorrowCap, 155_000_000); + vm.assume( + newBorrowCap != currentBorrowCap && + _isDifferenceLowerThanMax(currentBorrowCap, newBorrowCap, currentBorrowCap) + ); + + vm.prank(NEW_GHO_AAVE_STEWARD.RISK_COUNCIL()); + NEW_GHO_AAVE_STEWARD.updateGhoBorrowCap(newBorrowCap); + + assertEq(AaveV3Ethereum.POOL.getConfiguration(address(GHO)).getBorrowCap(), newBorrowCap); + assertEq(NEW_GHO_AAVE_STEWARD.getGhoTimelocks().ghoBorrowCapLastUpdate, vm.getBlockTimestamp()); + + // @dev gho cannot be supplied on ethereum + assertEq(AaveV3Ethereum.POOL.getConfiguration(address(GHO)).getSupplyCap(), 0); + } +} diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade.md b/src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade.md new file mode 100644 index 000000000..231fb9852 --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade.md @@ -0,0 +1,61 @@ +--- +title: "GHO Risk Stewards Update and GHO CCIP Integration Upgrade" +author: "Aave Labs" +discussions: "https://governance.aave.com/t/technical-maintenance-proposals/15274/59" +--- + +## Simple Summary + +This AIP proposes to: + +1. update the GHO Risk Steward contracts to enhance the Risk Council’s user experience and align the design of the Risk Stewards implementations throughout the Aave Protocol. +2. update the GHO CCIP Token Pools on Arbitrum and Ethereum to integrate them with latest version of CCIP (1.5.1) to leverage the full functionality of CCIP and prepare for future expansions to other chains. + +## Motivation + +This AIP seeks to enhance the Aave user experience and align the design of the Risk Stewards implementation across the Aave Protocol. Additionally, the CCIP was recently upgraded to version 1.5.1, introducing a number of enhancements for cross-chain pool management. Currently, GHO CCIP Token Pools are based on version 1.4, though still compatible with 1.5.1. + +Aave Labs will provide technical support to maintain the GHO CCIP Token Pools functional, secured, and aligned with the latest updates, enabling GHO expansion to other networks when needed. + +## Specification + +The proposal includes the following actions: + +Risk Stewards update: + +1. GhoAaveSteward: Remove the max cap of 25% configured by `GHO_BORROW_RATE_MAX`. While this limitation was sensible when applied to the Ethereum reserve only, it is not necessary for different instances of GHO when implemented as a regular reserve. Additionally, the Risk Stewards already have limitations and sanity checks in place to restrict capabilities during rates update. +2. GhoCcipSteward: Add a missing getter for the timelock state of the CCIP. +3. GhoBucketSteward: No modification, configure new token pool and retire permissions for the existing token pool. + +GHO CCIP Token Pools upgrade: + +1. Ownership maintenance of contracts: + 1. Accept ownership of new token pool contracts for GHO on each network. + 2. Assume Admin role for the GHO token in the CCIP TokenAdminRegistry contract on each network. + 3. Take ownership of the existing proxy pools (even though they'll be deprecated). +2. Migrate Liquidity Between Old and New Token Pools: + 1. On Ethereum: Transfer locked GHO liquidity from the old LockReleaseTokenPool contract to the new one, and properly initialize the new contract to reflect the correct amount of bridged liquidity. + 2. On Arbitrum: Mint tokens on the new BurnMintTokenPool contract and burn tokens from the old pool using the newly introduced `directMint` and `directBurn` methods. This is necessary to offboard the old pool as a facilitator and enable the new pool to handle bridge transactions. +3. Setup a token rate limit of 300,000 GHO capacity and 60 GHO per second refill rate (216,000 GHO per hour), as recommended by the Risk Provider ChaosLabs in the previous maintenance upgrade to v1.5, see [here](https://governance.aave.com/t/technical-maintenance-proposals/15274/54). +4. Keep GhoStewards functional by validating they can execute actions over the new CCIP lane and remain fully operational. + +## References + +- Implementation: [GhoAaveSteward](https://github.com/aave/gho-core/blob/cf6ee42adc8b2e9ac8ffd1d70bd5b52f06e536b6/src/contracts/misc/GhoAaveSteward.sol), [GhoCcipSteward](https://github.com/aave/gho-core/blob/cf6ee42adc8b2e9ac8ffd1d70bd5b52f06e536b6/src/contracts/misc/GhoCcipSteward.sol), [UpgradeableLockReleaseTokenPool](https://github.com/aave/ccip/blob/d5c6cedde6fbca9890a92a55f2db80e94793d0ec/contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol), [UpgradeableTokenPool](https://github.com/aave/ccip/blob/d5c6cedde6fbca9890a92a55f2db80e94793d0ec/contracts/src/v0.8/ccip/pools/GHO/UpgradeableTokenPool.sol) + +- Contracts: + + - Ethereum + - [UpgradeableLockReleaseTokenPool](https://etherscan.io/address/0x06179f7C1be40863405f374E7f5F8806c728660A) + - [GhoAaveSteward](https://etherscan.io/address/0x98217A06721Ebf727f2C8d9aD7718ec28b7aAe34) + - [GhoCcipSteward](https://etherscan.io/address/0xC5BcC58BE6172769ca1a78B8A45752E3C5059c39) + - Arbitrum + - [UpgradeableBurnMintTokenPool](https://arbiscan.io/address/0xB94Ab28c6869466a46a42abA834ca2B3cECCA5eB) + - [GhoAaveSteward](https://arbiscan.io/address/0xd2D586f849620ef042FE3aF52eAa10e9b78bf7De) + - [GhoCcipSteward](https://arbiscan.io/address/0xCd5ab470AaC5c13e1063ee700503f3346b7C90Db) + +- Discussion: [GHO CCIP Integration Maintenance (CCIP v1.5.1 upgrade)](https://governance.aave.com/t/technical-maintenance-proposals/15274/59), [Update GHO Risk Stewards](https://governance.aave.com/t/technical-maintenance-proposals/15274/60) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade_20241209.s.sol b/src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade_20241209.s.sol new file mode 100644 index 000000000..c4a58b46b --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade_20241209.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_GHOCCIP151Upgrade_20241209} from './AaveV3Ethereum_GHOCCIP151Upgrade_20241209.sol'; +import {AaveV3Arbitrum_GHOCCIP151Upgrade_20241209} from './AaveV3Arbitrum_GHOCCIP151Upgrade_20241209.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade_20241209.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/GHOCCIP151Upgrade_20241209.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_GHOCCIP151Upgrade_20241209).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/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade_20241209.s.sol:DeployArbitrum chain=arbitrum + * verify-command: FOUNDRY_PROFILE=arbitrum npx catapulta-verify -b broadcast/GHOCCIP151Upgrade_20241209.s.sol/42161/run-latest.json + */ +contract DeployArbitrum is ArbitrumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Arbitrum_GHOCCIP151Upgrade_20241209).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/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade_20241209.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_GHOCCIP151Upgrade_20241209).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + IPayloadsControllerCore.ExecutionAction[] + memory actionsArbitrum = new IPayloadsControllerCore.ExecutionAction[](1); + actionsArbitrum[0] = GovV3Helpers.buildAction( + type(AaveV3Arbitrum_GHOCCIP151Upgrade_20241209).creationCode + ); + payloads[1] = GovV3Helpers.buildArbitrumPayload(vm, actionsArbitrum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile(vm, 'src/20241209_Multi_GHOCCIP151Upgrade/GHOCCIP151Upgrade.md') + ); + } +} diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/config.ts b/src/20241209_Multi_GHOCCIP151Upgrade/config.ts new file mode 100644 index 000000000..c340e7540 --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/config.ts @@ -0,0 +1,17 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + pools: ['AaveV3Ethereum', 'AaveV3Arbitrum'], + title: 'GHO CCIP 1.5.1 Upgrade', + shortName: 'GHOCCIP151Upgrade', + date: '20241209', + author: 'Aave Labs', + discussion: 'https://governance.aave.com/t/technical-maintenance-proposals/15274/59', + snapshot: 'Direct-to-AIP', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Ethereum: {configs: {OTHERS: {}}, cache: {blockNumber: 21594804}}, + AaveV3Arbitrum: {configs: {OTHERS: {}}, cache: {blockNumber: 293994020}}, + }, +}; diff --git a/src/20241209_Multi_GHOCCIP151Upgrade/utils/CCIPUtils.sol b/src/20241209_Multi_GHOCCIP151Upgrade/utils/CCIPUtils.sol new file mode 100644 index 000000000..e853cd8cc --- /dev/null +++ b/src/20241209_Multi_GHOCCIP151Upgrade/utils/CCIPUtils.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IClient} from 'src/interfaces/ccip/IClient.sol'; +import {IRouter} from 'src/interfaces/ccip/IRouter.sol'; +import {IInternal} from 'src/interfaces/ccip/IInternal.sol'; +import {IEVM2EVMOnRamp} from 'src/interfaces/ccip/IEVM2EVMOnRamp.sol'; + +library CCIPUtils { + uint64 internal constant ETH_CHAIN_SELECTOR = 5009297550715157269; + uint64 internal constant ARB_CHAIN_SELECTOR = 4949039107694359620; + + bytes32 internal constant LEAF_DOMAIN_SEPARATOR = + 0x0000000000000000000000000000000000000000000000000000000000000000; + bytes32 internal constant INTERNAL_DOMAIN_SEPARATOR = + 0x0000000000000000000000000000000000000000000000000000000000000001; + bytes32 internal constant EVM_2_EVM_MESSAGE_HASH = keccak256('EVM2EVMMessageHashV2'); + bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9; + + enum PoolVersion { + V1_5_0, + V1_5_1 + } + + struct SourceTokenData { + bytes sourcePoolAddress; + bytes destTokenAddress; + bytes extraData; + uint32 destGasAmount; + } + + struct MessageToEventParams { + IClient.EVM2AnyMessage message; + IRouter router; + uint64 sourceChainSelector; + uint256 feeTokenAmount; + address originalSender; + address sourceToken; + address destinationToken; + PoolVersion poolVersion; + } + + function generateMessage( + address receiver, + uint256 tokenAmountsLength + ) internal pure returns (IClient.EVM2AnyMessage memory) { + return + IClient.EVM2AnyMessage({ + receiver: abi.encode(receiver), + data: '', + tokenAmounts: new IClient.EVMTokenAmount[](tokenAmountsLength), + feeToken: address(0), + extraArgs: argsToBytes(IClient.EVMExtraArgsV1({gasLimit: 0})) + }); + } + + function messageToEvent( + MessageToEventParams memory params + ) public view returns (IInternal.EVM2EVMMessage memory) { + uint64 destChainSelector = params.sourceChainSelector == ETH_CHAIN_SELECTOR + ? ARB_CHAIN_SELECTOR + : ETH_CHAIN_SELECTOR; + IEVM2EVMOnRamp onRamp = IEVM2EVMOnRamp(params.router.getOnRamp(destChainSelector)); + + bytes memory args = new bytes(params.message.extraArgs.length - 4); + for (uint256 i = 4; i < params.message.extraArgs.length; ++i) { + args[i - 4] = params.message.extraArgs[i]; + } + + IInternal.EVM2EVMMessage memory messageEvent = IInternal.EVM2EVMMessage({ + sequenceNumber: onRamp.getExpectedNextSequenceNumber(), + feeTokenAmount: params.feeTokenAmount, + sender: params.originalSender, + nonce: onRamp.getSenderNonce(params.originalSender) + 1, + gasLimit: abi.decode(args, (IClient.EVMExtraArgsV1)).gasLimit, + strict: false, + sourceChainSelector: params.sourceChainSelector, + receiver: abi.decode(params.message.receiver, (address)), + data: params.message.data, + tokenAmounts: params.message.tokenAmounts, + sourceTokenData: new bytes[](params.message.tokenAmounts.length), + feeToken: params.router.getWrappedNative(), + messageId: '' + }); + + for (uint256 i; i < params.message.tokenAmounts.length; ++i) { + messageEvent.sourceTokenData[i] = abi.encode( + SourceTokenData({ + sourcePoolAddress: abi.encode( + onRamp.getPoolBySourceToken(destChainSelector, params.message.tokenAmounts[i].token) + ), + destTokenAddress: abi.encode(params.destinationToken), + extraData: params.poolVersion == PoolVersion.V1_5_1 + ? abi.encode(getTokenDecimals(params.sourceToken)) + : new bytes(0), + destGasAmount: getDestGasAmount(onRamp, params.message.tokenAmounts[i].token) + }) + ); + } + + messageEvent.messageId = hash( + messageEvent, + generateMetadataHash(params.sourceChainSelector, destChainSelector, address(onRamp)) + ); + return messageEvent; + } + + function generateMetadataHash( + uint64 sourceChainSelector, + uint64 destChainSelector, + address onRamp + ) internal pure returns (bytes32) { + return + keccak256(abi.encode(EVM_2_EVM_MESSAGE_HASH, sourceChainSelector, destChainSelector, onRamp)); + } + + function argsToBytes( + IClient.EVMExtraArgsV1 memory extraArgs + ) internal pure returns (bytes memory bts) { + return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs); + } + + /// @dev Used to hash messages for single-lane ramps. + /// OnRamp hash(EVM2EVMMessage) = OffRamp hash(EVM2EVMMessage) + /// The EVM2EVMMessage's messageId is expected to be the output of this hash function + /// @param original Message to hash + /// @param metadataHash Immutable metadata hash representing a lane with a fixed OnRamp + /// @return hashedMessage hashed message as a keccak256 + function hash( + IInternal.EVM2EVMMessage memory original, + bytes32 metadataHash + ) internal pure returns (bytes32) { + // Fixed-size message fields are included in nested hash to reduce stack pressure. + // This hashing scheme is also used by RMN. If changing it, please notify the RMN maintainers. + return + keccak256( + abi.encode( + LEAF_DOMAIN_SEPARATOR, + metadataHash, + keccak256( + abi.encode( + original.sender, + original.receiver, + original.sequenceNumber, + original.gasLimit, + original.strict, + original.nonce, + original.feeToken, + original.feeTokenAmount + ) + ), + keccak256(original.data), + keccak256(abi.encode(original.tokenAmounts)), + keccak256(abi.encode(original.sourceTokenData)) + ) + ); + } + + function getDestGasAmount(IEVM2EVMOnRamp onRamp, address token) internal view returns (uint32) { + IEVM2EVMOnRamp.TokenTransferFeeConfig memory config = onRamp.getTokenTransferFeeConfig(token); + return + config.isEnabled + ? config.destGasOverhead + : onRamp.getDynamicConfig().defaultTokenDestGasOverhead; + } + + function getTokenDecimals(address token) internal view returns (uint8) { + (bool success, bytes memory data) = token.staticcall(abi.encodeWithSignature('decimals()')); + require(success, 'CCIPUtils: failed to get token decimals'); + return abi.decode(data, (uint8)); + } +} diff --git a/src/interfaces/IGhoCcipSteward.sol b/src/interfaces/IGhoCcipSteward.sol index 39bec9294..57c1e8017 100644 --- a/src/interfaces/IGhoCcipSteward.sol +++ b/src/interfaces/IGhoCcipSteward.sol @@ -41,6 +41,12 @@ interface IGhoCcipSteward { uint128 inboundRate ) external; + /** + * @notice Returns timestamp of the last update of Ccip parameters. + * @return The CcipDebounce struct describing the last update of Ccip parameters. + */ + function getCcipTimelocks() external view returns (CcipDebounce memory); + /** * @notice Returns the minimum delay that must be respected between parameters update. * @return The minimum delay between parameter updates (in seconds) diff --git a/src/interfaces/IGhoToken.sol b/src/interfaces/IGhoToken.sol index ab21aefbe..d37fb1643 100644 --- a/src/interfaces/IGhoToken.sol +++ b/src/interfaces/IGhoToken.sol @@ -1,13 +1,32 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -interface IGhoToken { +import {IERC20} from 'forge-std/interfaces/IERC20.sol'; + +interface IGhoToken is IERC20 { struct Facilitator { uint128 bucketCapacity; uint128 bucketLevel; string label; } + /** + * @notice Mints the requested amount of tokens to the account address. + * @dev Only facilitators with enough bucket capacity available can mint. + * @dev The bucket level is increased upon minting. + * @param account The address receiving the GHO tokens + * @param amount The amount to mint + */ + function mint(address account, uint256 amount) external; + + /** + * @notice Burns the requested amount of tokens from the account address. + * @dev Only active facilitators (bucket level > 0) can burn. + * @dev The bucket level is decreased upon burning. + * @param amount The amount to burn + */ + function burn(uint256 amount) external; + /** * @notice Add the facilitator passed with the parameters to the facilitators list. * @dev Only accounts with `FACILITATOR_MANAGER_ROLE` role can call this function @@ -21,7 +40,12 @@ interface IGhoToken { uint128 bucketCapacity ) external; - function balanceOf(address user) external returns (uint256); + /** + * @notice Remove the facilitator from the facilitators list. + * @dev Only accounts with `FACILITATOR_MANAGER_ROLE` role can call this function + * @param facilitatorAddress The address of the facilitator to remove + */ + function removeFacilitator(address facilitatorAddress) external; /** * @notice Returns the list of the addresses of the active facilitator diff --git a/src/interfaces/ccip/IEVM2EVMOffRamp.sol b/src/interfaces/ccip/IEVM2EVMOffRamp.sol index 7f8b84385..3c79cb4ef 100644 --- a/src/interfaces/ccip/IEVM2EVMOffRamp.sol +++ b/src/interfaces/ccip/IEVM2EVMOffRamp.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.0; -import {IInternal} from 'src/interfaces/ccip/IInternal.sol'; +import {IInternal} from './IInternal.sol'; +import {ITypeAndVersion} from './ITypeAndVersion.sol'; -interface IEVM2EVMOffRamp_1_2 { +interface IEVM2EVMOffRamp_1_2 is ITypeAndVersion { /// @notice Execute a single message. /// @param message The message that will be executed. /// @param offchainTokenData Token transfer data to be passed to TokenPool. @@ -18,7 +19,7 @@ interface IEVM2EVMOffRamp_1_2 { ) external; } -interface IEVM2EVMOffRamp_1_5 { +interface IEVM2EVMOffRamp_1_5 is ITypeAndVersion { /// @notice Execute a single message. /// @param message The message that will be executed. /// @param offchainTokenData Token transfer data to be passed to TokenPool. @@ -31,4 +32,18 @@ interface IEVM2EVMOffRamp_1_5 { bytes[] calldata offchainTokenData, uint32[] memory tokenGasOverrides ) external; + + /// @notice Dynamic offRamp config + /// @dev since OffRampConfig is part of OffRampConfigChanged event, if changing it, we should update the ABI on Atlas + struct DynamicConfig { + uint32 permissionLessExecutionThresholdSeconds; // ─╮ Waiting time before manual execution is enabled + uint32 maxDataBytes; // │ Maximum payload data size in bytes + uint16 maxNumberOfTokensPerMsg; // │ Maximum number of ERC20 token transfers that can be included per message + address router; // ─────────────────────────────────╯ Router address + address priceRegistry; // Price registry address + } + + /// @notice Returns the current dynamic config. + /// @return The current config. + function getDynamicConfig() external view returns (DynamicConfig memory); } diff --git a/src/interfaces/ccip/IEVM2EVMOnRamp.sol b/src/interfaces/ccip/IEVM2EVMOnRamp.sol index 20efb0cfd..306f1ebf0 100644 --- a/src/interfaces/ccip/IEVM2EVMOnRamp.sol +++ b/src/interfaces/ccip/IEVM2EVMOnRamp.sol @@ -3,8 +3,9 @@ pragma solidity ^0.8.0; import {IInternal} from './IInternal.sol'; +import {ITypeAndVersion} from './ITypeAndVersion.sol'; -interface IEVM2EVMOnRamp { +interface IEVM2EVMOnRamp is ITypeAndVersion { struct TokenTransferFeeConfig { uint32 minFeeUSDCents; // ──────────╮ Minimum fee to charge per token transfer, multiples of 0.01 USD uint32 maxFeeUSDCents; // │ Maximum fee to charge per token transfer, multiples of 0.01 USD @@ -34,6 +35,17 @@ interface IEVM2EVMOnRamp { bool enforceOutOfOrder; // ──────────────────╯ Whether to enforce the allowOutOfOrderExecution extraArg value to be true. } + struct StaticConfig { + address linkToken; // ────────╮ Link token address + uint64 chainSelector; // ─────╯ Source chainSelector + uint64 destChainSelector; // ─╮ Destination chainSelector + uint64 defaultTxGasLimit; // │ Default gas limit for a tx + uint96 maxNopFeesJuels; // ───╯ Max nop fee balance onramp can have + address prevOnRamp; // Address of previous-version OnRamp + address rmnProxy; // Address of RMN proxy + address tokenAdminRegistry; // Address of the token admin registry + } + /// @notice Gets the next sequence number to be used in the onRamp /// @return the next sequence number to be used function getExpectedNextSequenceNumber() external view returns (uint64); @@ -68,4 +80,8 @@ interface IEVM2EVMOnRamp { /// @notice Returns the dynamic onRamp config. /// @return dynamicConfig the configuration. function getDynamicConfig() external view returns (DynamicConfig memory dynamicConfig); + + /// @notice Returns the static onRamp config. + /// @return the configuration. + function getStaticConfig() external view returns (StaticConfig memory); } diff --git a/src/interfaces/ccip/IInternal.sol b/src/interfaces/ccip/IInternal.sol index 062ee93da..050e557cd 100644 --- a/src/interfaces/ccip/IInternal.sol +++ b/src/interfaces/ccip/IInternal.sol @@ -5,6 +5,33 @@ pragma solidity ^0.8.0; import {IClient} from 'src/interfaces/ccip/IClient.sol'; interface IInternal { + /// @notice A collection of token price and gas price updates. + /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. + struct PriceUpdates { + TokenPriceUpdate[] tokenPriceUpdates; + GasPriceUpdate[] gasPriceUpdates; + } + + /// @notice Token price in USD. + /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. + struct TokenPriceUpdate { + address sourceToken; // Source token + uint224 usdPerToken; // 1e18 USD per 1e18 of the smallest token denomination. + } + + /// @notice Gas price for a given chain in USD, its value may contain tightly packed fields. + /// @dev RMN depends on this struct, if changing, please notify the RMN maintainers. + struct GasPriceUpdate { + uint64 destChainSelector; // Destination chain selector + uint224 usdPerUnitGas; // 1e18 USD per smallest unit (e.g. wei) of destination chain gas + } + + /// @notice A timestamped uint224 value that can contain several tightly packed fields. + struct TimestampedPackedUint224 { + uint224 value; // ───────╮ Value in uint224, packed. + uint32 timestamp; // ────╯ Timestamp of the most recent price update. + } + struct PoolUpdate { address token; // The IERC20 token address address pool; // The token pool address diff --git a/src/interfaces/ccip/IPriceRegistry.sol b/src/interfaces/ccip/IPriceRegistry.sol new file mode 100644 index 000000000..664863b2d --- /dev/null +++ b/src/interfaces/ccip/IPriceRegistry.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ITypeAndVersion} from './ITypeAndVersion.sol'; +import {IInternal} from './IInternal.sol'; + +interface IPriceRegistry is ITypeAndVersion { + /// @notice Gets the fee token price and the gas price, both denominated in dollars. + /// @param token The source token to get the price for. + /// @param destChainSelector The destination chain to get the gas price for. + /// @return tokenPrice The price of the feeToken in 1e18 dollars per base unit. + /// @return gasPrice The price of gas in 1e18 dollars per base unit. + function getTokenAndGasPrices( + address token, + uint64 destChainSelector + ) external view returns (uint224 tokenPrice, uint224 gasPrice); + + /// @notice Update the price for given tokens and gas prices for given chains. + /// @param priceUpdates The price updates to apply. + function updatePrices(IInternal.PriceUpdates memory priceUpdates) external; + + /// @notice Get the `tokenPrice` for a given token. + /// @param token The token to get the price for. + /// @return tokenPrice The tokenPrice for the given token. + function getTokenPrice( + address token + ) external view returns (IInternal.TimestampedPackedUint224 memory); + + /// @notice Get an encoded `gasPrice` for a given destination chain ID. + /// The 224-bit result encodes necessary gas price components. + /// On L1 chains like Ethereum or Avax, the only component is the gas price. + /// On Optimistic Rollups, there are two components - the L2 gas price, and L1 base fee for data availability. + /// On future chains, there could be more or differing price components. + /// PriceRegistry does not contain chain-specific logic to parse destination chain price components. + /// @param destChainSelector The destination chain to get the price for. + /// @return gasPrice The encoded gasPrice for the given destination chain ID. + function getDestinationChainGasPrice( + uint64 destChainSelector + ) external view returns (IInternal.TimestampedPackedUint224 memory); + + /// @notice Gets the owner of the contract. + /// @return The owner of the contract. + function owner() external view returns (address); +} diff --git a/src/interfaces/ccip/IRouter.sol b/src/interfaces/ccip/IRouter.sol index 89c9275b2..7792d09cc 100644 --- a/src/interfaces/ccip/IRouter.sol +++ b/src/interfaces/ccip/IRouter.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.0; -import {IClient} from 'src/interfaces/ccip/IClient.sol'; +import {IClient} from './IClient.sol'; +import {ITypeAndVersion} from './ITypeAndVersion.sol'; -interface IRouter { +interface IRouter is ITypeAndVersion { error UnsupportedDestinationChain(uint64 destChainSelector); error InsufficientFeeTokenAmount(); error InvalidMsgValue(); diff --git a/src/interfaces/ccip/ITokenAdminRegistry.sol b/src/interfaces/ccip/ITokenAdminRegistry.sol index 05667ccba..649b1be76 100644 --- a/src/interfaces/ccip/ITokenAdminRegistry.sol +++ b/src/interfaces/ccip/ITokenAdminRegistry.sol @@ -1,13 +1,49 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {ITypeAndVersion} from './ITypeAndVersion.sol'; -interface ITokenAdminRegistry is ITypeAndVersion { +interface ITokenAdminRegistry { + struct TokenConfig { + address administrator; + address pendingAdministrator; + address tokenPool; + } + + error AlreadyRegistered(address token); + error InvalidTokenPoolToken(address token); + error OnlyAdministrator(address sender, address token); + error OnlyPendingAdministrator(address sender, address token); + error OnlyRegistryModuleOrOwner(address sender); + error ZeroAddress(); + + event AdministratorTransferRequested( + address indexed token, + address indexed currentAdmin, + address indexed newAdmin + ); + event AdministratorTransferred(address indexed token, address indexed newAdmin); + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + event PoolSet(address indexed token, address indexed previousPool, address indexed newPool); + event RegistryModuleAdded(address module); + event RegistryModuleRemoved(address indexed module); + + function acceptAdminRole(address localToken) external; + function acceptOwnership() external; + function addRegistryModule(address module) external; + function getAllConfiguredTokens( + uint64 startIndex, + uint64 maxCount + ) external view returns (address[] memory tokens); + function getPool(address token) external view returns (address); + function getPools(address[] memory tokens) external view returns (address[] memory); + function getTokenConfig(address token) external view returns (TokenConfig memory); + function isAdministrator(address localToken, address administrator) external view returns (bool); + function isRegistryModule(address module) external view returns (bool); function owner() external view returns (address); - function acceptAdminRole(address from) external; function proposeAdministrator(address localToken, address administrator) external; - function transferAdminRole(address localToken, address newAdministrator) external; - function isAdministrator(address localToken, address administrator) external view returns (bool); - function getPool(address token) external view returns (address); - function setPool(address source, address pool) external; + function removeRegistryModule(address module) external; + function setPool(address localToken, address pool) external; + function transferAdminRole(address localToken, address newAdmin) external; + function transferOwnership(address to) external; + function typeAndVersion() external view returns (string memory); } diff --git a/src/interfaces/ccip/tokenPool/IPool.sol b/src/interfaces/ccip/tokenPool/IPool.sol new file mode 100644 index 000000000..f85af151e --- /dev/null +++ b/src/interfaces/ccip/tokenPool/IPool.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IPool { + struct LockOrBurnInV1 { + bytes receiver; + uint64 remoteChainSelector; + address originalSender; + uint256 amount; + address localToken; + } + + struct LockOrBurnOutV1 { + bytes destTokenAddress; + bytes destPoolData; + } + + struct ReleaseOrMintInV1 { + bytes originalSender; + uint64 remoteChainSelector; + address receiver; + uint256 amount; + address localToken; + bytes sourcePoolAddress; + bytes sourcePoolData; + bytes offchainTokenData; + } + + struct ReleaseOrMintOutV1 { + uint256 destinationAmount; + } +} diff --git a/src/interfaces/ccip/tokenPool/IUpgradeableBurnMintTokenPool.sol b/src/interfaces/ccip/tokenPool/IUpgradeableBurnMintTokenPool.sol new file mode 100644 index 000000000..00bedbab6 --- /dev/null +++ b/src/interfaces/ccip/tokenPool/IUpgradeableBurnMintTokenPool.sol @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IRateLimiter} from '../IRateLimiter.sol'; +import {IPool} from './IPool.sol'; + +interface IUpgradeableBurnMintTokenPool_1_4 { + struct ChainUpdate { + uint64 remoteChainSelector; + bool allowed; + IRateLimiter.Config outboundIRateLimiterConfig; + IRateLimiter.Config inboundIRateLimiterConfig; + } + + error AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested); + error AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available); + error AllowListNotEnabled(); + error BadARMSignal(); + error BucketOverfilled(); + error CallerIsNotARampOnRouter(address caller); + error ChainAlreadyExists(uint64 chainSelector); + error ChainNotAllowed(uint64 remoteChainSelector); + error DisabledNonZeroRateLimit(IRateLimiter.Config config); + error InvalidRatelimitRate(IRateLimiter.Config IRateLimiterConfig); + error NonExistentChain(uint64 remoteChainSelector); + error RateLimitMustBeDisabled(); + error SenderNotAllowed(address sender); + error TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress); + error TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress); + error Unauthorized(address caller); + error ZeroAddressNotAllowed(); + + event AllowListAdd(address sender); + event AllowListRemove(address sender); + event Burned(address indexed sender, uint256 amount); + event ChainAdded( + uint64 remoteChainSelector, + IRateLimiter.Config outboundIRateLimiterConfig, + IRateLimiter.Config inboundIRateLimiterConfig + ); + event ChainConfigured( + uint64 remoteChainSelector, + IRateLimiter.Config outboundIRateLimiterConfig, + IRateLimiter.Config inboundIRateLimiterConfig + ); + event ChainRemoved(uint64 remoteChainSelector); + event ConfigChanged(IRateLimiter.Config config); + event Initialized(uint8 version); + event Locked(address indexed sender, uint256 amount); + event Minted(address indexed sender, address indexed recipient, uint256 amount); + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + event Released(address indexed sender, address indexed recipient, uint256 amount); + event RouterUpdated(address oldRouter, address newRouter); + event TokensConsumed(uint256 tokens); + + function acceptOwnership() external; + function applyAllowListUpdates(address[] memory removes, address[] memory adds) external; + function applyChainUpdates(ChainUpdate[] memory chains) external; + function directBurn(uint256 amount) external; + function getAllowList() external view returns (address[] memory); + function getAllowListEnabled() external view returns (bool); + function getArmProxy() external view returns (address armProxy); + function getCurrentInboundIRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getCurrentOutboundIRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getProxyPool() external view returns (address proxyPool); + function getRateLimitAdmin() external view returns (address); + function getRouter() external view returns (address router); + function getSupportedChains() external view returns (uint64[] memory); + function getToken() external view returns (address token); + function initialize(address owner, address[] memory allowlist, address router) external; + function isSupportedChain(uint64 remoteChainSelector) external view returns (bool); + function lockOrBurn( + address originalSender, + bytes memory, + uint256 amount, + uint64 remoteChainSelector, + bytes memory + ) external returns (bytes memory); + function owner() external view returns (address); + function releaseOrMint( + bytes memory, + address receiver, + uint256 amount, + uint64 remoteChainSelector, + bytes memory + ) external; + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + IRateLimiter.Config memory outboundConfig, + IRateLimiter.Config memory inboundConfig + ) external; + function setProxyPool(address proxyPool) external; + function setRateLimitAdmin(address rateLimitAdmin) external; + function setRouter(address newRouter) external; + function supportsInterface(bytes4 interfaceId) external pure returns (bool); + function transferOwnership(address to) external; + function typeAndVersion() external view returns (string memory); +} + +interface IUpgradeableBurnMintTokenPool_1_5_1 { + struct ChainUpdate { + uint64 remoteChainSelector; + bytes[] remotePoolAddresses; + bytes remoteTokenAddress; + IRateLimiter.Config outboundRateLimiterConfig; + IRateLimiter.Config inboundRateLimiterConfig; + } + + error AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested); + error AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available); + error AllowListNotEnabled(); + error BucketOverfilled(); + error CallerIsNotARampOnRouter(address caller); + error CannotTransferToSelf(); + error ChainAlreadyExists(uint64 chainSelector); + error ChainNotAllowed(uint64 remoteChainSelector); + error CursedByRMN(); + error DisabledNonZeroRateLimit(IRateLimiter.Config config); + error InvalidDecimalArgs(uint8 expected, uint8 actual); + error InvalidRateLimitRate(IRateLimiter.Config IRateLimiterConfig); + error InvalidRemoteChainDecimals(bytes sourcePoolData); + error InvalidRemotePoolForChain(uint64 remoteChainSelector, bytes remotePoolAddress); + error InvalidSourcePoolAddress(bytes sourcePoolAddress); + error InvalidToken(address token); + error MustBeProposedOwner(); + error NonExistentChain(uint64 remoteChainSelector); + error OnlyCallableByOwner(); + error OverflowDetected(uint8 remoteDecimals, uint8 localDecimals, uint256 remoteAmount); + error OwnerCannotBeZero(); + error PoolAlreadyAdded(uint64 remoteChainSelector, bytes remotePoolAddress); + error RateLimitMustBeDisabled(); + error SenderNotAllowed(address sender); + error TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress); + error TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress); + error Unauthorized(address caller); + error ZeroAddressNotAllowed(); + + event AllowListAdd(address sender); + event AllowListRemove(address sender); + event Burned(address indexed sender, uint256 amount); + event ChainAdded( + uint64 remoteChainSelector, + bytes remoteToken, + IRateLimiter.Config outboundIRateLimiterConfig, + IRateLimiter.Config inboundIRateLimiterConfig + ); + event ChainConfigured( + uint64 remoteChainSelector, + IRateLimiter.Config outboundIRateLimiterConfig, + IRateLimiter.Config inboundIRateLimiterConfig + ); + event ChainRemoved(uint64 remoteChainSelector); + event ConfigChanged(IRateLimiter.Config config); + event Initialized(uint8 version); + event Locked(address indexed sender, uint256 amount); + event Minted(address indexed sender, address indexed recipient, uint256 amount); + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + event RateLimitAdminSet(address rateLimitAdmin); + event Released(address indexed sender, address indexed recipient, uint256 amount); + event RemotePoolAdded(uint64 indexed remoteChainSelector, bytes remotePoolAddress); + event RemotePoolRemoved(uint64 indexed remoteChainSelector, bytes remotePoolAddress); + event RouterUpdated(address oldRouter, address newRouter); + event TokensConsumed(uint256 tokens); + + function acceptOwnership() external; + function addRemotePool(uint64 remoteChainSelector, bytes memory remotePoolAddress) external; + function applyAllowListUpdates(address[] memory removes, address[] memory adds) external; + function applyChainUpdates( + uint64[] memory remoteChainSelectorsToRemove, + ChainUpdate[] memory chainsToAdd + ) external; + function directMint(address to, uint256 amount) external; + function getAllowList() external view returns (address[] memory); + function getAllowListEnabled() external view returns (bool); + function getCurrentInboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getCurrentOutboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getRateLimitAdmin() external view returns (address); + function getRemotePools(uint64 remoteChainSelector) external view returns (bytes[] memory); + function getRemoteToken(uint64 remoteChainSelector) external view returns (bytes memory); + function getRmnProxy() external view returns (address rmnProxy); + function getRouter() external view returns (address router); + function getSupportedChains() external view returns (uint64[] memory); + function getToken() external view returns (address token); + function getTokenDecimals() external view returns (uint8 decimals); + function initialize(address owner_, address[] memory allowlist, address router) external; + function isRemotePool( + uint64 remoteChainSelector, + bytes memory remotePoolAddress + ) external view returns (bool); + function isSupportedChain(uint64 remoteChainSelector) external view returns (bool); + function isSupportedToken(address token) external view returns (bool); + function lockOrBurn( + IPool.LockOrBurnInV1 memory lockOrBurnIn + ) external returns (IPool.LockOrBurnOutV1 memory); + function owner() external view returns (address); + function releaseOrMint( + IPool.ReleaseOrMintInV1 memory releaseOrMintIn + ) external returns (IPool.ReleaseOrMintOutV1 memory); + function removeRemotePool(uint64 remoteChainSelector, bytes memory remotePoolAddress) external; + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + IRateLimiter.Config memory outboundConfig, + IRateLimiter.Config memory inboundConfig + ) external; + function setRateLimitAdmin(address rateLimitAdmin) external; + function setRouter(address newRouter) external; + function supportsInterface(bytes4 interfaceId) external pure returns (bool); + function transferOwnership(address to) external; + function typeAndVersion() external view returns (string memory); +} diff --git a/src/interfaces/ccip/tokenPool/IUpgradeableLockReleaseTokenPool.sol b/src/interfaces/ccip/tokenPool/IUpgradeableLockReleaseTokenPool.sol new file mode 100644 index 000000000..96a3dbe0f --- /dev/null +++ b/src/interfaces/ccip/tokenPool/IUpgradeableLockReleaseTokenPool.sol @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IRateLimiter} from '../IRateLimiter.sol'; +import {IPool} from './IPool.sol'; + +interface IUpgradeableLockReleaseTokenPool_1_4 { + struct ChainUpdate { + uint64 remoteChainSelector; + bool allowed; + IRateLimiter.Config outboundIRateLimiterConfig; + IRateLimiter.Config inboundIRateLimiterConfig; + } + + error AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested); + error AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available); + error AllowListNotEnabled(); + error BadARMSignal(); + error BridgeLimitExceeded(uint256 bridgeLimit); + error BucketOverfilled(); + error CallerIsNotARampOnRouter(address caller); + error ChainAlreadyExists(uint64 chainSelector); + error ChainNotAllowed(uint64 remoteChainSelector); + error DisabledNonZeroRateLimit(IRateLimiter.Config config); + error InsufficientLiquidity(); + error InvalidRatelimitRate(IRateLimiter.Config IRateLimiterConfig); + error LiquidityNotAccepted(); + error NonExistentChain(uint64 remoteChainSelector); + error NotEnoughBridgedAmount(); + error RateLimitMustBeDisabled(); + error SenderNotAllowed(address sender); + error TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress); + error TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress); + error Unauthorized(address caller); + error ZeroAddressNotAllowed(); + + event AllowListAdd(address sender); + event AllowListRemove(address sender); + event BridgeLimitAdminUpdated(address indexed oldAdmin, address indexed newAdmin); + event BridgeLimitUpdated(uint256 oldBridgeLimit, uint256 newBridgeLimit); + event Burned(address indexed sender, uint256 amount); + event ChainAdded( + uint64 remoteChainSelector, + IRateLimiter.Config outboundRateLimiterConfig, + IRateLimiter.Config inboundRateLimiterConfig + ); + event ChainConfigured( + uint64 remoteChainSelector, + IRateLimiter.Config outboundRateLimiterConfig, + IRateLimiter.Config inboundRateLimiterConfig + ); + event ChainRemoved(uint64 remoteChainSelector); + event ConfigChanged(IRateLimiter.Config config); + event Initialized(uint8 version); + event LiquidityAdded(address indexed provider, uint256 indexed amount); + event LiquidityRemoved(address indexed provider, uint256 indexed amount); + event Locked(address indexed sender, uint256 amount); + event Minted(address indexed sender, address indexed recipient, uint256 amount); + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + event Released(address indexed sender, address indexed recipient, uint256 amount); + event RouterUpdated(address oldRouter, address newRouter); + event TokensConsumed(uint256 tokens); + + function acceptOwnership() external; + function applyAllowListUpdates(address[] memory removes, address[] memory adds) external; + function applyChainUpdates(ChainUpdate[] memory chains) external; + function canAcceptLiquidity() external view returns (bool); + function getAllowList() external view returns (address[] memory); + function getAllowListEnabled() external view returns (bool); + function getArmProxy() external view returns (address armProxy); + function getBridgeLimit() external view returns (uint256); + function getBridgeLimitAdmin() external view returns (address); + function getCurrentBridgedAmount() external view returns (uint256); + function getCurrentInboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getCurrentOutboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getLockReleaseInterfaceId() external pure returns (bytes4); + function getProxyPool() external view returns (address proxyPool); + function getRateLimitAdmin() external view returns (address); + function getRebalancer() external view returns (address); + function getRouter() external view returns (address router); + function getSupportedChains() external view returns (uint64[] memory); + function getToken() external view returns (address token); + function initialize( + address owner, + address[] memory allowlist, + address router, + uint256 bridgeLimit + ) external; + function isSupportedChain(uint64 remoteChainSelector) external view returns (bool); + function lockOrBurn( + address originalSender, + bytes memory, + uint256 amount, + uint64 remoteChainSelector, + bytes memory + ) external returns (bytes memory); + function owner() external view returns (address); + function provideLiquidity(uint256 amount) external; + function releaseOrMint( + bytes memory, + address receiver, + uint256 amount, + uint64 remoteChainSelector, + bytes memory + ) external; + function setBridgeLimit(uint256 newBridgeLimit) external; + function setBridgeLimitAdmin(address bridgeLimitAdmin) external; + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + IRateLimiter.Config memory outboundConfig, + IRateLimiter.Config memory inboundConfig + ) external; + function setProxyPool(address proxyPool) external; + function setRateLimitAdmin(address rateLimitAdmin) external; + function setRebalancer(address rebalancer) external; + function setRouter(address newRouter) external; + function supportsInterface(bytes4 interfaceId) external pure returns (bool); + function transferOwnership(address to) external; + function typeAndVersion() external view returns (string memory); + function withdrawLiquidity(uint256 amount) external; +} + +interface IUpgradeableLockReleaseTokenPool_1_5_1 { + struct ChainUpdate { + uint64 remoteChainSelector; + bytes[] remotePoolAddresses; + bytes remoteTokenAddress; + IRateLimiter.Config outboundRateLimiterConfig; + IRateLimiter.Config inboundRateLimiterConfig; + } + + error AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested); + error AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available); + error AllowListNotEnabled(); + error BridgeLimitExceeded(uint256 bridgeLimit); + error BucketOverfilled(); + error CallerIsNotARampOnRouter(address caller); + error CannotTransferToSelf(); + error ChainAlreadyExists(uint64 chainSelector); + error ChainNotAllowed(uint64 remoteChainSelector); + error CursedByRMN(); + error DisabledNonZeroRateLimit(IRateLimiter.Config config); + error InsufficientLiquidity(); + error InvalidDecimalArgs(uint8 expected, uint8 actual); + error InvalidRateLimitRate(IRateLimiter.Config IRateLimiterConfig); + error InvalidRemoteChainDecimals(bytes sourcePoolData); + error InvalidRemotePoolForChain(uint64 remoteChainSelector, bytes remotePoolAddress); + error InvalidSourcePoolAddress(bytes sourcePoolAddress); + error InvalidToken(address token); + error LiquidityNotAccepted(); + error MustBeProposedOwner(); + error NonExistentChain(uint64 remoteChainSelector); + error NotEnoughBridgedAmount(); + error OnlyCallableByOwner(); + error OverflowDetected(uint8 remoteDecimals, uint8 localDecimals, uint256 remoteAmount); + error OwnerCannotBeZero(); + error PoolAlreadyAdded(uint64 remoteChainSelector, bytes remotePoolAddress); + error RateLimitMustBeDisabled(); + error SenderNotAllowed(address sender); + error TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress); + error TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress); + error Unauthorized(address caller); + error ZeroAddressNotAllowed(); + + event AllowListAdd(address sender); + event AllowListRemove(address sender); + event BridgeLimitAdminUpdated(address indexed oldAdmin, address indexed newAdmin); + event BridgeLimitUpdated(uint256 oldBridgeLimit, uint256 newBridgeLimit); + event Burned(address indexed sender, uint256 amount); + event ChainAdded( + uint64 remoteChainSelector, + bytes remoteToken, + IRateLimiter.Config outboundRateLimiterConfig, + IRateLimiter.Config inboundRateLimiterConfig + ); + event ChainConfigured( + uint64 remoteChainSelector, + IRateLimiter.Config outboundRateLimiterConfig, + IRateLimiter.Config inboundRateLimiterConfig + ); + event ChainRemoved(uint64 remoteChainSelector); + event ConfigChanged(IRateLimiter.Config config); + event Initialized(uint8 version); + event LiquidityAdded(address indexed provider, uint256 indexed amount); + event LiquidityRemoved(address indexed provider, uint256 indexed amount); + event LiquidityTransferred(address indexed from, uint256 amount); + event Locked(address indexed sender, uint256 amount); + event Minted(address indexed sender, address indexed recipient, uint256 amount); + event OwnershipTransferRequested(address indexed from, address indexed to); + event OwnershipTransferred(address indexed from, address indexed to); + event RateLimitAdminSet(address rateLimitAdmin); + event Released(address indexed sender, address indexed recipient, uint256 amount); + event RemotePoolAdded(uint64 indexed remoteChainSelector, bytes remotePoolAddress); + event RemotePoolRemoved(uint64 indexed remoteChainSelector, bytes remotePoolAddress); + event RouterUpdated(address oldRouter, address newRouter); + event TokensConsumed(uint256 tokens); + + function acceptOwnership() external; + function addRemotePool(uint64 remoteChainSelector, bytes memory remotePoolAddress) external; + function applyAllowListUpdates(address[] memory removes, address[] memory adds) external; + function applyChainUpdates( + uint64[] memory remoteChainSelectorsToRemove, + ChainUpdate[] memory chainsToAdd + ) external; + function canAcceptLiquidity() external view returns (bool); + function getAllowList() external view returns (address[] memory); + function getAllowListEnabled() external view returns (bool); + function getBridgeLimit() external view returns (uint256); + function getBridgeLimitAdmin() external view returns (address); + function getCurrentBridgedAmount() external view returns (uint256); + function getCurrentInboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getCurrentOutboundRateLimiterState( + uint64 remoteChainSelector + ) external view returns (IRateLimiter.TokenBucket memory); + function getRateLimitAdmin() external view returns (address); + function getRebalancer() external view returns (address); + function getRemotePools(uint64 remoteChainSelector) external view returns (bytes[] memory); + function getRemoteToken(uint64 remoteChainSelector) external view returns (bytes memory); + function getRmnProxy() external view returns (address rmnProxy); + function getRouter() external view returns (address router); + function getSupportedChains() external view returns (uint64[] memory); + function getToken() external view returns (address token); + function getTokenDecimals() external view returns (uint8 decimals); + function initialize( + address owner_, + address[] memory allowlist, + address router, + uint256 bridgeLimit + ) external; + function isRemotePool( + uint64 remoteChainSelector, + bytes memory remotePoolAddress + ) external view returns (bool); + function isSupportedChain(uint64 remoteChainSelector) external view returns (bool); + function isSupportedToken(address token) external view returns (bool); + function lockOrBurn( + IPool.LockOrBurnInV1 memory lockOrBurnIn + ) external returns (IPool.LockOrBurnOutV1 memory); + function owner() external view returns (address); + function provideLiquidity(uint256 amount) external; + function releaseOrMint( + IPool.ReleaseOrMintInV1 memory releaseOrMintIn + ) external returns (IPool.ReleaseOrMintOutV1 memory); + function removeRemotePool(uint64 remoteChainSelector, bytes memory remotePoolAddress) external; + function setBridgeLimit(uint256 newBridgeLimit) external; + function setCurrentBridgedAmount(uint256 newBridgedAmount) external; + function setBridgeLimitAdmin(address bridgeLimitAdmin) external; + function setChainRateLimiterConfig( + uint64 remoteChainSelector, + IRateLimiter.Config memory outboundConfig, + IRateLimiter.Config memory inboundConfig + ) external; + function setRateLimitAdmin(address rateLimitAdmin) external; + function setRebalancer(address rebalancer) external; + function setRouter(address newRouter) external; + function supportsInterface(bytes4 interfaceId) external pure returns (bool); + function transferLiquidity(address from, uint256 amount) external; + function transferOwnership(address to) external; + function typeAndVersion() external view returns (string memory); + function withdrawLiquidity(uint256 amount) external; +} From 0dff1b66a30e113f7536feaf31c84e3b9c4e97eb Mon Sep 17 00:00:00 2001 From: dhairya <55102840+DhairyaSethi@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:28:34 +0530 Subject: [PATCH 19/21] feat: explicitly enable optimizer (#571) --- foundry.toml | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/foundry.toml b/foundry.toml index 42e201d77..ec2034e8f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,6 +3,8 @@ src = 'src' test = 'tests' script = 'scripts' solc = '0.8.20' +optimizer = true +optimizer_runs = 200 out = 'out' bytecode_hash = 'none' libs = ['lib'] @@ -19,6 +21,8 @@ src = 'zksync' test = 'zksync' libs = ['lib'] solc = '0.8.20' +optimizer = true +optimizer_runs = 200 fs_permissions = [{ access = "write", path = "./reports" }] ffi = true evm_version = 'shanghai' @@ -75,18 +79,18 @@ scroll = "${RPC_SCROLL}" zksync = "${RPC_ZKSYNC}" [etherscan] -mainnet = { key="${ETHERSCAN_API_KEY_MAINNET}", chain=1 } -optimism = { key="${ETHERSCAN_API_KEY_OPTIMISM}", chain=10 } -avalanche = { key="${ETHERSCAN_API_KEY_AVALANCHE}", chain=43114 } -polygon = { key="${ETHERSCAN_API_KEY_POLYGON}", chain=137 } -arbitrum = { key="${ETHERSCAN_API_KEY_ARBITRUM}", chain=42161 } -fantom = { key="${ETHERSCAN_API_KEY_FANTOM}", chain=250 } -metis = { key="any", chainId=1088, url='https://andromeda-explorer.metis.io/' } -base = { key="${ETHERSCAN_API_KEY_BASE}", chainId=8453 } -zkevm = { key="${ETHERSCAN_API_KEY_ZKEVM}", chainId=1101 } -gnosis = { key="${ETHERSCAN_API_KEY_GNOSIS}", chainId=100 } -bnb = { key="${ETHERSCAN_API_KEY_BNB}", chainId=56, url='https://api.bscscan.com/api'} -scroll = { key="${ETHERSCAN_API_KEY_SCROLL}", chainId=534352 } -zksync = { key="${ETHERSCAN_API_KEY_ZKSYNC}", chain = 324 } +mainnet = { key = "${ETHERSCAN_API_KEY_MAINNET}", chain = 1 } +optimism = { key = "${ETHERSCAN_API_KEY_OPTIMISM}", chain = 10 } +avalanche = { key = "${ETHERSCAN_API_KEY_AVALANCHE}", chain = 43114 } +polygon = { key = "${ETHERSCAN_API_KEY_POLYGON}", chain = 137 } +arbitrum = { key = "${ETHERSCAN_API_KEY_ARBITRUM}", chain = 42161 } +fantom = { key = "${ETHERSCAN_API_KEY_FANTOM}", chain = 250 } +metis = { key = "any", chainId = 1088, url = 'https://andromeda-explorer.metis.io/' } +base = { key = "${ETHERSCAN_API_KEY_BASE}", chainId = 8453 } +zkevm = { key = "${ETHERSCAN_API_KEY_ZKEVM}", chainId = 1101 } +gnosis = { key = "${ETHERSCAN_API_KEY_GNOSIS}", chainId = 100 } +bnb = { key = "${ETHERSCAN_API_KEY_BNB}", chainId = 56, url = 'https://api.bscscan.com/api' } +scroll = { key = "${ETHERSCAN_API_KEY_SCROLL}", chainId = 534352 } +zksync = { key = "${ETHERSCAN_API_KEY_ZKSYNC}", chain = 324 } # See more config options https://github.com/gakonst/foundry/tree/master/config From 97f99f8f3e9a0f6b53881ab3dfd8026d55d4b120 Mon Sep 17 00:00:00 2001 From: Alice <121383428+Rozengarden@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:55:55 +0100 Subject: [PATCH 20/21] [Aavechan] lbtc onboarding (#562) * feat: onboard LBTC * fix cap and bump block height * Update src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md Co-authored-by: Harsh Pandey * Update src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md Co-authored-by: Harsh Pandey * Update src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/config.ts Co-authored-by: Harsh Pandey * Update src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol Co-authored-by: Ian Flexa <85500650+ianflexa@users.noreply.github.com> * Update src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md Co-authored-by: Ian Flexa <85500650+ianflexa@users.noreply.github.com> * typo in assetListing.ts --------- Co-authored-by: Harsh Pandey Co-authored-by: Ian Flexa <85500650+ianflexa@users.noreply.github.com> --- ...ModeOnAavev3CoreInstance_20241223_after.md | 152 ++++++++++++++++++ generator/features/assetListing.ts | 2 +- ...quidEModeOnAavev3CoreInstance_20241223.sol | 109 +++++++++++++ ...idEModeOnAavev3CoreInstance_20241223.t.sol | 60 +++++++ ...LBTCWBTCLiquidEModeOnAavev3CoreInstance.md | 74 +++++++++ ...idEModeOnAavev3CoreInstance_20241223.s.sol | 60 +++++++ .../config.ts | 57 +++++++ 7 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 diffs/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_before_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_after.md create mode 100644 src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol create mode 100644 src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol create mode 100644 src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md create mode 100644 src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.s.sol create mode 100644 src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/config.ts diff --git a/diffs/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_before_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_after.md b/diffs/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_before_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_after.md new file mode 100644 index 000000000..9cc4cd6ef --- /dev/null +++ b/diffs/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_before_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_after.md @@ -0,0 +1,152 @@ +## Reserve changes + +### Reserves added + +#### LBTC ([0x8236a87084f8B84306f72007F36F2618A5634494](https://etherscan.io/address/0x8236a87084f8B84306f72007F36F2618A5634494)) + +| description | value | +| --- | --- | +| decimals | 8 | +| isActive | true | +| isFrozen | false | +| supplyCap | 800 LBTC | +| borrowCap | 80 LBTC | +| debtCeiling | 0 $ [0] | +| isSiloed | false | +| isFlashloanable | true | +| oracle | [0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c](https://etherscan.io/address/0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c) | +| oracleDecimals | 8 | +| oracleDescription | BTC / USD | +| oracleLatestAnswer | 102127.88158314 | +| usageAsCollateralEnabled | true | +| ltv | 70 % [7000] | +| liquidationThreshold | 75 % [7500] | +| liquidationBonus | 8.5 % | +| liquidationProtocolFee | 10 % [1000] | +| reserveFactor | 50 % [5000] | +| aToken | [0x65906988ADEe75306021C417a1A3458040239602](https://etherscan.io/address/0x65906988ADEe75306021C417a1A3458040239602) | +| aTokenImpl | [0x7EfFD7b47Bfd17e52fB7559d3f924201b9DbfF3d](https://etherscan.io/address/0x7EfFD7b47Bfd17e52fB7559d3f924201b9DbfF3d) | +| variableDebtToken | [0x68aeB290C7727D899B47c56d1c96AEAC475cD0dD](https://etherscan.io/address/0x68aeB290C7727D899B47c56d1c96AEAC475cD0dD) | +| variableDebtTokenImpl | [0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6](https://etherscan.io/address/0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6) | +| borrowingEnabled | true | +| isBorrowableInIsolation | false | +| interestRateStrategy | [0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB](https://etherscan.io/address/0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB) | +| aTokenName | Aave Ethereum LBTC | +| aTokenSymbol | aEthLBTC | +| aTokenUnderlyingBalance | 0.001 LBTC [100000] | +| id | 37 | +| isPaused | false | +| variableDebtTokenName | Aave Ethereum Variable Debt LBTC | +| variableDebtTokenSymbol | variableDebtEthLBTC | +| virtualAccountingActive | true | +| virtualBalance | 0.001 LBTC [100000] | +| optimalUsageRatio | 45 % | +| maxVariableBorrowRate | 304 % | +| baseVariableBorrowRate | 0 % | +| variableRateSlope1 | 4 % | +| variableRateSlope2 | 300 % | +| interestRate | ![ir](https://dash.onaave.com/api/static?variableRateSlope1=40000000000000000000000000&variableRateSlope2=3000000000000000000000000000&optimalUsageRatio=450000000000000000000000000&baseVariableBorrowRate=0&maxVariableBorrowRate=3040000000000000000000000000) | + + +## Emodes changed + +### EMode: ETH correlated(id: 1) + + + +### EMode: sUSDe Stablecoins(id: 2) + + + +### EMode: rsETH LST main(id: 3) + + + +### EMode: LBTC / WBTC(id: 4) + +| description | value before | value after | +| --- | --- | --- | +| eMode.label | - | LBTC / WBTC | +| eMode.ltv | - | 84 % | +| eMode.liquidationThreshold | - | 86 % | +| eMode.liquidationBonus | - | 3 % | +| eMode.borrowableBitmap | - | WBTC | +| eMode.collateralBitmap | - | LBTC | + + +## Raw diff + +```json +{ + "eModes": { + "4": { + "from": null, + "to": { + "borrowableBitmap": "4", + "collateralBitmap": "137438953472", + "eModeCategory": 4, + "label": "LBTC / WBTC", + "liquidationBonus": 10300, + "liquidationThreshold": 8600, + "ltv": 8400 + } + } + }, + "reserves": { + "0x8236a87084f8B84306f72007F36F2618A5634494": { + "from": null, + "to": { + "aToken": "0x65906988ADEe75306021C417a1A3458040239602", + "aTokenImpl": "0x7EfFD7b47Bfd17e52fB7559d3f924201b9DbfF3d", + "aTokenName": "Aave Ethereum LBTC", + "aTokenSymbol": "aEthLBTC", + "aTokenUnderlyingBalance": "100000", + "borrowCap": 80, + "borrowingEnabled": true, + "debtCeiling": 0, + "decimals": 8, + "id": 37, + "interestRateStrategy": "0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB", + "isActive": true, + "isBorrowableInIsolation": false, + "isFlashloanable": true, + "isFrozen": false, + "isPaused": false, + "isSiloed": false, + "liquidationBonus": 10850, + "liquidationProtocolFee": 1000, + "liquidationThreshold": 7500, + "ltv": 7000, + "oracle": "0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c", + "oracleDecimals": 8, + "oracleDescription": "BTC / USD", + "oracleLatestAnswer": "10212788158314", + "reserveFactor": 5000, + "supplyCap": 800, + "symbol": "LBTC", + "underlying": "0x8236a87084f8B84306f72007F36F2618A5634494", + "usageAsCollateralEnabled": true, + "variableDebtToken": "0x68aeB290C7727D899B47c56d1c96AEAC475cD0dD", + "variableDebtTokenImpl": "0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6", + "variableDebtTokenName": "Aave Ethereum Variable Debt LBTC", + "variableDebtTokenSymbol": "variableDebtEthLBTC", + "virtualAccountingActive": true, + "virtualBalance": "100000" + } + } + }, + "strategies": { + "0x8236a87084f8B84306f72007F36F2618A5634494": { + "from": null, + "to": { + "address": "0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB", + "baseVariableBorrowRate": "0", + "maxVariableBorrowRate": "3040000000000000000000000000", + "optimalUsageRatio": "450000000000000000000000000", + "variableRateSlope1": "40000000000000000000000000", + "variableRateSlope2": "3000000000000000000000000000" + } + } + } +} +``` \ No newline at end of file diff --git a/generator/features/assetListing.ts b/generator/features/assetListing.ts index 48dafe6ae..1c139f072 100644 --- a/generator/features/assetListing.ts +++ b/generator/features/assetListing.ts @@ -201,7 +201,7 @@ export const assetListing: FeatureModule = { listingTemplate += `| Borrowable in Isolation | ${cfg.borrowableInIsolation} |\n`; listingTemplate += `| Oracle | ${cfg.priceFeed} |\n`; if (isAddress(cfg.admin)) { - listingTemplate += `\nAdditionaly [${cfg.admin}](${getExplorerLink(CHAIN_TO_CHAIN_ID[getPoolChain(pool)], cfg.admin)}) has been set as the emission admin for ${cfg.assetSymbol} and the corresponding aToken.\n`; + listingTemplate += `\nAdditionally [${cfg.admin}](${getExplorerLink(CHAIN_TO_CHAIN_ID[getPoolChain(pool)], cfg.admin)}) has been set as the emission admin for ${cfg.assetSymbol} and the corresponding aToken.\n`; } return listingTemplate; }), diff --git a/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol new file mode 100644 index 000000000..4377f42b3 --- /dev/null +++ b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {AaveV3Ethereum, 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'; +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 LBTC & Enable LBTC/WBTC liquid E-Mode on Aave v3 Core Instance + * @author Aave Chan Initiative + * - Snapshot: https://snapshot.box/#/s:aave.eth/proposal/0x61f027ea797763c9e01736693570141a27a0a5d4517a6b63d0fd84474e8be995 + * - Discussion: https://governance.aave.com/t/arfc-enable-lbtc-wbtc-liquid-e-mode-on-aave-v3-core-instance/20142 + */ +contract AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223 is + AaveV3PayloadEthereum +{ + using SafeERC20 for IERC20; + + address public constant LBTC = 0x8236a87084f8B84306f72007F36F2618A5634494; + uint256 public constant LBTC_SEED_AMOUNT = 1e5; + address public constant LBTC_LM_ADMIN = 0xac140648435d03f784879cd789130F22Ef588Fcd; + + function _postExecute() internal override { + IERC20(LBTC).forceApprove(address(AaveV3Ethereum.POOL), LBTC_SEED_AMOUNT); + AaveV3Ethereum.POOL.supply(LBTC, LBTC_SEED_AMOUNT, address(AaveV3Ethereum.COLLECTOR), 0); + + (address aLBTC, , ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + LBTC + ); + IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).setEmissionAdmin(LBTC, LBTC_LM_ADMIN); + IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).setEmissionAdmin(aLBTC, LBTC_LM_ADMIN); + } + + function eModeCategoriesUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory) + { + IAaveV3ConfigEngine.EModeCategoryUpdate[] + memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](1); + + eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({ + eModeCategory: 4, + ltv: 84_00, + liqThreshold: 86_00, + liqBonus: 3_00, + label: 'LBTC / WBTC' + }); + + return eModeUpdates; + } + function assetsEModeUpdates() + public + pure + override + returns (IAaveV3ConfigEngine.AssetEModeUpdate[] memory) + { + IAaveV3ConfigEngine.AssetEModeUpdate[] + memory assetEModeUpdates = new IAaveV3ConfigEngine.AssetEModeUpdate[](2); + + assetEModeUpdates[0] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: LBTC, + eModeCategory: 4, + borrowable: EngineFlags.DISABLED, + collateral: EngineFlags.ENABLED + }); + assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({ + asset: AaveV3EthereumAssets.WBTC_UNDERLYING, + eModeCategory: 4, + borrowable: EngineFlags.ENABLED, + collateral: EngineFlags.DISABLED + }); + + return assetEModeUpdates; + } + function newListings() public pure override returns (IAaveV3ConfigEngine.Listing[] memory) { + IAaveV3ConfigEngine.Listing[] memory listings = new IAaveV3ConfigEngine.Listing[](1); + + listings[0] = IAaveV3ConfigEngine.Listing({ + asset: LBTC, + assetSymbol: 'LBTC', + priceFeed: 0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c, + enabledToBorrow: EngineFlags.ENABLED, + borrowableInIsolation: EngineFlags.DISABLED, + withSiloedBorrowing: EngineFlags.DISABLED, + flashloanable: EngineFlags.ENABLED, + ltv: 70_00, + liqThreshold: 75_00, + liqBonus: 8_50, + reserveFactor: 50_00, + supplyCap: 800, + borrowCap: 80, + debtCeiling: 0, + liqProtocolFee: 10_00, + rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({ + optimalUsageRatio: 45_00, + baseVariableBorrowRate: 0, + variableRateSlope1: 4_00, + variableRateSlope2: 300_00 + }) + }); + + return listings; + } +} diff --git a/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol new file mode 100644 index 000000000..ccff96bf7 --- /dev/null +++ b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol'; +import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol'; +import {IERC20} from 'solidity-utils/contracts/oz-common/interfaces/IERC20.sol'; +import {IEmissionManager} from 'aave-v3-origin/contracts/rewards/interfaces/IEmissionManager.sol'; + +import 'forge-std/Test.sol'; +import {ProtocolV3TestBase, ReserveConfig} from 'aave-helpers/src/ProtocolV3TestBase.sol'; +import {AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223} from './AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol'; + +/** + * @dev Test for AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223 + * command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol -vv + */ +contract AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223_Test is + ProtocolV3TestBase +{ + AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223 internal proposal; + + function setUp() public { + vm.createSelectFork(vm.rpcUrl('mainnet'), 21567986); + proposal = new AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223(); + } + + /** + * @dev executes the generic test suite including e2e and config snapshots + */ + function test_defaultProposalExecution() public { + defaultTest( + 'AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223', + AaveV3Ethereum.POOL, + address(proposal) + ); + } + + function test_collectorHasLBTCFunds() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aTokenAddress, , ) = AaveV3Ethereum + .AAVE_PROTOCOL_DATA_PROVIDER + .getReserveTokensAddresses(proposal.LBTC()); + assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3Ethereum.COLLECTOR)), 10 ** 5); + } + + function test_LBTCAdmin() public { + GovV3Helpers.executePayload(vm, address(proposal)); + (address aLBTC, , ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses( + proposal.LBTC() + ); + assertEq( + IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).getEmissionAdmin(proposal.LBTC()), + proposal.LBTC_LM_ADMIN() + ); + assertEq( + IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).getEmissionAdmin(aLBTC), + proposal.LBTC_LM_ADMIN() + ); + } +} diff --git a/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md new file mode 100644 index 000000000..c8cacd83b --- /dev/null +++ b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md @@ -0,0 +1,74 @@ +--- +title: "Onboard LBTC & Enable LBTC/WBTC liquid E-Mode on Aave v3 Core Instance" +author: "Aave Chan Initiative" +discussions: "https://governance.aave.com/t/arfc-enable-lbtc-wbtc-liquid-e-mode-on-aave-v3-core-instance/20142" +snapshot: "https://snapshot.box/#/s:aave.eth/proposal/0x61f027ea797763c9e01736693570141a27a0a5d4517a6b63d0fd84474e8be995" +--- + +## Simple Summary + +This proposal aims to onboard LBTC and enable LBTC/WBTC liquid E-Mode for the Main Instance. By implementing this change, we seek to enhance capital efficiency for borrowers using LBTC/WBTC as collateral. + +[TEMP CHECK](https://governance.aave.com/t/temp-check-onboard-enable-lbtc-wbtc-liquid-e-mode-on-aave-v3-core-instance/19968/7) and [TEMP CHECK Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x8fdee3ec7a301f9fba2e4c048227257070645d636b09a7afb369ee9c002ad764) have passed recently. + +## Motivation + +The motivation behind this proposal stems from several key factors: + +- High Utilization: LBTC/WBTC has demonstrated significant usage as collateral for borrowing stablecoins on other platforms. +- Capital Efficiency: Enabling liquid E-Mode for LBTC/WBTC will allow borrowers to substantially improve their capital efficiency when using this asset as collateral. +- Controlled Growth: Liquid E-Mode provides a mechanism for more precise control over the growth and borrow demand in relation to the overall stablecoin liquidity within Aave v3 on Core Instance. + +By implementing this proposal, we aim to optimize the use of LBTC/WBTC within the Aave ecosystem, attracting more liquidity for the protocol and increasing revenues. + +## Specification + +The table below illustrates the configured risk parameters for **LBTC** + +| Parameter | Value | +| ------------------------- | -----------------------------------------: | +| Isolation Mode | false | +| Borrowable | ENABLED | +| Collateral Enabled | true | +| Supply Cap (LBTC) | 800 | +| Borrow Cap (LBTC) | 80 | +| Debt Ceiling | USD 0 | +| LTV | 70 % | +| LT | 75 % | +| Liquidation Bonus | 8.5 % | +| Liquidation Protocol Fee | 10 % | +| Reserve Factor | 50 % | +| Base Variable Borrow Rate | 0 % | +| Variable Slope 1 | 4 % | +| Variable Slope 2 | 300 % | +| Uoptimal | 45 % | +| Flashloanable | ENABLED | +| Siloed Borrowing | DISABLED | +| Borrowable in Isolation | DISABLED | +| Oracle | 0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c | + +Additionally [0xac140648435d03f784879cd789130F22Ef588Fcd](https://etherscan.io/address/0xac140648435d03f784879cd789130F22Ef588Fcd) has been set as the emission admin for LBTC and the corresponding aToken. + +**E-mode** + +the following E-mode will be created + +| Parameter | Value | Value | +| --------------------- | ----- | ----- | +| Asset | LBTC | WBTC | +| Collateral | Yes | No | +| Borrowable | No | Yes | +| Max LTV | 84% | - | +| Liquidation Threshold | 86% | - | +| Liquidation Bonus | 3.00% | - | + +## References + +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol) +- [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x61f027ea797763c9e01736693570141a27a0a5d4517a6b63d0fd84474e8be995) +- [Discussion](https://governance.aave.com/t/arfc-enable-lbtc-wbtc-liquid-e-mode-on-aave-v3-core-instance/20142) + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.s.sol b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.s.sol new file mode 100644 index 000000000..82a2eeadc --- /dev/null +++ b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.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_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223} from './AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol'; + +/** + * @dev Deploy Ethereum + * deploy-command: make deploy-ledger contract=src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.s.sol:DeployEthereum chain=mainnet + * verify-command: FOUNDRY_PROFILE=mainnet npx catapulta-verify -b broadcast/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.s.sol/1/run-latest.json + */ +contract DeployEthereum is EthereumScript { + function run() external broadcast { + // deploy payloads + address payload0 = GovV3Helpers.deployDeterministic( + type(AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223).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/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.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_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223).creationCode + ); + payloads[0] = GovV3Helpers.buildMainnetPayload(vm, actionsEthereum); + + // create proposal + vm.startBroadcast(); + GovV3Helpers.createProposal( + vm, + payloads, + GovernanceV3Ethereum.VOTING_PORTAL_ETH_POL, + GovV3Helpers.ipfsHashFile( + vm, + 'src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md' + ) + ); + } +} diff --git a/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/config.ts b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/config.ts new file mode 100644 index 000000000..1a00500fa --- /dev/null +++ b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/config.ts @@ -0,0 +1,57 @@ +import {ConfigFile} from '../../generator/types'; +export const config: ConfigFile = { + rootOptions: { + configFile: 'tmp.ts', + author: 'Aave Chan Initiative', + pools: ['AaveV3Ethereum'], + title: 'Onboard LBTC & enable LBTC/WBTC liquid E-Mode on Aave v3 Core Instance', + shortName: 'EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance', + date: '20241223', + discussion: + 'https://governance.aave.com/t/arfc-enable-lbtc-wbtc-liquid-e-mode-on-aave-v3-core-instance/20142', + snapshot: + 'https://snapshot.box/#/s:aave.eth/proposal/0x61f027ea797763c9e01736693570141a27a0a5d4517a6b63d0fd84474e8be995', + votingNetwork: 'POLYGON', + }, + poolOptions: { + AaveV3Ethereum: { + configs: { + EMODES_UPDATES: [ + {eModeCategory: 4, ltv: '84', liqThreshold: '86', liqBonus: '3', label: 'LBTC / WBTC'}, + ], + EMODES_ASSETS: [ + {asset: 'LBTC', eModeCategory: '4', collateral: 'ENABLED', borrowable: 'DISABLED'}, + {asset: 'WBTC', eModeCategory: '4', collateral: 'DISABLED', borrowable: 'ENABLED'}, + ], + ASSET_LISTING: [ + { + assetSymbol: 'LBTC', + decimals: 8, + priceFeed: '0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c', + ltv: '70', + liqThreshold: '75', + liqBonus: '8.5', + debtCeiling: '0', + liqProtocolFee: '10', + enabledToBorrow: 'ENABLED', + flashloanable: 'ENABLED', + borrowableInIsolation: 'DISABLED', + withSiloedBorrowing: 'DISABLED', + reserveFactor: '50', + supplyCap: '800', + borrowCap: '80', + rateStrategyParams: { + optimalUtilizationRate: '45', + baseVariableBorrowRate: '0', + variableRateSlope1: '4', + variableRateSlope2: '300', + }, + asset: '0x8236a87084f8B84306f72007F36F2618A5634494', + admin: '0xac140648435d03f784879cd789130F22Ef588Fcd', + }, + ], + }, + cache: {blockNumber: 21466630}, + }, + }, +}; From e9257167453f0284a42fca6b3f6827a71b99a13e Mon Sep 17 00:00:00 2001 From: Cache bot Date: Mon, 20 Jan 2025 11:56:17 +0000 Subject: [PATCH 21/21] fix(cache): automated cache update [skip ci] --- .../EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md index c8cacd83b..1cb904b63 100644 --- a/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md +++ b/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance.md @@ -64,8 +64,8 @@ the following E-mode will be created ## References -- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol) -- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/main/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol) +- Implementation: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/97f99f8f3e9a0f6b53881ab3dfd8026d55d4b120/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.sol) +- Tests: [AaveV3Ethereum](https://github.com/bgd-labs/aave-proposals-v3/blob/97f99f8f3e9a0f6b53881ab3dfd8026d55d4b120/src/20241223_AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance/AaveV3Ethereum_EnableLBTCWBTCLiquidEModeOnAavev3CoreInstance_20241223.t.sol) - [Snapshot](https://snapshot.box/#/s:aave.eth/proposal/0x61f027ea797763c9e01736693570141a27a0a5d4517a6b63d0fd84474e8be995) - [Discussion](https://governance.aave.com/t/arfc-enable-lbtc-wbtc-liquid-e-mode-on-aave-v3-core-instance/20142)