Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Onboard rsETH on Aave V3 Ethereum #513

Merged
merged 11 commits into from
Nov 15, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
## Reserve changes

### Reserves added

#### rsETH ([0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7](https://etherscan.io/address/0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7))

| description | value |
| --- | --- |
| decimals | 18 |
| isActive | true |
| isFrozen | false |
| supplyCap | 19,000 rsETH |
| borrowCap | 1,900 rsETH |
| debtCeiling | 0 $ [0] |
| isSiloed | false |
| isFlashloanable | true |
| oracle | [0x47F52B2e43D0386cF161e001835b03Ad49889e3b](https://etherscan.io/address/0x47F52B2e43D0386cF161e001835b03Ad49889e3b) |
| oracleDecimals | 8 |
| oracleDescription | Capped rsETH / ETH / USD |
| oracleLatestAnswer | 2892.03476952 |
| usageAsCollateralEnabled | true |
| ltv | 72 % [7200] |
| liquidationThreshold | 75 % [7500] |
| liquidationBonus | 7.5 % |
| liquidationProtocolFee | 10 % [1000] |
| reserveFactor | 15 % [1500] |
| aToken | [0x2D62109243b87C4bA3EE7bA1D91B0dD0A074d7b1](https://etherscan.io/address/0x2D62109243b87C4bA3EE7bA1D91B0dD0A074d7b1) |
| aTokenImpl | [0x7EfFD7b47Bfd17e52fB7559d3f924201b9DbfF3d](https://etherscan.io/address/0x7EfFD7b47Bfd17e52fB7559d3f924201b9DbfF3d) |
| variableDebtToken | [0x6De3E52A1B7294A34e271a508082b1Ff4a37E30e](https://etherscan.io/address/0x6De3E52A1B7294A34e271a508082b1Ff4a37E30e) |
| variableDebtTokenImpl | [0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6](https://etherscan.io/address/0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6) |
| borrowingEnabled | true |
| isBorrowableInIsolation | false |
| interestRateStrategy | [0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB](https://etherscan.io/address/0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB) |
| aTokenName | Aave Ethereum rsETH |
| aTokenSymbol | aEthrsETH |
| aTokenUnderlyingBalance | 0.04 rsETH [40000000000000000] |
| id | 36 |
| isPaused | false |
| variableDebtTokenName | Aave Ethereum Variable Debt rsETH |
| variableDebtTokenSymbol | variableDebtEthrsETH |
| virtualAccountingActive | true |
| virtualBalance | 0.04 rsETH [40000000000000000] |
| optimalUsageRatio | 45 % |
| maxVariableBorrowRate | 307 % |
| baseVariableBorrowRate | 0 % |
| variableRateSlope1 | 7 % |
| variableRateSlope2 | 300 % |
| interestRate | ![ir](https://dash.onaave.com/api/static?variableRateSlope1=70000000000000000000000000&variableRateSlope2=3000000000000000000000000000&optimalUsageRatio=450000000000000000000000000&baseVariableBorrowRate=0&maxVariableBorrowRate=3070000000000000000000000000) |


### Reserve altered

#### wstETH ([0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0](https://etherscan.io/address/0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0))

| description | value before | value after |
| --- | --- | --- |
| borrowCap | 48,000 wstETH | 60,000 wstETH |


#### ETHx ([0xA35b1B31Ce002FBF2058D22F30f95D405200A15b](https://etherscan.io/address/0xA35b1B31Ce002FBF2058D22F30f95D405200A15b))

| description | value before | value after |
| --- | --- | --- |
| borrowCap | 320 ETHx | 5,000 ETHx |


## Emodes changed

### EMode: ETH correlated(id: 1)



### EMode: rsETH(id: 2)
MartinGbz marked this conversation as resolved.
Show resolved Hide resolved

| description | value before | value after |
| --- | --- | --- |
| eMode.label | - | rsETH |
MartinGbz marked this conversation as resolved.
Show resolved Hide resolved
| eMode.ltv | - | 92.5 % |
| eMode.liquidationThreshold | - | 94.5 % |
| eMode.liquidationBonus | - | 1 % |
| eMode.borrowableBitmap | - | wstETH, ETHx |
| eMode.collateralBitmap | - | rsETH |


## Raw diff

```json
{
"eModes": {
"2": {
"from": null,
"to": {
"borrowableBitmap": 2147483650,
"collateralBitmap": 68719476736,
"eModeCategory": 2,
"label": "rsETH",
MartinGbz marked this conversation as resolved.
Show resolved Hide resolved
"liquidationBonus": 10100,
"liquidationThreshold": 9450,
"ltv": 9250
}
}
},
"reserves": {
"0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0": {
"borrowCap": {
"from": 48000,
"to": 60000
}
},
"0xA35b1B31Ce002FBF2058D22F30f95D405200A15b": {
"borrowCap": {
"from": 320,
"to": 5000
}
},
"0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7": {
"from": null,
"to": {
"aToken": "0x2D62109243b87C4bA3EE7bA1D91B0dD0A074d7b1",
"aTokenImpl": "0x7EfFD7b47Bfd17e52fB7559d3f924201b9DbfF3d",
"aTokenName": "Aave Ethereum rsETH",
"aTokenSymbol": "aEthrsETH",
"aTokenUnderlyingBalance": "40000000000000000",
"borrowCap": 1900,
"borrowingEnabled": true,
"debtCeiling": 0,
"decimals": 18,
"id": 36,
"interestRateStrategy": "0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB",
"isActive": true,
"isBorrowableInIsolation": false,
"isFlashloanable": true,
"isFrozen": false,
"isPaused": false,
"isSiloed": false,
"liquidationBonus": 10750,
"liquidationProtocolFee": 1000,
"liquidationThreshold": 7500,
"ltv": 7200,
"oracle": "0x47F52B2e43D0386cF161e001835b03Ad49889e3b",
"oracleDecimals": 8,
"oracleDescription": "Capped rsETH / ETH / USD",
"oracleLatestAnswer": 289203476952,
"reserveFactor": 1500,
"supplyCap": 19000,
"symbol": "rsETH",
"underlying": "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7",
"usageAsCollateralEnabled": true,
"variableDebtToken": "0x6De3E52A1B7294A34e271a508082b1Ff4a37E30e",
"variableDebtTokenImpl": "0xaC725CB59D16C81061BDeA61041a8A5e73DA9EC6",
"variableDebtTokenName": "Aave Ethereum Variable Debt rsETH",
"variableDebtTokenSymbol": "variableDebtEthrsETH",
"virtualAccountingActive": true,
"virtualBalance": "40000000000000000"
}
}
},
"strategies": {
"0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7": {
"from": null,
"to": {
"address": "0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB",
"baseVariableBorrowRate": 0,
"maxVariableBorrowRate": "3070000000000000000000000000",
"optimalUsageRatio": "450000000000000000000000000",
"variableRateSlope1": "70000000000000000000000000",
"variableRateSlope2": "3000000000000000000000000000"
}
}
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// 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 rsETH to Aave V3 Ethereum
* @author ACI
* - Snapshot: https://snapshot.org/#/aave.eth/proposal/0xe83b67dfdddd469c298ce6133f4fdb84c9796c671c023b88617d5a25b5933c7f
* - Discussion: https://governance.aave.com/t/arfc-add-rseth-to-aave-v3-ethereum/17696
*/
contract AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum_20241104 is AaveV3PayloadEthereum {
using SafeERC20 for IERC20;

address public constant rsETH = 0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7;
uint256 public constant rsETH_SEED_AMOUNT = 4e16;
address public constant rsETH_ADMIN = 0xac140648435d03f784879cd789130F22Ef588Fcd;

function _postExecute() internal override {
IERC20(rsETH).forceApprove(address(AaveV3Ethereum.POOL), rsETH_SEED_AMOUNT);
AaveV3Ethereum.POOL.supply(rsETH, rsETH_SEED_AMOUNT, address(AaveV3Ethereum.COLLECTOR), 0);

(address arsETH, , ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(
rsETH
);
IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).setEmissionAdmin(rsETH, rsETH_ADMIN);
IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).setEmissionAdmin(arsETH, rsETH_ADMIN);
}

function capsUpdates() public pure override returns (IAaveV3ConfigEngine.CapsUpdate[] memory) {
IAaveV3ConfigEngine.CapsUpdate[] memory capsUpdate = new IAaveV3ConfigEngine.CapsUpdate[](2);

capsUpdate[0] = IAaveV3ConfigEngine.CapsUpdate({
asset: AaveV3EthereumAssets.wstETH_UNDERLYING,
supplyCap: EngineFlags.KEEP_CURRENT,
borrowCap: 60_000
});
capsUpdate[1] = IAaveV3ConfigEngine.CapsUpdate({
asset: AaveV3EthereumAssets.ETHx_UNDERLYING,
supplyCap: EngineFlags.KEEP_CURRENT,
borrowCap: 5_000
});

return capsUpdate;
}
function eModeCategoriesUpdates()
public
pure
override
returns (IAaveV3ConfigEngine.EModeCategoryUpdate[] memory)
{
IAaveV3ConfigEngine.EModeCategoryUpdate[]
memory eModeUpdates = new IAaveV3ConfigEngine.EModeCategoryUpdate[](1);

eModeUpdates[0] = IAaveV3ConfigEngine.EModeCategoryUpdate({
eModeCategory: 2,
ltv: 92_50,
liqThreshold: 94_50,
liqBonus: 1_00,
label: 'rsETH'
MartinGbz marked this conversation as resolved.
Show resolved Hide resolved
});

return eModeUpdates;
}
function assetsEModeUpdates()
public
pure
override
returns (IAaveV3ConfigEngine.AssetEModeUpdate[] memory)
{
IAaveV3ConfigEngine.AssetEModeUpdate[]
memory assetEModeUpdates = new IAaveV3ConfigEngine.AssetEModeUpdate[](3);

assetEModeUpdates[0] = IAaveV3ConfigEngine.AssetEModeUpdate({
asset: rsETH,
eModeCategory: 2,
borrowable: EngineFlags.DISABLED,
collateral: EngineFlags.ENABLED
});

assetEModeUpdates[1] = IAaveV3ConfigEngine.AssetEModeUpdate({
asset: AaveV3EthereumAssets.wstETH_UNDERLYING,
eModeCategory: 2,
borrowable: EngineFlags.ENABLED,
collateral: EngineFlags.DISABLED
});

assetEModeUpdates[2] = IAaveV3ConfigEngine.AssetEModeUpdate({
asset: AaveV3EthereumAssets.ETHx_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[](1);

listings[0] = IAaveV3ConfigEngine.Listing({
asset: rsETH,
assetSymbol: 'rsETH',
priceFeed: 0x47F52B2e43D0386cF161e001835b03Ad49889e3b,
enabledToBorrow: EngineFlags.ENABLED,
borrowableInIsolation: EngineFlags.DISABLED,
withSiloedBorrowing: EngineFlags.DISABLED,
flashloanable: EngineFlags.ENABLED,
ltv: 72_00,
liqThreshold: 75_00,
liqBonus: 7_50,
reserveFactor: 15_00,
supplyCap: 19_000,
borrowCap: 1_900,
debtCeiling: 0,
liqProtocolFee: 10_00,
rateStrategyParams: IAaveV3ConfigEngine.InterestRateInputData({
optimalUsageRatio: 45_00,
baseVariableBorrowRate: 0,
variableRateSlope1: 7_00,
variableRateSlope2: 300_00
})
});

return listings;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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_OnboardRsETHToAaveV3Ethereum_20241104} from './AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum_20241104.sol';

/**
* @dev Test for AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum_20241104
* command: FOUNDRY_PROFILE=mainnet forge test --match-path=src/20241104_AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum/AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum_20241104.t.sol -vv
*/
contract AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum_20241104_Test is ProtocolV3TestBase {
AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum_20241104 internal proposal;

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

/**
* @dev executes the generic test suite including e2e and config snapshots
*/
function test_defaultProposalExecution() public {
defaultTest(
'AaveV3Ethereum_OnboardRsETHToAaveV3Ethereum_20241104',
AaveV3Ethereum.POOL,
address(proposal)
);
}

function test_collectorHasrsETHFunds() public {
GovV3Helpers.executePayload(vm, address(proposal));
(address aTokenAddress, , ) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveTokensAddresses(proposal.rsETH());
assertGe(IERC20(aTokenAddress).balanceOf(address(AaveV3Ethereum.COLLECTOR)), 4 * 10 ** 16);
}

function test_rsETHAdmin() public {
GovV3Helpers.executePayload(vm, address(proposal));
(address arsETH, , ) = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getReserveTokensAddresses(
proposal.rsETH()
);
assertEq(
IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).getEmissionAdmin(proposal.rsETH()),
proposal.rsETH_ADMIN()
);
assertEq(
IEmissionManager(AaveV3Ethereum.EMISSION_MANAGER).getEmissionAdmin(arsETH),
proposal.rsETH_ADMIN()
);
}
}
Loading