diff --git a/Makefile b/Makefile
index 360993d..ed287d0 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,10 @@ update:; forge update
coverage :; forge coverage --report lcov && \
lcov --remove ./lcov.info -o ./lcov.info.p \
'tests/*' \
+ 'src/contracts/dependencies/*' \
+ 'src/contracts/examples/*' \
+ 'src/contracts/updates/*' \
+ 'scripts/*' \
&& genhtml ./lcov.info.p -o report --branch-coverage \
&& coverage=$$(awk -F '[<>]' '/headerCovTableEntryHi/{print $3}' ./report/index.html | sed 's/[^0-9.]//g' | head -n 1); \
wget -O ./report/coverage.svg "https://img.shields.io/badge/coverage-$${coverage}%25-brightgreen"
@@ -27,4 +31,4 @@ git-diff :
deploy-ledger :; FOUNDRY_PROFILE=${chain} forge script $(if $(filter zksync,${chain}),--zksync) ${contract} --rpc-url ${chain} $(if ${dry},--sender 0x25F2226B597E8F9514B3F68F00f494cF4f286491 -vvvv, --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv --slow --broadcast)
deploy-pk :; FOUNDRY_PROFILE=${chain} forge script $(if $(filter zksync,${chain}),--zksync) ${contract} --rpc-url ${chain} $(if ${dry},--sender 0x25F2226B597E8F9514B3F68F00f494cF4f286491 -vvvv, --private-key ${PRIVATE_KEY} --verify -vvvv --slow --broadcast)
-run-script:; FOUNDRY_PROFILE=${network} forge script ${contract_path} --rpc-url ${network} --sig "run(bool)" ${broadcast} -vv
+run-script:; FOUNDRY_PROFILE=${network} forge script ${contract} --rpc-url ${network} --sig "run(bool)" ${broadcast} -vv
diff --git a/README.md b/README.md
index 9a2f42b..90d72c5 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Expanding from the scope of CapsPlusRiskSteward, we now introduce the new RiskSt
## Specification
-The new RiskSteward we propose follows the same design as the CapsPlusRiskSteward: a smart contract to which the Aave Governance gives `POOL_ADMIN` the role over all v3 instances, controlled by a 2-of-2 multi-sig of the risk providers, and heavily constrained on what can do and how by its own logic.
+The new RiskSteward we propose follows the same design as the CapsPlusRiskSteward: a smart contract to which the Aave Governance gives `POOL_ADMIN` the role over all v3 instances. It is controlled by a 2-of-2 multi-sig and is heavily constrained on what can do and how by its own logic.
_Note: The Risk Stewards will only be available for Aave V3.1 instances and not Aave V2 (or previous Aave v3) due to missing admin roles on Aave V2 instances and other incompatibilities._
@@ -39,10 +39,10 @@ For each risk param, `minDelay` can be configured, which is the minimum amount o
#### Max Percent Change:
-For each risk param, `maxPercentChange` which is the maximum percent change allowed (both upwards and downwards) for the risk param using the RiskStewards.
+For each risk param, `maxPercentChange` is the maximum percent change allowed (both upwards and downwards) for the risk param using the RiskStewards.
-- Supply cap, Borrow cap and Debt ceiling: The `maxPercentChange` is relative and is denominated in BPS. (Ex. `50_00` for +-50% relative change).
- For example, for a current supply cap of an asset at 1_000_000 and `maxPercentChange` configured for supply cap at `50_00`, the max supply cap that can be configured is 1_500_000 and the minimum 500_000 via the steward.
+- Supply cap, Borrow cap, and Debt ceiling: The `maxPercentChange` is relative and is denominated in BPS. (Ex. `50_00` for +-50% relative change).
+ For example, with an asset's current supply cap at 1_000_000 and `maxPercentChange` configured for supply cap at `50_00`, the max supply cap that can be configured is 1_500_000, and the minimum is 500_000 via the steward.
- LTV, LT, LB: The `maxPercentChange` is in absolute values and is also denominated in BPS. (Ex. `5_00` for +-5% change in LTV).
For example, for a current LTV of an asset configured at 70_00 (70%) and `maxPercentChange` configured for ltv at `10_00`, the max ltv that can be configured is 77_00 (77%) and the minimum 63_00 (63%) via the steward.
@@ -66,6 +66,30 @@ Some assets/oracles can also be restricted on the RiskStewards by calling the `s
+## AGRS + Edge Infrastructure (Risk Oracles)
+
+With the introduction of Edge Risk Oracles by Chaos Labs, which leverages advanced off-chain infrastructure to deliver real-time risk updates to the Aave protocol via the Risk Oracle, the risk updates for the Aave protocol can be automated in a constrained manner. This can be done by combining the Edge Risk Oracle with the Aave Risk Steward, using a middleware contract `AaveStewardsInjector`.
+
+The Aave Risk Steward contract used for automated updates (called now [EdgeRiskSteward](./src/contracts/EdgeRiskSteward.sol)), has been slightly modified to only allow Interest Rates Updates on the protocol initially as a matter of extra security considerations.
+
+The following is a simple diagram of how the system works as a whole:
+
+
+
+### AaveStewardsInjector
+
+The [AaveStewardsInjector](./src/contracts//AaveStewardInjector.sol) is a Chainlink automation compatible contract which checks if the latest update from the Edge Risk Oracle could be pushed to the Risk Steward, and if so, injects it to the Aave Risk Stewards. The `AaveStewardsInjector` should be set as the `riskCouncil` on the Aave Risk Steward contract so it can inject updates.
+
+The `AaveStewardsInjector` has the following major functions:
+
+- `checkUpkeep()`: This method is called off-chain by Chainlink nodes every block to check if the latest update could be injected, and if so, calls `performUpKeep()`. It fetches the latest interest rate update for the whitelisted asset (initially WETH) using the `getLatestUpdateByParameterAndMarket()` method on the Risk Oracle, and checks if the update can be injected into the Risk Steward if not already executed before. If the latest update is not executed or disabled the method returns true.
+
+- `performUpkeep()`: The `performUpkeep()` method is called by the Chainlink automation nodes when the `checkUpkeep()` method returns true. The `performUpkeep()` call injects the latest update on the Risk Steward, unless that update has been disabled by the steward injector guardian or previously executed. After an update has been injected on the Risk Steward using the params from the Risk Oracle, we mark the updateId as executed on storage mapping `_isUpdateIdExecuted[id]`. The `performUpkeep()` method is permissionless on purpose, so as to allow injections from the Risk Oracle to the Risk Steward even in case of some downtime on the automation infra via a manual trigger.
+
+The Stewards Injector Guardian is an entity, which is the owner of the `AaveStewardsInjector` contract and has access to disable updates for the specific `updateId` using the `disableUpdateById()` method in case of any emergencies. The guardian can also change / add update types using the `addUpdateType()` method, to whitelist the type of updates that can be injected via the Stewards Injector.
+
+The `AaveStewardsInjector` contract also introduces an `EXPIRATION_PERIOD` to disallow outdated risk param updates to be injected. The `EXPIRATION_PERIOD` is set to 6 hours, which means after an update is pushed on the Edge Risk Oracle, the `AaveStewardsInjector` has a maximum of 6 hours to inject the update onto the Risk Steward otherwise the update expires.
+
## Security
- Certora security review: [2024-07-10](./audits/10-07-2024_Certora_AaveV3-Risk-Steward.pdf)
diff --git a/certora/confs/rules.conf b/certora/confs/rules.conf
index 5b08713..61a1992 100644
--- a/certora/confs/rules.conf
+++ b/certora/confs/rules.conf
@@ -1,7 +1,7 @@
{
"files": [
"certora/munged/src/contracts/RiskSteward.sol",
- "lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/periphery/contracts/v3-config-engine/AaveV3ConfigEngine.sol"
+ "lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/contracts/extensions/v3-config-engine/AaveV3ConfigEngine.sol"
],
"link": [
"RiskSteward:CONFIG_ENGINE=AaveV3ConfigEngine",
@@ -10,10 +10,9 @@
"aave-helpers=lib/aave-helpers",
"forge-std=lib/aave-helpers/lib/forge-std/src",
"aave-address-book=lib/aave-helpers/lib/aave-address-book/src",
- "solidity-utils=lib/aave-helpers/lib/solidity-utils/src",
- "aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
- "aave-v3-core=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/core",
- "aave-v3-periphery=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/periphery",
+ "solidity-utils=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/src",
+ "lib/aave-helpers:aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
+ "aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin",
"aave-capo=lib/aave-capo/src",
"lib/aave-capo:cl-synchronicity-price-adapter=lib/aave-capo/lib/cl-synchronicity-price-adapter/src"
],
diff --git a/certora/confs/sanity.conf b/certora/confs/sanity.conf
index fe4c17c..683b9d6 100644
--- a/certora/confs/sanity.conf
+++ b/certora/confs/sanity.conf
@@ -6,10 +6,9 @@
"aave-helpers=lib/aave-helpers",
"forge-std=lib/aave-helpers/lib/forge-std/src",
"aave-address-book=lib/aave-helpers/lib/aave-address-book/src",
- "solidity-utils=lib/aave-helpers/lib/solidity-utils/src",
- "aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
- "aave-v3-core=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/core",
- "aave-v3-periphery=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/periphery",
+ "solidity-utils=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/src",
+ "lib/aave-helpers:aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
+ "aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin",
"aave-capo=lib/aave-capo/src",
"lib/aave-capo:cl-synchronicity-price-adapter=lib/aave-capo/lib/cl-synchronicity-price-adapter/src"
],
diff --git a/docs/agrs-edge.png b/docs/agrs-edge.png
new file mode 100644
index 0000000..f17f864
Binary files /dev/null and b/docs/agrs-edge.png differ
diff --git a/foundry.toml b/foundry.toml
index 640a1df..862658f 100644
--- a/foundry.toml
+++ b/foundry.toml
@@ -3,7 +3,7 @@ src = 'src'
test = 'tests'
script = 'scripts'
out = 'out'
-solc = '0.8.19'
+solc = '0.8.20'
libs = ['lib']
remappings = [
]
@@ -16,10 +16,10 @@ src = 'zksync'
test = 'zksync'
script = 'scripts'
libs = ['lib']
-solc = '0.8.19'
+solc = '0.8.20'
fs_permissions = [{ access = "write", path = "./reports" }]
ffi = true
-evm_version = 'paris'
+evm_version = 'shanghai'
[profile.zksync.zksync]
compile = true
diff --git a/generator/features/__snapshots__/capUpdates.spec.ts.snap b/generator/features/__snapshots__/capUpdates.spec.ts.snap
index ef8e65c..e365b26 100644
--- a/generator/features/__snapshots__/capUpdates.spec.ts.snap
+++ b/generator/features/__snapshots__/capUpdates.spec.ts.snap
@@ -28,7 +28,7 @@ pragma solidity ^0.8.0;
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
import {RiskStewardsEthereum} from '../../../../scripts/networks/RiskStewardsEthereum.s.sol';
-import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
/**
* @title test
diff --git a/generator/features/__snapshots__/collateralUpdates.spec.ts.snap b/generator/features/__snapshots__/collateralUpdates.spec.ts.snap
index d458893..3e7a8af 100644
--- a/generator/features/__snapshots__/collateralUpdates.spec.ts.snap
+++ b/generator/features/__snapshots__/collateralUpdates.spec.ts.snap
@@ -39,8 +39,8 @@ pragma solidity ^0.8.0;
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
import {RiskStewardsEthereum} from '../../../../scripts/networks/RiskStewardsEthereum.s.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
-import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
/**
* @title test
diff --git a/generator/features/__snapshots__/rateUpdates.spec.ts.snap b/generator/features/__snapshots__/rateUpdates.spec.ts.snap
index 23620ef..337ce4f 100644
--- a/generator/features/__snapshots__/rateUpdates.spec.ts.snap
+++ b/generator/features/__snapshots__/rateUpdates.spec.ts.snap
@@ -76,8 +76,8 @@ pragma solidity ^0.8.0;
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
import {RiskStewardsEthereum} from '../../../../scripts/networks/RiskStewardsEthereum.s.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
-import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
/**
* @title test
diff --git a/generator/utils/importsResolver.spec.ts b/generator/utils/importsResolver.spec.ts
index 926309b..c91b8d7 100644
--- a/generator/utils/importsResolver.spec.ts
+++ b/generator/utils/importsResolver.spec.ts
@@ -17,11 +17,11 @@ describe('prefixWithImports', () => {
it('should detect v3 Engine imports', () => {
expect(prefixWithImports(`EngineFlags.KEEP_CURRENT`)).toContain(
- `import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';`,
+ `import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';`,
);
expect(prefixWithImports('IAaveV3ConfigEngine.CapsUpdate')).toContain(
- `import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';`,
+ `import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';`,
);
});
diff --git a/generator/utils/importsResolver.ts b/generator/utils/importsResolver.ts
index 314e904..469c4fa 100644
--- a/generator/utils/importsResolver.ts
+++ b/generator/utils/importsResolver.ts
@@ -95,11 +95,11 @@ export function prefixWithImports(code: string) {
}
// shared config engine imports
if (findMatch(code, 'EngineFlags')) {
- imports += `import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';\n`;
+ imports += `import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';\n`;
}
// v3 config engine imports
if (findMatch(code, 'IAaveV3ConfigEngine')) {
- imports += `import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';\n`;
+ imports += `import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';\n`;
}
// v2 config engine imports
if (findMatch(code, 'IAaveV2ConfigEngine')) {
diff --git a/lib/aave-helpers b/lib/aave-helpers
index b10d123..19d8ece 160000
--- a/lib/aave-helpers
+++ b/lib/aave-helpers
@@ -1 +1 @@
-Subproject commit b10d123434d124db02dd9d2048c9a56b09d2e2e1
+Subproject commit 19d8ece8a12d3d789ccb96fd184fef0a646b201c
diff --git a/package.json b/package.json
index 0bf8b22..644a8c7 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"prettier": "2.8.7",
"prettier-plugin-solidity": "1.1.3",
"vitest": "^2.0.4",
- "@bgd-labs/aave-address-book": "^3.2.1",
+ "@bgd-labs/aave-address-book": "^4.3.1",
"@bgd-labs/aave-cli": "^0.16.2",
"@bgd-labs/js-utils": "^1.4.2",
"@inquirer/prompts": "^3.3.0",
diff --git a/remappings.txt b/remappings.txt
index 9b47e90..25e78da 100644
--- a/remappings.txt
+++ b/remappings.txt
@@ -1,9 +1,8 @@
aave-helpers/=lib/aave-helpers/
forge-std/=lib/aave-helpers/lib/forge-std/src/
aave-address-book/=lib/aave-helpers/lib/aave-address-book/src/
-solidity-utils/=lib/aave-helpers/lib/solidity-utils/src/
-aave-v3-origin/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/
-aave-v3-core/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/core
-aave-v3-periphery/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/periphery
+solidity-utils/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/src/
+lib/aave-helpers:aave-v3-origin/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src
+aave-v3-origin/=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/
aave-capo/=lib/aave-capo/src
lib/aave-capo:cl-synchronicity-price-adapter/=lib/aave-capo/lib/cl-synchronicity-price-adapter/src/
diff --git a/scripts/RiskStewardsBase.s.sol b/scripts/RiskStewardsBase.s.sol
index 6ca8917..974ebc3 100644
--- a/scripts/RiskStewardsBase.s.sol
+++ b/scripts/RiskStewardsBase.s.sol
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
-import {IAaveV3ConfigEngine as IEngine, IPool} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {IAaveV3ConfigEngine as IEngine, IPool} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {IRiskSteward} from '../src/interfaces/IRiskSteward.sol';
import {ProtocolV3TestBase} from 'aave-helpers/src/ProtocolV3TestBase.sol';
import {IOwnable} from 'aave-address-book/common/IOwnable.sol';
@@ -40,12 +40,6 @@ abstract contract RiskStewardsBase is ProtocolV3TestBase {
* @notice This script doesn't broadcast as it's intended to be used via safe
*/
function run(bool broadcastToSafe) external {
- // TODO: remove once risk stewards are activated via governance
- vm.startPrank(IOwnable(address(STEWARD)).owner());
- address aclManager = STEWARD.POOL_DATA_PROVIDER().ADDRESSES_PROVIDER().getACLManager();
- IACLManager(aclManager).grantRole(IACLManager(aclManager).RISK_ADMIN_ROLE(), address(STEWARD));
- vm.stopPrank();
-
vm.startPrank(STEWARD.RISK_COUNCIL());
bytes[] memory callDatas = _simulateAndGenerateDiff();
vm.stopPrank();
diff --git a/scripts/deploy/DeployInjector.s.sol b/scripts/deploy/DeployInjector.s.sol
new file mode 100644
index 0000000..594e324
--- /dev/null
+++ b/scripts/deploy/DeployInjector.s.sol
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+import 'solidity-utils/contracts/utils/ScriptUtils.sol';
+import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol';
+import {AaveV3EthereumLido, AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol';
+import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
+import {ICreate3Factory} from 'solidity-utils/contracts/create3/interfaces/ICreate3Factory.sol';
+import {IOwnable} from 'aave-address-book/common/IOwnable.sol';
+import {EdgeRiskSteward, IRiskSteward, IPoolDataProvider, IEngine} from '../../src/contracts/EdgeRiskSteward.sol';
+import {AaveStewardInjector, IAaveStewardInjector} from '../../src/contracts/AaveStewardInjector.sol';
+
+library DeployStewardContracts {
+ address constant EDGE_RISK_ORACLE = 0x7ABB46C690C52E919687D19ebF89C81A6136C1F2;
+
+ function _deployRiskStewards(
+ address poolDataProvider,
+ address configEngine,
+ address riskCouncil,
+ address governance
+ ) internal returns (address) {
+ address riskSteward = address(new EdgeRiskSteward(
+ IPoolDataProvider(poolDataProvider),
+ IEngine(configEngine),
+ riskCouncil,
+ _getRiskConfig()
+ ));
+ IOwnable(riskSteward).transferOwnership(governance);
+ return riskSteward;
+ }
+
+ function _deployStewardsInjector(
+ bytes32 salt,
+ address riskSteward,
+ address guardian,
+ address whitelistedAsset
+ ) internal returns (address) {
+ address stewardInjector = ICreate3Factory(MiscEthereum.CREATE_3_FACTORY).create(
+ salt,
+ abi.encodePacked(
+ type(AaveStewardInjector).creationCode,
+ abi.encode(
+ EDGE_RISK_ORACLE,
+ riskSteward,
+ guardian,
+ whitelistedAsset
+ )
+ )
+ );
+ return stewardInjector;
+ }
+
+ function _getRiskConfig() internal pure returns (IRiskSteward.Config memory) {
+ return IRiskSteward.Config({
+ ltv: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 25}),
+ liquidationThreshold: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 25}),
+ liquidationBonus: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 50}),
+ supplyCap: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 100_00}),
+ borrowCap: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 100_00}),
+ debtCeiling: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 20_00}),
+ baseVariableBorrowRate: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 50}),
+ variableRateSlope1: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 50}),
+ variableRateSlope2: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 5_00}),
+ optimalUsageRatio: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 3_00}),
+ priceCapLst: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 5_00}),
+ priceCapStable: IRiskSteward.RiskParamConfig({minDelay: 1 days, maxPercentChange: 50})
+ });
+ }
+}
+
+// make deploy-ledger contract=scripts/deploy/DeployInjector.s.sol:DeployEthereumLido chain=mainnet
+contract DeployEthereumLido is EthereumScript {
+ address constant GUARDIAN = 0xff37939808EcF199A2D599ef91D699Fb13dab7F7;
+
+ function run() external {
+ vm.startBroadcast();
+ bytes32 salt = 'StewardInjector';
+ address predictedStewardsInjector = ICreate3Factory(MiscEthereum.CREATE_3_FACTORY).predictAddress(msg.sender, salt);
+
+ address riskSteward = DeployStewardContracts._deployRiskStewards(
+ address(AaveV3EthereumLido.AAVE_PROTOCOL_DATA_PROVIDER),
+ AaveV3EthereumLido.CONFIG_ENGINE,
+ predictedStewardsInjector,
+ GovernanceV3Ethereum.EXECUTOR_LVL_1
+ );
+
+ DeployStewardContracts._deployStewardsInjector(salt, riskSteward, GUARDIAN, AaveV3EthereumLidoAssets.WETH_UNDERLYING);
+ vm.stopBroadcast();
+ }
+}
diff --git a/scripts/deploy/DeployStewards.s.sol b/scripts/deploy/DeployStewards.s.sol
index 14d38ff..f258cf8 100644
--- a/scripts/deploy/DeployStewards.s.sol
+++ b/scripts/deploy/DeployStewards.s.sol
@@ -27,18 +27,18 @@ library DeployRiskStewards {
function _getRiskConfig() internal pure returns (IRiskSteward.Config memory) {
return IRiskSteward.Config({
- ltv: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 3_00}),
- liquidationThreshold: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 3_00}),
- liquidationBonus: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 2_00}),
+ ltv: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 25}),
+ liquidationThreshold: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 25}),
+ liquidationBonus: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 50}),
supplyCap: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 100_00}),
borrowCap: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 100_00}),
- debtCeiling: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 100_00}),
- baseVariableBorrowRate: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 2_00}),
- variableRateSlope1: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 2_00}),
- variableRateSlope2: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 20_00}),
- optimalUsageRatio: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 10_00}),
- priceCapLst: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 15_00}),
- priceCapStable: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 2_00})
+ debtCeiling: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 20_00}),
+ baseVariableBorrowRate: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 50}),
+ variableRateSlope1: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 50}),
+ variableRateSlope2: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 5_00}),
+ optimalUsageRatio: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 3_00}),
+ priceCapLst: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 5_00}),
+ priceCapStable: IRiskSteward.RiskParamConfig({minDelay: 3 days, maxPercentChange: 50})
});
}
}
diff --git a/src/contracts/AaveStewardInjector.sol b/src/contracts/AaveStewardInjector.sol
new file mode 100644
index 0000000..9d91672
--- /dev/null
+++ b/src/contracts/AaveStewardInjector.sol
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.0;
+
+import {IRiskOracle} from './dependencies/IRiskOracle.sol';
+import {IRiskSteward} from '../interfaces/IRiskSteward.sol';
+import {IAaveStewardInjector, AutomationCompatibleInterface} from '../interfaces/IAaveStewardInjector.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {Ownable} from 'solidity-utils/contracts/oz-common/Ownable.sol';
+
+/**
+ * @title AaveStewardInjector
+ * @author BGD Labs
+ * @notice Contract to perform automation on risk steward using the edge risk oracle.
+ * The contract only permits for injecting rate updates for the whitelisted asset.
+ * @dev Aave chainlink automation-keeper-compatible contract to:
+ * - check if updates from edge risk oracles can be injected into risk steward.
+ * - injectes risk updates on the risk steward if all conditions are met.
+ */
+contract AaveStewardInjector is Ownable, IAaveStewardInjector {
+ /// @inheritdoc IAaveStewardInjector
+ address public immutable RISK_ORACLE;
+
+ /// @inheritdoc IAaveStewardInjector
+ address public immutable RISK_STEWARD;
+
+ /// @inheritdoc IAaveStewardInjector
+ address public immutable WHITELISTED_ASSET;
+
+ /// @inheritdoc IAaveStewardInjector
+ string public constant WHITELISTED_UPDATE_TYPE = 'RateStrategyUpdate';
+
+ /**
+ * @inheritdoc IAaveStewardInjector
+ * @dev after an update is added on the risk oracle, the update is only valid from the timestamp it was added
+ * on the risk oracle plus the expiration time, after which the update cannot be injected into the risk steward.
+ */
+ uint256 public constant EXPIRATION_PERIOD = 6 hours;
+
+ mapping(uint256 => bool) internal _isUpdateIdExecuted;
+ mapping(uint256 => bool) internal _disabledUpdates;
+
+ /**
+ * @param riskOracle address of the edge risk oracle contract.
+ * @param riskSteward address of the risk steward contract.
+ * @param guardian address of the guardian / owner of the stewards injector.
+ * @param whitelistedAsset address of the whitelisted asset for which update can be injected.
+ */
+ constructor(address riskOracle, address riskSteward, address guardian, address whitelistedAsset) {
+ RISK_ORACLE = riskOracle;
+ RISK_STEWARD = riskSteward;
+ WHITELISTED_ASSET = whitelistedAsset;
+ _transferOwnership(guardian);
+ }
+
+ /**
+ * @inheritdoc AutomationCompatibleInterface
+ * @dev run off-chain, checks if the latest update from risk oracle should be injected on risk steward
+ */
+ function checkUpkeep(bytes memory) public view virtual override returns (bool, bytes memory) {
+ IRiskOracle.RiskParameterUpdate memory updateRiskParams = IRiskOracle(RISK_ORACLE)
+ .getLatestUpdateByParameterAndMarket(WHITELISTED_UPDATE_TYPE, WHITELISTED_ASSET);
+
+ if (_canUpdateBeInjected(updateRiskParams)) return (true, '');
+
+ return (false, '');
+ }
+
+ /**
+ * @inheritdoc AutomationCompatibleInterface
+ * @dev executes injection of the latest update from the risk oracle into the risk steward.
+ */
+ function performUpkeep(bytes calldata) external override {
+ IRiskOracle.RiskParameterUpdate memory updateRiskParams = IRiskOracle(RISK_ORACLE)
+ .getLatestUpdateByParameterAndMarket(WHITELISTED_UPDATE_TYPE, WHITELISTED_ASSET);
+
+ if (!_canUpdateBeInjected(updateRiskParams)) {
+ revert UpdateCannotBeInjected();
+ }
+
+ IRiskSteward(RISK_STEWARD).updateRates(_repackRateUpdate(updateRiskParams));
+ _isUpdateIdExecuted[updateRiskParams.updateId] = true;
+
+ emit ActionSucceeded(updateRiskParams.updateId);
+ }
+
+ /// @inheritdoc IAaveStewardInjector
+ function isDisabled(uint256 updateId) public view returns (bool) {
+ return _disabledUpdates[updateId];
+ }
+
+ /// @inheritdoc IAaveStewardInjector
+ function disableUpdateById(uint256 updateId, bool disabled) external onlyOwner {
+ _disabledUpdates[updateId] = disabled;
+ emit UpdateDisabled(updateId, disabled);
+ }
+
+ /// @inheritdoc IAaveStewardInjector
+ function isUpdateIdExecuted(uint256 updateid) public view returns (bool) {
+ return _isUpdateIdExecuted[updateid];
+ }
+
+ /**
+ * @notice method to check if the update from risk oracle could be injected into the risk steward.
+ * @dev only allow injecting interest rate updates for the whitelisted asset.
+ * @param updateRiskParams struct containing the risk param update from the risk oralce to check if it can be injected.
+ * @return true if the update could be injected to the risk steward, false otherwise.
+ */
+ function _canUpdateBeInjected(
+ IRiskOracle.RiskParameterUpdate memory updateRiskParams
+ ) internal view returns (bool) {
+ return (
+ !isUpdateIdExecuted(updateRiskParams.updateId) &&
+ (updateRiskParams.timestamp + EXPIRATION_PERIOD > block.timestamp) &&
+ updateRiskParams.market == WHITELISTED_ASSET &&
+ keccak256(bytes(updateRiskParams.updateType)) == keccak256(bytes(WHITELISTED_UPDATE_TYPE)) &&
+ !isDisabled(updateRiskParams.updateId)
+ );
+ }
+
+ /**
+ * @notice method to repack update params from the risk oracle to the format of risk steward.
+ * @param riskParams the risk update param from the edge risk oracle.
+ * @return the repacked risk update in the format of the risk steward.
+ */
+ function _repackRateUpdate(
+ IRiskOracle.RiskParameterUpdate memory riskParams
+ ) internal pure returns (IEngine.RateStrategyUpdate[] memory) {
+ IEngine.RateStrategyUpdate[] memory rateUpdate = new IEngine.RateStrategyUpdate[](1);
+ rateUpdate[0].asset = riskParams.market;
+ rateUpdate[0].params = abi.decode(riskParams.newValue, (IEngine.InterestRateInputData));
+ return rateUpdate;
+ }
+}
diff --git a/src/contracts/EdgeRiskSteward.sol b/src/contracts/EdgeRiskSteward.sol
new file mode 100644
index 0000000..a63f65b
--- /dev/null
+++ b/src/contracts/EdgeRiskSteward.sol
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.0;
+
+import './RiskSteward.sol';
+
+/**
+ * @title EdgeRiskSteward
+ * @author BGD labs
+ * @notice Contract to manage the interest rates params within configured bound on aave v3 pool.
+ * To be triggered by the Aave Steward Injector Contract in a automated way via the Edge Risk Oracle.
+ */
+contract EdgeRiskSteward is RiskSteward {
+ /**
+ * @param poolDataProvider The pool data provider of the pool to be controlled by the steward
+ * @param engine the config engine to be used by the steward
+ * @param riskCouncil the safe address of the council being able to interact with the steward
+ * @param riskConfig the risk configuration to setup for each individual risk param
+ */
+ constructor(
+ IPoolDataProvider poolDataProvider,
+ IEngine engine,
+ address riskCouncil,
+ Config memory riskConfig
+ ) RiskSteward(poolDataProvider, engine, riskCouncil, riskConfig) {}
+
+ /// @inheritdoc IRiskSteward
+ function updateCaps(IEngine.CapsUpdate[] calldata) external virtual override onlyRiskCouncil {
+ revert UpdateNotAllowed();
+ }
+
+ /// @inheritdoc IRiskSteward
+ function updateCollateralSide(
+ IEngine.CollateralUpdate[] calldata
+ ) external virtual override onlyRiskCouncil {
+ revert UpdateNotAllowed();
+ }
+
+ /// @inheritdoc IRiskSteward
+ function updateLstPriceCaps(
+ PriceCapLstUpdate[] calldata
+ ) external virtual override onlyRiskCouncil {
+ revert UpdateNotAllowed();
+ }
+
+ /// @inheritdoc IRiskSteward
+ function updateStablePriceCaps(
+ PriceCapStableUpdate[] calldata
+ ) external virtual override onlyRiskCouncil {
+ revert UpdateNotAllowed();
+ }
+}
diff --git a/src/contracts/RiskSteward.sol b/src/contracts/RiskSteward.sol
index 8519e9a..e08e54f 100644
--- a/src/contracts/RiskSteward.sol
+++ b/src/contracts/RiskSteward.sol
@@ -4,11 +4,11 @@ pragma solidity ^0.8.0;
import {IPoolDataProvider} from 'aave-address-book/AaveV3.sol';
import {Address} from 'solidity-utils/contracts/oz-common/Address.sol';
import {SafeCast} from 'solidity-utils/contracts/oz-common/SafeCast.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {Ownable} from 'solidity-utils/contracts/oz-common/Ownable.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/AaveV3ConfigEngine.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {IRiskSteward} from '../interfaces/IRiskSteward.sol';
-import {IDefaultInterestRateStrategyV2} from 'aave-v3-origin/core/contracts/interfaces/IDefaultInterestRateStrategyV2.sol';
+import {IDefaultInterestRateStrategyV2} from 'aave-v3-origin/src/contracts/interfaces/IDefaultInterestRateStrategyV2.sol';
import {IPriceCapAdapter} from 'aave-capo/interfaces/IPriceCapAdapter.sol';
import {IPriceCapAdapterStable} from 'aave-capo/interfaces/IPriceCapAdapterStable.sol';
@@ -67,13 +67,15 @@ contract RiskSteward is Ownable, IRiskSteward {
}
/// @inheritdoc IRiskSteward
- function updateCaps(IEngine.CapsUpdate[] calldata capsUpdate) external onlyRiskCouncil {
+ function updateCaps(IEngine.CapsUpdate[] calldata capsUpdate) external virtual onlyRiskCouncil {
_validateCapsUpdate(capsUpdate);
_updateCaps(capsUpdate);
}
/// @inheritdoc IRiskSteward
- function updateRates(IEngine.RateStrategyUpdate[] calldata ratesUpdate) external onlyRiskCouncil {
+ function updateRates(
+ IEngine.RateStrategyUpdate[] calldata ratesUpdate
+ ) external virtual onlyRiskCouncil {
_validateRatesUpdate(ratesUpdate);
_updateRates(ratesUpdate);
}
@@ -81,7 +83,7 @@ contract RiskSteward is Ownable, IRiskSteward {
/// @inheritdoc IRiskSteward
function updateCollateralSide(
IEngine.CollateralUpdate[] calldata collateralUpdates
- ) external onlyRiskCouncil {
+ ) external virtual onlyRiskCouncil {
_validateCollateralsUpdate(collateralUpdates);
_updateCollateralSide(collateralUpdates);
}
@@ -89,7 +91,7 @@ contract RiskSteward is Ownable, IRiskSteward {
/// @inheritdoc IRiskSteward
function updateLstPriceCaps(
PriceCapLstUpdate[] calldata priceCapUpdates
- ) external onlyRiskCouncil {
+ ) external virtual onlyRiskCouncil {
_validatePriceCapUpdate(priceCapUpdates);
_updateLstPriceCaps(priceCapUpdates);
}
@@ -97,7 +99,7 @@ contract RiskSteward is Ownable, IRiskSteward {
/// @inheritdoc IRiskSteward
function updateStablePriceCaps(
PriceCapStableUpdate[] calldata priceCapUpdates
- ) external onlyRiskCouncil {
+ ) external virtual onlyRiskCouncil {
_validatePriceCapStableUpdate(priceCapUpdates);
_updateStablePriceCaps(priceCapUpdates);
}
diff --git a/src/contracts/dependencies/AutomationCompatibleInterface.sol b/src/contracts/dependencies/AutomationCompatibleInterface.sol
new file mode 100644
index 0000000..a60e3f9
--- /dev/null
+++ b/src/contracts/dependencies/AutomationCompatibleInterface.sol
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+interface AutomationCompatibleInterface {
+ /**
+ * @notice method that is simulated by the keepers to see if any work actually
+ * needs to be performed. This method does does not actually need to be
+ * executable, and since it is only ever simulated it can consume lots of gas.
+ * @dev To ensure that it is never called, you may want to add the
+ * cannotExecute modifier from KeeperBase to your implementation of this
+ * method.
+ * @param checkData specified in the upkeep registration so it is always the
+ * same for a registered upkeep. This can easily be broken down into specific
+ * arguments using `abi.decode`, so multiple upkeeps can be registered on the
+ * same contract and easily differentiated by the contract.
+ * @return upkeepNeeded boolean to indicate whether the keeper should call
+ * performUpkeep or not.
+ * @return performData bytes that the keeper should call performUpkeep with, if
+ * upkeep is needed. If you would like to encode data to decode later, try
+ * `abi.encode`.
+ */
+ function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData);
+
+ /**
+ * @notice method that is actually executed by the keepers, via the registry.
+ * The data returned by the checkUpkeep simulation will be passed into
+ * this method to actually be executed.
+ * @dev The input to this method should not be trusted, and the caller of the
+ * method should not even be restricted to any single registry. Anyone should
+ * be able call it, and the input should be validated, there is no guarantee
+ * that the data passed in is the performData returned from checkUpkeep. This
+ * could happen due to malicious keepers, racing keepers, or simply a state
+ * change while the performUpkeep transaction is waiting for confirmation.
+ * Always validate the data passed in.
+ * @param performData is the data which was passed back from the checkData
+ * simulation. If it is encoded, it can easily be decoded into other types by
+ * calling `abi.decode`. This data should not be trusted, and should be
+ * validated against the contract's current state.
+ */
+ function performUpkeep(bytes calldata performData) external;
+}
diff --git a/src/contracts/dependencies/IRiskOracle.sol b/src/contracts/dependencies/IRiskOracle.sol
new file mode 100644
index 0000000..16d7575
--- /dev/null
+++ b/src/contracts/dependencies/IRiskOracle.sol
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+interface IRiskOracle {
+ struct RiskParameterUpdate {
+ uint256 timestamp; // Timestamp of the update
+ bytes newValue; // Encoded parameters, flexible for various data types
+ string referenceId; // External reference, potentially linking to a document or off-chain data
+ bytes previousValue; // Previous value of the parameter for historical comparison
+ string updateType; // Classification of the update for validation purposes
+ uint256 updateId; // Unique identifier for this specific update
+ address market; // Address for market of the parameter update
+ bytes additionalData; // Additional data for the update
+ }
+
+ event ParameterUpdated(
+ string referenceId,
+ bytes newValue,
+ bytes previousValue,
+ uint256 timestamp,
+ string indexed updateType,
+ uint256 indexed updateId,
+ address indexed market,
+ bytes additionalData
+ );
+
+ event AuthorizedSenderAdded(address indexed sender);
+ event AuthorizedSenderRemoved(address indexed sender);
+ event UpdateTypeAdded(string indexed updateType);
+
+ /**
+ * @notice Adds a new sender to the list of addresses authorized to perform updates.
+ * @param sender Address to be authorized.
+ */
+ function addAuthorizedSender(address sender) external;
+
+ /**
+ * @notice Removes an address from the list of authorized senders.
+ * @param sender Address to be unauthorized.
+ */
+ function removeAuthorizedSender(address sender) external;
+
+ /**
+ * @notice Method to fetch the counter which tracks of the total number of updates.
+ * @return The latest update counter.
+ */
+ function updateCounter() external view returns (uint256);
+
+ /**
+ * @notice Adds a new type of update to the list of authorized update types.
+ * @param newUpdateType New type of update to allow.
+ */
+ function addUpdateType(string memory newUpdateType) external;
+
+ /**
+ * @notice Publishes a new risk parameter update.
+ * @param referenceId An external reference ID associated with the update.
+ * @param newValue The new value of the risk parameter being updated.
+ * @param updateType Type of update performed, must be previously authorized.
+ * @param market Address for market of the parameter update
+ * @param additionalData Additional data for the update
+ */
+ function publishRiskParameterUpdate(
+ string memory referenceId,
+ bytes memory newValue,
+ string memory updateType,
+ address market,
+ bytes memory additionalData
+ ) external;
+
+ /**
+ * @notice Publishes multiple risk parameter updates in a single transaction.
+ * @param referenceIds Array of external reference IDs.
+ * @param newValues Array of new values for each update.
+ * @param updateTypes Array of types for each update, all must be authorized.
+ * @param markets Array of addresses for markets of the parameter updates
+ * @param additionalData Array of additional data for the updates
+ *
+ */
+ function publishBulkRiskParameterUpdates(
+ string[] memory referenceIds,
+ bytes[] memory newValues,
+ string[] memory updateTypes,
+ address[] memory markets,
+ bytes[] memory additionalData
+ ) external;
+
+ function getAllUpdateTypes() external view returns (string[] memory);
+
+ /**
+ * @notice Fetches the most recent update for a specific parameter in a specific market.
+ * @param updateType The identifier for the parameter.
+ * @param market The market identifier.
+ * @return The most recent RiskParameterUpdate for the specified parameter and market.
+ */
+ function getLatestUpdateByParameterAndMarket(
+ string memory updateType,
+ address market
+ ) external view returns (RiskParameterUpdate memory);
+
+ /*
+ * @notice Fetches the update for a provided updateId.
+ * @param updateId Update ID.
+ * @return The most recent RiskParameterUpdate for the specified id.
+ */
+ function getUpdateById(uint256 updateId) external view returns (RiskParameterUpdate memory);
+
+ /**
+ * @notice Checks if an address is authorized to perform updates.
+ * @param sender Address to check.
+ * @return Boolean indicating whether the address is authorized.
+ */
+ function isAuthorized(address sender) external view returns (bool);
+}
diff --git a/src/contracts/dependencies/RiskOracle.sol b/src/contracts/dependencies/RiskOracle.sol
new file mode 100644
index 0000000..28fce01
--- /dev/null
+++ b/src/contracts/dependencies/RiskOracle.sol
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.20;
+
+import '@openzeppelin/contracts/access/Ownable.sol';
+import '@openzeppelin/contracts/utils/Strings.sol';
+
+/**
+ * @title Dynamic Risk Oracle
+ */
+contract RiskOracle is Ownable {
+ struct RiskParameterUpdate {
+ uint256 timestamp; // Timestamp of the update
+ bytes newValue; // Encoded parameters, flexible for various data types
+ string referenceId; // External reference, potentially linking to a document or off-chain data
+ bytes previousValue; // Previous value of the parameter for historical comparison
+ string updateType; // Classification of the update for validation purposes
+ uint256 updateId; // Unique identifier for this specific update
+ address market; // Address for market of the parameter update
+ bytes additionalData; // Additional data for the update
+ }
+
+ string[] private allUpdateTypes; // Array to store all update types
+ mapping(string => bool) internal validUpdateTypes; // Whitelist of valid update type identifiers
+ mapping(uint256 => RiskParameterUpdate) private updatesById; // Mapping from unique update ID to the update details
+ mapping(address => bool) private authorizedSenders; // Authorized accounts capable of executing updates
+
+ mapping(address => mapping(string => uint256)) public latestUpdateIdByMarketAndType; // Mapping to store the latest update ID for each combination of market and update type
+ uint256 public updateCounter; // Counter to keep track of the total number of updates
+ string public description; // Description of contract
+
+ event ParameterUpdated(
+ string referenceId,
+ bytes newValue,
+ bytes previousValue,
+ uint256 timestamp,
+ string indexed updateType,
+ uint256 indexed updateId,
+ address indexed market,
+ bytes additionalData
+ );
+
+ event AuthorizedSenderAdded(address indexed sender);
+ event AuthorizedSenderRemoved(address indexed sender);
+ event UpdateTypeAdded(string indexed updateType);
+
+ modifier onlyAuthorized() {
+ require(authorizedSenders[msg.sender], 'Unauthorized: Sender not authorized.');
+ _;
+ }
+
+ modifier onlyValidString(string memory input) {
+ require(bytes(input).length > 0 && bytes(input).length <= 64, 'Invalid update type string');
+ _;
+ }
+
+ /**
+ * @notice Constructor to set initial authorized addresses and approved update types.
+ * @param _description Description of contract
+ * @param initialSenders List of addresses that will initially be authorized to perform updates.
+ * @param initialUpdateTypes List of valid update types initially allowed.
+ */
+ constructor(
+ string memory _description,
+ address[] memory initialSenders,
+ string[] memory initialUpdateTypes
+ ) Ownable(msg.sender) {
+ description = _description;
+ for (uint256 i = 0; i < initialSenders.length; i++) {
+ authorizedSenders[initialSenders[i]] = true; // Automatically authorize initial senders
+ }
+ for (uint256 i = 0; i < initialUpdateTypes.length; i++) {
+ if (!validUpdateTypes[initialUpdateTypes[i]]) {
+ // Ensure no duplicate updateTypes can be set
+ validUpdateTypes[initialUpdateTypes[i]] = true; // Register initial valid updates
+ allUpdateTypes.push(initialUpdateTypes[i]);
+ }
+ }
+ }
+
+ /**
+ * @notice Adds a new sender to the list of addresses authorized to perform updates.
+ * @param sender Address to be authorized.
+ */
+ function addAuthorizedSender(address sender) external onlyOwner {
+ require(!authorizedSenders[sender], 'Sender already authorized.');
+ authorizedSenders[sender] = true;
+ emit AuthorizedSenderAdded(sender);
+ }
+
+ /**
+ * @notice Removes an address from the list of authorized senders.
+ * @param sender Address to be unauthorized.
+ */
+ function removeAuthorizedSender(address sender) external onlyOwner {
+ require(authorizedSenders[sender], 'Sender not authorized.');
+ authorizedSenders[sender] = false;
+ emit AuthorizedSenderRemoved(sender);
+ }
+
+ /**
+ * @notice Adds a new type of update to the list of authorized update types.
+ * @param newUpdateType New type of update to allow.
+ */
+ function addUpdateType(
+ string memory newUpdateType
+ ) external onlyOwner onlyValidString(newUpdateType) {
+ require(!validUpdateTypes[newUpdateType], 'Update type already exists.');
+ validUpdateTypes[newUpdateType] = true;
+ allUpdateTypes.push(newUpdateType);
+ emit UpdateTypeAdded(newUpdateType);
+ }
+
+ /**
+ * @notice Publishes a new risk parameter update.
+ * @param referenceId An external reference ID associated with the update.
+ * @param newValue The new value of the risk parameter being updated.
+ * @param updateType Type of update performed, must be previously authorized.
+ * @param market Address for market of the parameter update
+ * @param additionalData Additional data for the update
+ */
+ function publishRiskParameterUpdate(
+ string memory referenceId,
+ bytes memory newValue,
+ string memory updateType,
+ address market,
+ bytes memory additionalData
+ ) external onlyAuthorized {
+ _processUpdate(referenceId, newValue, updateType, market, additionalData);
+ }
+
+ /**
+ * @notice Publishes multiple risk parameter updates in a single transaction.
+ * @param referenceIds Array of external reference IDs.
+ * @param newValues Array of new values for each update.
+ * @param updateTypes Array of types for each update, all must be authorized.
+ * @param markets Array of addresses for markets of the parameter updates
+ * @param additionalData Array of additional data for the updates
+ *
+ */
+ function publishBulkRiskParameterUpdates(
+ string[] memory referenceIds,
+ bytes[] memory newValues,
+ string[] memory updateTypes,
+ address[] memory markets,
+ bytes[] memory additionalData
+ ) external onlyAuthorized {
+ for (uint256 i = 0; i < referenceIds.length; i++) {
+ _processUpdate(referenceIds[i], newValues[i], updateTypes[i], markets[i], additionalData[i]);
+ }
+ }
+
+ /**
+ * @dev Processes an update internally, recording and emitting an event.
+ */
+ function _processUpdate(
+ string memory referenceId,
+ bytes memory newValue,
+ string memory updateType,
+ address market,
+ bytes memory additionalData
+ ) internal {
+ require(validUpdateTypes[updateType], 'Unauthorized update type.');
+ updateCounter++;
+ uint256 previousUpdateId = latestUpdateIdByMarketAndType[market][updateType];
+ bytes memory previousValue = updatesById[previousUpdateId].newValue;
+
+ RiskParameterUpdate memory newUpdate = RiskParameterUpdate(
+ block.timestamp,
+ newValue,
+ referenceId,
+ previousValue,
+ updateType,
+ updateCounter,
+ market,
+ additionalData
+ );
+ updatesById[updateCounter] = newUpdate;
+
+ // Update the latest update ID for the market and updateType combination
+ latestUpdateIdByMarketAndType[market][updateType] = updateCounter;
+
+ emit ParameterUpdated(
+ referenceId,
+ newValue,
+ previousValue,
+ block.timestamp,
+ updateType,
+ updateCounter,
+ market,
+ additionalData
+ );
+ }
+
+ function getAllUpdateTypes() external view returns (string[] memory) {
+ return allUpdateTypes;
+ }
+
+ /**
+ * @notice Fetches the most recent update for a specific parameter in a specific market.
+ * @param updateType The identifier for the parameter.
+ * @param market The market identifier.
+ * @return The most recent RiskParameterUpdate for the specified parameter and market.
+ */
+ function getLatestUpdateByParameterAndMarket(
+ string memory updateType,
+ address market
+ ) external view returns (RiskParameterUpdate memory) {
+ uint256 updateId = latestUpdateIdByMarketAndType[market][updateType];
+ require(updateId > 0, 'No update found for the specified parameter and market.');
+ return updatesById[updateId];
+ }
+
+ /*
+ * @notice Fetches the update for a provided updateId.
+ * @param updateId Update ID.
+ * @return The most recent RiskParameterUpdate for the specified id.
+ */
+ function getUpdateById(uint256 updateId) external view returns (RiskParameterUpdate memory) {
+ require(updateId > 0 && updateId <= updateCounter, 'Invalid update ID.');
+ return updatesById[updateId];
+ }
+
+ /**
+ * @notice Checks if an address is authorized to perform updates.
+ * @param sender Address to check.
+ * @return Boolean indicating whether the address is authorized.
+ */
+ function isAuthorized(address sender) external view returns (bool) {
+ return authorizedSenders[sender];
+ }
+}
diff --git a/src/contracts/examples/ArbitrumExample.sol b/src/contracts/examples/ArbitrumExample.sol
index 416dd15..8531ff5 100644
--- a/src/contracts/examples/ArbitrumExample.sol
+++ b/src/contracts/examples/ArbitrumExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3ArbitrumAssets} from 'aave-address-book/AaveV3Arbitrum.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsArbitrum} from '../../../scripts/networks/RiskStewardsArbitrum.s.sol';
// make run-script network=arbitrum contract_path=src/contracts/examples/ArbitrumExample.sol:ArbitrumExample broadcast=false
diff --git a/src/contracts/examples/AvalancheExample.sol b/src/contracts/examples/AvalancheExample.sol
index a5997bc..9664578 100644
--- a/src/contracts/examples/AvalancheExample.sol
+++ b/src/contracts/examples/AvalancheExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3AvalancheAssets} from 'aave-address-book/AaveV3Avalanche.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsAvalanche} from '../../../scripts/networks/RiskStewardsAvalanche.s.sol';
// make run-script network=avalanche contract_path=src/contracts/examples/AvalancheExample.sol:AvalancheExample broadcast=false
diff --git a/src/contracts/examples/BNBExample.sol b/src/contracts/examples/BNBExample.sol
index b40b9a2..0749851 100644
--- a/src/contracts/examples/BNBExample.sol
+++ b/src/contracts/examples/BNBExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3BNBAssets} from 'aave-address-book/AaveV3BNB.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsBNB} from '../../../scripts/networks/RiskStewardsBNB.s.sol';
// make run-script network=bnb contract_path=src/contracts/examples/BNBExample.sol:BNBExample broadcast=false
diff --git a/src/contracts/examples/BaseExample.sol b/src/contracts/examples/BaseExample.sol
index ec25ab0..d3e17b2 100644
--- a/src/contracts/examples/BaseExample.sol
+++ b/src/contracts/examples/BaseExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3BaseAssets} from 'aave-address-book/AaveV3Base.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsBaseChain} from '../../../scripts/networks/RiskStewardsBaseChain.s.sol';
// make run-script network=base contract_path=src/contracts/examples/BaseExample.sol:BaseExample broadcast=false
diff --git a/src/contracts/examples/EthereumExample.sol b/src/contracts/examples/EthereumExample.sol
index e547b2f..3ee5fb5 100644
--- a/src/contracts/examples/EthereumExample.sol
+++ b/src/contracts/examples/EthereumExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsEthereum} from '../../../scripts/networks/RiskStewardsEthereum.s.sol';
import {IRiskSteward, IPriceCapAdapter} from '../../interfaces/IRiskSteward.sol';
diff --git a/src/contracts/examples/EthereumLidoExample.sol b/src/contracts/examples/EthereumLidoExample.sol
index f5d5cc4..d705a0e 100644
--- a/src/contracts/examples/EthereumLidoExample.sol
+++ b/src/contracts/examples/EthereumLidoExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsEthereumLido} from '../../../scripts/networks/RiskStewardsEthereumLido.s.sol';
// make run-script network=mainnet contract_path=src/contracts/examples/EthereumLidoExample.sol:EthereumLidoExample broadcast=false
diff --git a/src/contracts/examples/GnosisExample.sol b/src/contracts/examples/GnosisExample.sol
index 6edfac2..5d87900 100644
--- a/src/contracts/examples/GnosisExample.sol
+++ b/src/contracts/examples/GnosisExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3GnosisAssets} from 'aave-address-book/AaveV3Gnosis.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsGnosis} from '../../../scripts/networks/RiskStewardsGnosis.s.sol';
// make run-script network=gnosis contract_path=src/contracts/examples/GnosisExample.sol:GnosisExample broadcast=false
diff --git a/src/contracts/examples/MetisExample.sol b/src/contracts/examples/MetisExample.sol
index e422711..a7d7b6d 100644
--- a/src/contracts/examples/MetisExample.sol
+++ b/src/contracts/examples/MetisExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3MetisAssets} from 'aave-address-book/AaveV3Metis.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsMetis} from '../../../scripts/networks/RiskStewardsMetis.s.sol';
// make run-script network=metis contract_path=src/contracts/examples/MetisExample.sol:MetisExample broadcast=false
diff --git a/src/contracts/examples/OptimismExample.sol b/src/contracts/examples/OptimismExample.sol
index 4ffc4ef..47921db 100644
--- a/src/contracts/examples/OptimismExample.sol
+++ b/src/contracts/examples/OptimismExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3OptimismAssets} from 'aave-address-book/AaveV3Optimism.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsOptimism} from '../../../scripts/networks/RiskStewardsOptimism.s.sol';
// make run-script network=optimism contract_path=src/contracts/examples/OptimismExample.sol:OptimismExample broadcast=false
diff --git a/src/contracts/examples/PolygonExample.sol b/src/contracts/examples/PolygonExample.sol
index 04678c8..230a73f 100644
--- a/src/contracts/examples/PolygonExample.sol
+++ b/src/contracts/examples/PolygonExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3PolygonAssets} from 'aave-address-book/AaveV3Polygon.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsPolygon} from '../../../scripts/networks/RiskStewardsPolygon.s.sol';
// make run-script network=polygon contract_path=src/contracts/examples/PolygonExample.sol:PolygonExample broadcast=false
diff --git a/src/contracts/examples/ScrollExample.sol b/src/contracts/examples/ScrollExample.sol
index 973ef3d..12be4bb 100644
--- a/src/contracts/examples/ScrollExample.sol
+++ b/src/contracts/examples/ScrollExample.sol
@@ -2,8 +2,8 @@
pragma solidity ^0.8.0;
import {AaveV3ScrollAssets} from 'aave-address-book/AaveV3Scroll.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {RiskStewardsScroll} from '../../../scripts/networks/RiskStewardsScroll.s.sol';
// make run-script network=scroll contract_path=src/contracts/examples/ScrollExample.sol:ScrollExample broadcast=false
diff --git a/src/interfaces/IAaveStewardInjector.sol b/src/interfaces/IAaveStewardInjector.sol
new file mode 100644
index 0000000..bbd683c
--- /dev/null
+++ b/src/interfaces/IAaveStewardInjector.sol
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
+
+import {AutomationCompatibleInterface} from '../contracts/dependencies/AutomationCompatibleInterface.sol';
+
+/**
+ * @title IAaveStewardInjector
+ * @author BGD Labs
+ * @notice Defines the interface for the injector contract to automate actions for Risk Steward.
+ **/
+interface IAaveStewardInjector is AutomationCompatibleInterface {
+ /**
+ * @notice Emitted when performUpkeep is called and an update is injected into the risk steward.
+ * @param updateId the risk oracle update id injected into the risk steward.
+ */
+ event ActionSucceeded(uint256 indexed updateId);
+
+ /**
+ * @notice Emitted when injection of a updateId is disabled/enabled.
+ * @param updateId the risk oracle update id for which automation is disabled/enabled.
+ * @param disabled true if updateId is disabled, false otherwise.
+ */
+ event UpdateDisabled(uint256 indexed updateId, bool indexed disabled);
+
+ /**
+ * @notice The following update cannot be injected in the steward injector because the conditions are not met.
+ */
+ error UpdateCannotBeInjected();
+
+ /**
+ * @notice method to check if injection of a updateId on risk steward is disabled.
+ * @param updateId updateId from risk oracle to check if disabled.
+ * @return bool if updateId is disabled or not.
+ **/
+ function isDisabled(uint256 updateId) external view returns (bool);
+
+ /**
+ * @notice method called by owner to disable/enabled injection of a updateId on risk steward.
+ * @param updateId updateId from risk oracle for which we need to disable/enable injection.
+ * @param disabled true if updateId should be disabled, false otherwise.
+ */
+ function disableUpdateById(uint256 updateId, bool disabled) external;
+
+ /**
+ * @notice method to check if the updateId from the risk oracle has been executed/injected into the risk steward.
+ * @param updateid the updateId from the risk oracle to check if already executed/injected.
+ * @return bool true if the updateId is executed/injected, false otherwise.
+ */
+ function isUpdateIdExecuted(uint256 updateid) external view returns (bool);
+
+ /**
+ * @notice method to get the address of the edge risk oracle contract.
+ * @return edge risk oracle contract address.
+ */
+ function RISK_ORACLE() external view returns (address);
+
+ /**
+ * @notice method to get the address of the aave risk steward contract.
+ * @return aave risk steward contract address.
+ */
+ function RISK_STEWARD() external view returns (address);
+
+ /**
+ * @notice method to get the expiration time for an update from the risk oracle.
+ * @return time in seconds of the expiration time.
+ */
+ function EXPIRATION_PERIOD() external view returns (uint256);
+
+ /**
+ * @notice method to get the whitelisted update type for which injection is allowed from the risk oracle into the stewards.
+ * @return string for the whitelisted update type - interest rate update.
+ */
+ function WHITELISTED_UPDATE_TYPE() external view returns (string memory);
+
+ /**
+ * @notice method to get the whitelisted asset for which injection is allowed from the risk oracle into the stewards.
+ * @return address for the whitelisted asset.
+ */
+ function WHITELISTED_ASSET() external view returns (address);
+}
diff --git a/src/interfaces/IRiskSteward.sol b/src/interfaces/IRiskSteward.sol
index 03ca61c..d233e3b 100644
--- a/src/interfaces/IRiskSteward.sol
+++ b/src/interfaces/IRiskSteward.sol
@@ -2,7 +2,7 @@
pragma solidity ^0.8.0;
import {IPoolDataProvider} from 'aave-address-book/AaveV3.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/AaveV3ConfigEngine.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {IPriceCapAdapter} from 'aave-capo/interfaces/IPriceCapAdapter.sol';
/**
@@ -26,6 +26,11 @@ interface IRiskSteward {
*/
error UpdateNotInRange();
+ /**
+ * @notice The risk param update is not allowed on the Risk Steward.
+ */
+ error UpdateNotAllowed();
+
/**
* @notice There must be at least one risk param update per execution
*/
diff --git a/tests/AaveStewardsInjector.t.sol b/tests/AaveStewardsInjector.t.sol
new file mode 100644
index 0000000..faa3df1
--- /dev/null
+++ b/tests/AaveStewardsInjector.t.sol
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.0;
+
+import {RiskSteward, IRiskSteward, IEngine, EngineFlags} from 'src/contracts/RiskSteward.sol';
+import {TestnetProcedures} from 'aave-v3-origin/tests/utils/TestnetProcedures.sol';
+import {RiskOracle} from '../src/contracts/dependencies/RiskOracle.sol';
+import {AaveStewardInjector, IAaveStewardInjector} from '../src/contracts/AaveStewardInjector.sol';
+import {AaveV3EthereumLidoAssets} from 'aave-address-book/AaveV3EthereumLido.sol';
+
+contract AaveStewardsInjector_Test is TestnetProcedures {
+ RiskSteward _riskSteward;
+ RiskOracle _riskOracle;
+ AaveStewardInjector _stewardInjector;
+
+ address _riskOracleOwner = address(20);
+ address _stewardsInjectorOwner = address(25);
+
+ event ActionSucceeded(uint256 indexed updateId);
+ event AddressWhitelisted(address indexed contractAddress, bool indexed isWhitelisted);
+ event UpdateDisabled(uint256 indexed updateId, bool indexed disabled);
+ event UpdateTypeChanged(string indexed updateType, bool indexed isValid);
+
+ function setUp() public {
+ initTestEnvironment();
+
+ IRiskSteward.RiskParamConfig memory defaultRiskParamConfig = IRiskSteward.RiskParamConfig({
+ minDelay: 3 days,
+ maxPercentChange: 5_00 // 5%
+ });
+
+ IRiskSteward.Config memory riskConfig = IRiskSteward.Config({
+ ltv: defaultRiskParamConfig,
+ liquidationThreshold: defaultRiskParamConfig,
+ liquidationBonus: defaultRiskParamConfig,
+ supplyCap: defaultRiskParamConfig,
+ borrowCap: defaultRiskParamConfig,
+ debtCeiling: defaultRiskParamConfig,
+ baseVariableBorrowRate: defaultRiskParamConfig,
+ variableRateSlope1: defaultRiskParamConfig,
+ variableRateSlope2: defaultRiskParamConfig,
+ optimalUsageRatio: defaultRiskParamConfig,
+ priceCapLst: defaultRiskParamConfig,
+ priceCapStable: defaultRiskParamConfig
+ });
+
+ // setup risk oracle
+ vm.startPrank(_riskOracleOwner);
+ address[] memory initialSenders = new address[](1);
+ initialSenders[0] = _riskOracleOwner;
+ string[] memory initialUpdateTypes = new string[](1);
+ initialUpdateTypes[0] = 'RateStrategyUpdate';
+
+ _riskOracle = new RiskOracle(
+ 'RiskOracle',
+ initialSenders,
+ initialUpdateTypes
+ );
+ vm.stopPrank();
+
+ // setup steward injector
+ vm.startPrank(_stewardsInjectorOwner);
+
+ address computedRiskStewardAddress = vm.computeCreateAddress(_stewardsInjectorOwner, vm.getNonce(_stewardsInjectorOwner) + 1);
+ _stewardInjector = new AaveStewardInjector(
+ address(_riskOracle),
+ address(computedRiskStewardAddress),
+ _stewardsInjectorOwner,
+ address(weth)
+ );
+
+ // setup risk steward
+ _riskSteward = new RiskSteward(
+ contracts.protocolDataProvider,
+ IEngine(report.configEngine),
+ address(_stewardInjector),
+ riskConfig
+ );
+
+ vm.assertEq(computedRiskStewardAddress, address(_riskSteward));
+ vm.stopPrank();
+
+ vm.startPrank(poolAdmin);
+ contracts.aclManager.addRiskAdmin(address(_riskSteward));
+ vm.stopPrank();
+
+ vm.warp(5 days);
+ }
+
+ function test_rateInjection() public {
+ // add rate update to risk oracle
+ _addUpdateToRiskOracle();
+
+ vm.expectEmit(address(_stewardInjector));
+ emit ActionSucceeded(1);
+
+ bool isAutomationPerformed = _checkAndPerformAutomation();
+ assertTrue(isAutomationPerformed);
+ }
+
+ function test_disableUpdate() public {
+ // add rate update to risk oracle
+ _addUpdateToRiskOracle();
+
+ vm.prank(address(1));
+ vm.expectRevert(bytes('Ownable: caller is not the owner'));
+ _stewardInjector.disableUpdateById(1, true);
+
+ assertFalse(_stewardInjector.isDisabled(1));
+
+ vm.expectEmit(address(_stewardInjector));
+ emit UpdateDisabled(1, true);
+
+ vm.prank(_stewardsInjectorOwner);
+ _stewardInjector.disableUpdateById(1, true);
+
+ assertTrue(_stewardInjector.isDisabled(1));
+
+ bool isAutomationPerformed = _checkAndPerformAutomation();
+ assertFalse(isAutomationPerformed);
+
+ vm.expectEmit(address(_stewardInjector));
+ emit UpdateDisabled(1, false);
+
+ vm.prank(_stewardsInjectorOwner);
+ _stewardInjector.disableUpdateById(1, false);
+
+ assertFalse(_stewardInjector.isDisabled(1));
+
+ isAutomationPerformed = _checkAndPerformAutomation();
+ assertTrue(isAutomationPerformed);
+ }
+
+ function test_isUpdatedIdExecuted() public {
+ // add rate update to risk oracle
+ _addUpdateToRiskOracle();
+
+ assertFalse(_stewardInjector.isUpdateIdExecuted(1));
+
+ bool isAutomationPerformed = _checkAndPerformAutomation();
+ assertTrue(isAutomationPerformed);
+ assertTrue(_stewardInjector.isUpdateIdExecuted(1));
+
+ isAutomationPerformed = _checkAndPerformAutomation();
+ assertFalse(isAutomationPerformed);
+ }
+
+ function test_expiredUpdate() public {
+ // add rate update to risk oracle
+ _addUpdateToRiskOracle();
+
+ uint256 initialTs = block.timestamp;
+ vm.warp(initialTs + _stewardInjector.EXPIRATION_PERIOD());
+
+ bool isAutomationPerformed = _checkAndPerformAutomation();
+ assertFalse(isAutomationPerformed);
+
+ vm.warp(initialTs);
+ isAutomationPerformed = _checkAndPerformAutomation();
+ assertTrue(isAutomationPerformed);
+ }
+
+ function test_reverts_sameUpdateInjectedTwice() public {
+ _addUpdateToRiskOracle(EngineFlags.KEEP_CURRENT, 5_00, EngineFlags.KEEP_CURRENT, EngineFlags.KEEP_CURRENT, block.timestamp - 100); // updateId 1
+
+ vm.expectEmit(address(_stewardInjector));
+ emit ActionSucceeded(1);
+
+ bool isAutomationPerformed = _checkAndPerformAutomation();
+ assertTrue(isAutomationPerformed);
+
+ vm.expectRevert(IAaveStewardInjector.UpdateCannotBeInjected.selector);
+ _stewardInjector.performUpkeep('');
+ }
+
+ function test_reverts_ifUpdateDoesNotExist() public {
+ vm.expectRevert(bytes('No update found for the specified parameter and market.'));
+ _stewardInjector.checkUpkeep('');
+
+ vm.expectRevert(bytes('No update found for the specified parameter and market.'));
+ _stewardInjector.performUpkeep('');
+ }
+
+ function _addUpdateToRiskOracle(
+ uint256 optimalUsageRatio,
+ uint256 baseVariableBorrowRate,
+ uint256 variableRateSlope1,
+ uint256 variableRateSlope2,
+ uint256 updateTimestamp
+ ) internal {
+ uint256 currentTs = block.timestamp;
+ vm.startPrank(_riskOracleOwner);
+ vm.warp(updateTimestamp);
+
+ IEngine.InterestRateInputData memory rate = IEngine.InterestRateInputData({
+ optimalUsageRatio: optimalUsageRatio,
+ baseVariableBorrowRate: baseVariableBorrowRate,
+ variableRateSlope1: variableRateSlope1,
+ variableRateSlope2: variableRateSlope2
+ });
+ _riskOracle.publishRiskParameterUpdate(
+ 'referenceId',
+ abi.encode(rate),
+ 'RateStrategyUpdate',
+ address(weth),
+ 'additionalData'
+ );
+ vm.warp(currentTs);
+ vm.stopPrank();
+ }
+
+ function _addUpdateToRiskOracle() internal {
+ vm.startPrank(_riskOracleOwner);
+
+ IEngine.InterestRateInputData memory rate = IEngine.InterestRateInputData({
+ optimalUsageRatio: EngineFlags.KEEP_CURRENT,
+ baseVariableBorrowRate: 5_00,
+ variableRateSlope1: EngineFlags.KEEP_CURRENT,
+ variableRateSlope2: EngineFlags.KEEP_CURRENT
+ });
+ _riskOracle.publishRiskParameterUpdate(
+ 'referenceId',
+ abi.encode(rate),
+ 'RateStrategyUpdate',
+ address(weth),
+ 'additionalData'
+ );
+ vm.stopPrank();
+ }
+
+ function _checkAndPerformAutomation() internal virtual returns (bool) {
+ (bool shouldRunKeeper, bytes memory performData) = _stewardInjector.checkUpkeep('');
+ if (shouldRunKeeper) {
+ _stewardInjector.performUpkeep(performData);
+ }
+ return shouldRunKeeper;
+ }
+}
diff --git a/tests/EdgeRiskSteward.t.sol b/tests/EdgeRiskSteward.t.sol
new file mode 100644
index 0000000..a35c5b9
--- /dev/null
+++ b/tests/EdgeRiskSteward.t.sol
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: BUSL-1.1
+pragma solidity ^0.8.0;
+
+import {EdgeRiskSteward} from 'src/contracts/EdgeRiskSteward.sol';
+import {IPriceCapAdapter} from 'aave-capo/interfaces/IPriceCapAdapter.sol';
+import './RiskSteward.t.sol';
+
+contract EdgeRiskSteward_Test is RiskSteward_Test {
+ function setUp() public override {
+ super.setUp();
+
+ vm.startPrank(GovernanceV3Ethereum.EXECUTOR_LVL_1);
+ steward = new EdgeRiskSteward(
+ AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER,
+ IEngine(configEngine),
+ riskCouncil,
+ riskConfig
+ );
+ AaveV3Ethereum.ACL_MANAGER.addRiskAdmin(address(steward));
+ vm.stopPrank();
+ }
+
+ /* ----------------------------- Caps Tests ----------------------------- */
+
+ function test_updateCaps() public override {
+ (uint256 daiBorrowCapBefore, uint256 daiSupplyCapBefore) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
+
+ IEngine.CapsUpdate[] memory capUpdates = new IEngine.CapsUpdate[](1);
+ capUpdates[0] = IEngine.CapsUpdate(
+ AaveV3EthereumAssets.DAI_UNDERLYING,
+ (daiSupplyCapBefore * 110) / 100, // 10% relative increase
+ (daiBorrowCapBefore * 110) / 100 // 10% relative increase
+ );
+
+ vm.startPrank(riskCouncil);
+ vm.expectRevert(IRiskSteward.UpdateNotAllowed.selector);
+ steward.updateCaps(capUpdates);
+ }
+
+ function test_updateCaps_outOfRange() public override {}
+
+ function test_updateCaps_debounceNotRespected() public override {}
+
+ function test_updateCaps_allKeepCurrent() public override {}
+
+ function test_updateCaps_sameUpdate() public override {}
+
+ function test_updateCaps_assetUnlisted() public override {}
+
+ function test_updateCaps_assetRestricted() public override {}
+
+ function test_updateCaps_toValueZeroNotAllowed() public override {}
+
+ /* ----------------------------- Collateral Tests ----------------------------- */
+
+ function test_updateCollateralSide() public override {
+ (, uint256 ltvBefore, uint256 ltBefore, uint256 lbBefore, , , , , , ) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveConfigurationData(AaveV3EthereumAssets.UNI_UNDERLYING);
+
+ // as the definition is with 2 decimals, and config engine does not take the decimals into account, so we divide by 100.
+ uint256 debtCeilingBefore = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getDebtCeiling(
+ AaveV3EthereumAssets.UNI_UNDERLYING
+ ) / 100;
+
+ IEngine.CollateralUpdate[] memory collateralUpdates = new IEngine.CollateralUpdate[](1);
+ collateralUpdates[0] = IEngine.CollateralUpdate({
+ asset: AaveV3EthereumAssets.UNI_UNDERLYING,
+ ltv: ltvBefore + 10_00, // 10% absolute increase
+ liqThreshold: ltBefore + 5_00, // 5% absolute increase
+ liqBonus: (lbBefore - 100_00) + 2_00, // 2% absolute increase
+ debtCeiling: (debtCeilingBefore * 110) / 100, // 10% relative increase
+ liqProtocolFee: EngineFlags.KEEP_CURRENT
+ });
+
+ vm.startPrank(riskCouncil);
+ vm.expectRevert(IRiskSteward.UpdateNotAllowed.selector);
+ steward.updateCollateralSide(collateralUpdates);
+ }
+
+ function test_updateCollateralSide_outOfRange() public override {}
+
+ function test_updateCollateralSide_debounceNotRespected() public override {}
+
+ function test_updateCollateralSide_liqProtocolFeeNotAllowed() public override {}
+
+ function test_updateCollateralSide_assetUnlisted() public override {}
+
+ function test_updateCollateralSide_assetRestricted() public override {}
+
+ function test_updateCollateralSide_toValueZeroNotAllowed() public override {}
+
+ function test_updateCollaterals_allKeepCurrent() public override {}
+
+ function test_updateCollaterals_sameUpdate() public override {}
+
+ /* ----------------------------- LST Price Cap Tests ----------------------------- */
+
+ function test_updateLstPriceCap() public {
+ IRiskSteward.PriceCapLstUpdate[] memory priceCapUpdates = new IRiskSteward.PriceCapLstUpdate[](
+ 1
+ );
+ priceCapUpdates[0] = IRiskSteward.PriceCapLstUpdate({
+ oracle: AaveV3EthereumAssets.wstETH_ORACLE,
+ priceCapUpdateParams: IPriceCapAdapter.PriceCapUpdateParams({
+ snapshotTimestamp: uint48(block.timestamp - 2),
+ snapshotRatio: 1.1e18,
+ maxYearlyRatioGrowthPercent: 9_68
+ })
+ });
+
+ vm.startPrank(riskCouncil);
+ vm.expectRevert(IRiskSteward.UpdateNotAllowed.selector);
+ steward.updateLstPriceCaps(priceCapUpdates);
+ }
+
+ /* ----------------------------- Stable Price Cap Test ----------------------------- */
+
+ function test_updateStablePriceCap() public {
+ IRiskSteward.PriceCapStableUpdate[]
+ memory priceCapUpdates = new IRiskSteward.PriceCapStableUpdate[](1);
+
+ priceCapUpdates[0] = IRiskSteward.PriceCapStableUpdate({
+ oracle: AaveV3EthereumAssets.USDT_ORACLE,
+ priceCap: 1060000
+ });
+
+ vm.startPrank(riskCouncil);
+ vm.expectRevert(IRiskSteward.UpdateNotAllowed.selector);
+ steward.updateStablePriceCaps(priceCapUpdates);
+ }
+}
diff --git a/tests/RiskSteward.t.sol b/tests/RiskSteward.t.sol
index b4f2fc2..a886ac0 100644
--- a/tests/RiskSteward.t.sol
+++ b/tests/RiskSteward.t.sol
@@ -3,17 +3,17 @@ pragma solidity ^0.8.0;
import 'forge-std/Test.sol';
import {IACLManager, IPoolConfigurator, IPoolDataProvider} from 'aave-address-book/AaveV3.sol';
-import {IDefaultInterestRateStrategyV2} from 'aave-v3-origin/core/contracts/interfaces/IDefaultInterestRateStrategyV2.sol';
+import {IDefaultInterestRateStrategyV2} from 'aave-v3-origin/src/contracts/interfaces/IDefaultInterestRateStrategyV2.sol';
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
import {RiskSteward, IRiskSteward, IEngine, EngineFlags} from 'src/contracts/RiskSteward.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/AaveV3ConfigEngine.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol';
import {ConfigEngineDeployer} from './utils/ConfigEngineDeployer.sol';
contract RiskSteward_Test is Test {
address public constant riskCouncil = address(42);
- RiskSteward public steward;
+ IRiskSteward public steward;
address public configEngine;
IRiskSteward.RiskParamConfig public defaultRiskParamConfig;
IRiskSteward.Config public riskConfig;
@@ -22,8 +22,8 @@ contract RiskSteward_Test is Test {
event RiskConfigSet(IRiskSteward.Config indexed riskConfig);
- function setUp() public {
- vm.createSelectFork(vm.rpcUrl('mainnet'), 20439517);
+ function setUp() public virtual {
+ vm.createSelectFork(vm.rpcUrl('mainnet'), 20934847);
configEngine = AaveV3Ethereum.CONFIG_ENGINE;
@@ -64,7 +64,7 @@ contract RiskSteward_Test is Test {
/* ----------------------------- Caps Tests ----------------------------- */
- function test_updateCaps() public {
+ function test_updateCaps() public virtual {
(uint256 daiBorrowCapBefore, uint256 daiSupplyCapBefore) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
@@ -113,7 +113,7 @@ contract RiskSteward_Test is Test {
assertEq(daiSupplyCapAfter, capUpdates[0].supplyCap);
}
- function test_updateCaps_outOfRange() public {
+ function test_updateCaps_outOfRange() public virtual {
(uint256 daiBorrowCapBefore, uint256 daiSupplyCapBefore) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
@@ -140,7 +140,7 @@ contract RiskSteward_Test is Test {
vm.stopPrank();
}
- function test_updateCaps_debounceNotRespected() public {
+ function test_updateCaps_debounceNotRespected() public virtual {
(uint256 daiBorrowCapBefore, uint256 daiSupplyCapBefore) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
@@ -155,13 +155,24 @@ contract RiskSteward_Test is Test {
vm.startPrank(riskCouncil);
steward.updateCaps(capUpdates);
+ (daiBorrowCapBefore, daiSupplyCapBefore) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
+
+ capUpdates = new IEngine.CapsUpdate[](1);
+ capUpdates[0] = IEngine.CapsUpdate(
+ AaveV3EthereumAssets.DAI_UNDERLYING,
+ daiSupplyCapBefore + 1,
+ daiBorrowCapBefore + 1
+ );
+
// expect revert as minimum time has not passed for next update
vm.expectRevert(IRiskSteward.DebounceNotRespected.selector);
steward.updateCaps(capUpdates);
vm.stopPrank();
}
- function test_updateCaps_allKeepCurrent() public {
+ function test_updateCaps_allKeepCurrent() public virtual {
(uint256 daiBorrowCapBefore, uint256 daiSupplyCapBefore) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
@@ -180,16 +191,34 @@ contract RiskSteward_Test is Test {
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
- RiskSteward.Debounce memory lastUpdated = steward.getTimelock(
- AaveV3EthereumAssets.DAI_UNDERLYING
+ assertEq(daiBorrowCapBefore, daiBorrowCapAfter);
+ assertEq(daiSupplyCapBefore, daiSupplyCapAfter);
+ }
+
+ function test_updateCaps_sameUpdate() public virtual {
+ (uint256 daiBorrowCapBefore, uint256 daiSupplyCapBefore) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
+
+ IEngine.CapsUpdate[] memory capUpdates = new IEngine.CapsUpdate[](1);
+ capUpdates[0] = IEngine.CapsUpdate(
+ AaveV3EthereumAssets.DAI_UNDERLYING,
+ daiSupplyCapBefore,
+ daiBorrowCapBefore
);
- assertEq(daiBorrowCapAfter, daiBorrowCapBefore);
- assertEq(daiSupplyCapAfter, daiSupplyCapBefore);
- assertEq(lastUpdated.supplyCapLastUpdated, 0);
- assertEq(lastUpdated.borrowCapLastUpdated, 0);
+
+ vm.startPrank(riskCouncil);
+ steward.updateCaps(capUpdates);
+
+ (uint256 daiBorrowCapAfter, uint256 daiSupplyCapAfter) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveCaps(AaveV3EthereumAssets.DAI_UNDERLYING);
+
+ assertEq(daiBorrowCapBefore, daiBorrowCapAfter);
+ assertEq(daiSupplyCapBefore, daiSupplyCapAfter);
}
- function test_updateCaps_assetUnlisted() public {
+ function test_updateCaps_assetUnlisted() public virtual {
address unlistedAsset = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84; // stETH
IEngine.CapsUpdate[] memory capUpdates = new IEngine.CapsUpdate[](1);
@@ -201,7 +230,7 @@ contract RiskSteward_Test is Test {
steward.updateCaps(capUpdates);
}
- function test_updateCaps_assetRestricted() public {
+ function test_updateCaps_assetRestricted() public virtual {
vm.startPrank(GovernanceV3Ethereum.EXECUTOR_LVL_1);
steward.setAddressRestricted(AaveV3EthereumAssets.GHO_UNDERLYING, true);
vm.stopPrank();
@@ -215,7 +244,7 @@ contract RiskSteward_Test is Test {
vm.stopPrank();
}
- function test_updateCaps_toValueZeroNotAllowed() public {
+ function test_updateCaps_toValueZeroNotAllowed() public virtual {
// set risk config to allow 100% cap change to 0
IRiskSteward.RiskParamConfig memory capsParamConfig = IRiskSteward.RiskParamConfig({
minDelay: 5 days,
@@ -372,6 +401,23 @@ contract RiskSteward_Test is Test {
vm.startPrank(riskCouncil);
steward.updateRates(rateUpdates);
+ (
+ beforeOptimalUsageRatio,
+ beforeBaseVariableBorrowRate,
+ beforeVariableRateSlope1,
+ beforeVariableRateSlope2
+ ) = _getInterestRatesForAsset(AaveV3EthereumAssets.WETH_UNDERLYING);
+
+ rateUpdates[0] = IEngine.RateStrategyUpdate({
+ asset: AaveV3EthereumAssets.WETH_UNDERLYING,
+ params: IEngine.InterestRateInputData({
+ optimalUsageRatio: beforeOptimalUsageRatio + 1,
+ baseVariableBorrowRate: beforeBaseVariableBorrowRate + 1,
+ variableRateSlope1: beforeVariableRateSlope1 + 1,
+ variableRateSlope2: beforeVariableRateSlope2 + 1
+ })
+ });
+
// expect revert as minimum time has not passed for next update
vm.expectRevert(IRiskSteward.DebounceNotRespected.selector);
steward.updateRates(rateUpdates);
@@ -416,9 +462,79 @@ contract RiskSteward_Test is Test {
steward.updateRates(rateUpdates);
}
+ function test_updateRates_allKeepCurrent() public {
+ (
+ uint256 beforeOptimalUsageRatio,
+ uint256 beforeBaseVariableBorrowRate,
+ uint256 beforeVariableRateSlope1,
+ uint256 beforeVariableRateSlope2
+ ) = _getInterestRatesForAsset(AaveV3EthereumAssets.WETH_UNDERLYING);
+
+ IEngine.RateStrategyUpdate[] memory rateUpdates = new IEngine.RateStrategyUpdate[](1);
+ rateUpdates[0] = IEngine.RateStrategyUpdate({
+ asset: AaveV3EthereumAssets.WETH_UNDERLYING,
+ params: IEngine.InterestRateInputData({
+ optimalUsageRatio: EngineFlags.KEEP_CURRENT,
+ baseVariableBorrowRate: EngineFlags.KEEP_CURRENT,
+ variableRateSlope1: EngineFlags.KEEP_CURRENT,
+ variableRateSlope2: EngineFlags.KEEP_CURRENT
+ })
+ });
+
+ vm.startPrank(riskCouncil);
+ steward.updateRates(rateUpdates);
+
+ (
+ uint256 afterOptimalUsageRatio,
+ uint256 afterBaseVariableBorrowRate,
+ uint256 afterVariableRateSlope1,
+ uint256 afterVariableRateSlope2
+ ) = _getInterestRatesForAsset(AaveV3EthereumAssets.WETH_UNDERLYING);
+
+ assertEq(beforeOptimalUsageRatio, afterOptimalUsageRatio);
+ assertEq(beforeBaseVariableBorrowRate, afterBaseVariableBorrowRate);
+ assertEq(beforeVariableRateSlope1, afterVariableRateSlope1);
+ assertEq(beforeVariableRateSlope2, afterVariableRateSlope2);
+ }
+
+ function test_updateRate_sameUpdate() public {
+ (
+ uint256 beforeOptimalUsageRatio,
+ uint256 beforeBaseVariableBorrowRate,
+ uint256 beforeVariableRateSlope1,
+ uint256 beforeVariableRateSlope2
+ ) = _getInterestRatesForAsset(AaveV3EthereumAssets.WETH_UNDERLYING);
+
+ IEngine.RateStrategyUpdate[] memory rateUpdates = new IEngine.RateStrategyUpdate[](1);
+ rateUpdates[0] = IEngine.RateStrategyUpdate({
+ asset: AaveV3EthereumAssets.WETH_UNDERLYING,
+ params: IEngine.InterestRateInputData({
+ optimalUsageRatio: beforeOptimalUsageRatio,
+ baseVariableBorrowRate: beforeBaseVariableBorrowRate,
+ variableRateSlope1: beforeVariableRateSlope1,
+ variableRateSlope2: beforeVariableRateSlope2
+ })
+ });
+
+ vm.startPrank(riskCouncil);
+ steward.updateRates(rateUpdates);
+
+ (
+ uint256 afterOptimalUsageRatio,
+ uint256 afterBaseVariableBorrowRate,
+ uint256 afterVariableRateSlope1,
+ uint256 afterVariableRateSlope2
+ ) = _getInterestRatesForAsset(AaveV3EthereumAssets.WETH_UNDERLYING);
+
+ assertEq(beforeOptimalUsageRatio, afterOptimalUsageRatio);
+ assertEq(beforeBaseVariableBorrowRate, afterBaseVariableBorrowRate);
+ assertEq(beforeVariableRateSlope1, afterVariableRateSlope1);
+ assertEq(beforeVariableRateSlope2, afterVariableRateSlope2);
+ }
+
/* ----------------------------- Collateral Tests ----------------------------- */
- function test_updateCollateralSide() public {
+ function test_updateCollateralSide() public virtual {
(, uint256 ltvBefore, uint256 ltBefore, uint256 lbBefore, , , , , , ) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveConfigurationData(AaveV3EthereumAssets.UNI_UNDERLYING);
@@ -507,7 +623,7 @@ contract RiskSteward_Test is Test {
assertEq(lastUpdated.liquidationBonusLastUpdated, block.timestamp);
}
- function test_updateCollateralSide_outOfRange() public {
+ function test_updateCollateralSide_outOfRange() public virtual {
(, uint256 ltvBefore, uint256 ltBefore, uint256 lbBefore, , , , , , ) = AaveV3Ethereum
.AAVE_PROTOCOL_DATA_PROVIDER
.getReserveConfigurationData(AaveV3EthereumAssets.UNI_UNDERLYING);
@@ -548,7 +664,7 @@ contract RiskSteward_Test is Test {
vm.stopPrank();
}
- function test_updateCollateralSide_debounceNotRespected() public {
+ function test_updateCollateralSide_debounceNotRespected() public virtual {
// as the definition is with 2 decimals, and config engine does not take the decimals into account, so we divide by 100.
uint256 debtCeilingBefore = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getDebtCeiling(
AaveV3EthereumAssets.UNI_UNDERLYING
@@ -569,13 +685,28 @@ contract RiskSteward_Test is Test {
vm.warp(block.timestamp + 1 days);
+ debtCeilingBefore =
+ AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getDebtCeiling(
+ AaveV3EthereumAssets.UNI_UNDERLYING
+ ) /
+ 100;
+
+ collateralUpdates[0] = IEngine.CollateralUpdate({
+ asset: AaveV3EthereumAssets.UNI_UNDERLYING,
+ ltv: EngineFlags.KEEP_CURRENT,
+ liqThreshold: EngineFlags.KEEP_CURRENT,
+ liqBonus: EngineFlags.KEEP_CURRENT,
+ debtCeiling: debtCeilingBefore + 1,
+ liqProtocolFee: EngineFlags.KEEP_CURRENT
+ });
+
// expect revert as minimum time has not passed for next update
vm.expectRevert(IRiskSteward.DebounceNotRespected.selector);
steward.updateCollateralSide(collateralUpdates);
vm.stopPrank();
}
- function test_updateCollateralSide_liqProtocolFeeNotAllowed() public {
+ function test_updateCollateralSide_liqProtocolFeeNotAllowed() public virtual {
IEngine.CollateralUpdate[] memory collateralUpdates = new IEngine.CollateralUpdate[](1);
collateralUpdates[0] = IEngine.CollateralUpdate({
asset: AaveV3EthereumAssets.UNI_UNDERLYING,
@@ -592,7 +723,7 @@ contract RiskSteward_Test is Test {
vm.stopPrank();
}
- function test_updateCollateralSide_assetUnlisted() public {
+ function test_updateCollateralSide_assetUnlisted() public virtual {
IEngine.CollateralUpdate[] memory collateralUpdates = new IEngine.CollateralUpdate[](1);
collateralUpdates[0] = IEngine.CollateralUpdate({
asset: 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84, // stETH
@@ -608,7 +739,7 @@ contract RiskSteward_Test is Test {
steward.updateCollateralSide(collateralUpdates);
}
- function test_updateCollateralSide_assetRestricted() public {
+ function test_updateCollateralSide_assetRestricted() public virtual {
vm.startPrank(GovernanceV3Ethereum.EXECUTOR_LVL_1);
steward.setAddressRestricted(AaveV3EthereumAssets.UNI_UNDERLYING, true);
vm.stopPrank();
@@ -628,7 +759,7 @@ contract RiskSteward_Test is Test {
steward.updateCollateralSide(collateralUpdates);
}
- function test_updateCollateralSide_toValueZeroNotAllowed() public {
+ function test_updateCollateralSide_toValueZeroNotAllowed() public virtual {
// set risk config to allow 100% collateral param change to 0
IRiskSteward.RiskParamConfig memory collateralParamConfig = IRiskSteward.RiskParamConfig({
minDelay: 5 days,
@@ -658,6 +789,82 @@ contract RiskSteward_Test is Test {
steward.updateCollateralSide(collateralUpdates);
}
+ function test_updateCollaterals_allKeepCurrent() public virtual {
+ (, uint256 ltvBefore, uint256 ltBefore, uint256 lbBefore, , , , , , ) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveConfigurationData(AaveV3EthereumAssets.UNI_UNDERLYING);
+
+ uint256 debtCeilingBefore = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getDebtCeiling(
+ AaveV3EthereumAssets.UNI_UNDERLYING
+ ) / 100;
+
+ IEngine.CollateralUpdate[] memory collateralUpdates = new IEngine.CollateralUpdate[](1);
+ collateralUpdates[0] = IEngine.CollateralUpdate({
+ asset: AaveV3EthereumAssets.UNI_UNDERLYING,
+ ltv: EngineFlags.KEEP_CURRENT,
+ liqThreshold: EngineFlags.KEEP_CURRENT,
+ liqBonus: EngineFlags.KEEP_CURRENT,
+ debtCeiling: EngineFlags.KEEP_CURRENT,
+ liqProtocolFee: EngineFlags.KEEP_CURRENT
+ });
+
+ vm.startPrank(riskCouncil);
+ steward.updateCollateralSide(collateralUpdates);
+
+ (, uint256 ltvAfter, uint256 ltAfter, uint256 lbAfter, , , , , , ) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveConfigurationData(AaveV3EthereumAssets.UNI_UNDERLYING);
+
+ uint256 debtCeilingAfter = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getDebtCeiling(
+ AaveV3EthereumAssets.UNI_UNDERLYING
+ ) / 100;
+
+ assertEq(ltvBefore, ltvAfter);
+ assertEq(ltBefore, ltAfter);
+ assertEq(lbBefore, lbAfter);
+ assertEq(debtCeilingBefore, debtCeilingAfter);
+ }
+
+ function test_updateCollaterals_sameUpdate() public virtual {
+ (, uint256 ltvBefore, uint256 ltBefore, uint256 lbBefore, , , , , , ) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveConfigurationData(AaveV3EthereumAssets.UNI_UNDERLYING);
+ lbBefore = lbBefore - 100_00;
+
+ uint256 debtCeilingBefore = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getDebtCeiling(
+ AaveV3EthereumAssets.UNI_UNDERLYING
+ ) / 100;
+
+ IEngine.CollateralUpdate[] memory collateralUpdates = new IEngine.CollateralUpdate[](1);
+ collateralUpdates[0] = IEngine.CollateralUpdate({
+ asset: AaveV3EthereumAssets.UNI_UNDERLYING,
+ ltv: ltvBefore,
+ liqThreshold: ltBefore,
+ liqBonus: lbBefore,
+ debtCeiling: debtCeilingBefore,
+ liqProtocolFee: EngineFlags.KEEP_CURRENT
+ });
+
+ vm.startPrank(riskCouncil);
+ steward.updateCollateralSide(collateralUpdates);
+
+ (, uint256 ltvAfter, uint256 ltAfter, uint256 lbAfter, , , , , , ) = AaveV3Ethereum
+ .AAVE_PROTOCOL_DATA_PROVIDER
+ .getReserveConfigurationData(AaveV3EthereumAssets.UNI_UNDERLYING);
+ lbAfter = lbAfter - 100_00;
+
+ uint256 debtCeilingAfter = AaveV3Ethereum.AAVE_PROTOCOL_DATA_PROVIDER.getDebtCeiling(
+ AaveV3EthereumAssets.UNI_UNDERLYING
+ ) / 100;
+
+ assertEq(ltvBefore, ltvAfter);
+ assertEq(ltBefore, ltAfter);
+ assertEq(lbBefore, lbAfter);
+ assertEq(debtCeilingBefore, debtCeilingAfter);
+ }
+
+ /* ----------------------------- MISC ----------------------------- */
+
function test_invalidCaller(address caller) public {
vm.assume(caller != riskCouncil);
@@ -709,8 +916,6 @@ contract RiskSteward_Test is Test {
vm.stopPrank();
}
- /* ----------------------------- MISC ----------------------------- */
-
function test_assetRestricted() public {
vm.expectEmit();
emit AddressRestricted(AaveV3EthereumAssets.GHO_UNDERLYING, true);
diff --git a/tests/RiskStewardCapo.t.sol b/tests/RiskStewardCapo.t.sol
index 310bb93..a5874b1 100644
--- a/tests/RiskStewardCapo.t.sol
+++ b/tests/RiskStewardCapo.t.sol
@@ -5,9 +5,9 @@ import 'forge-std/Test.sol';
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
import {RiskSteward, IRiskSteward, IEngine, EngineFlags} from 'src/contracts/RiskSteward.sol';
-import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/AaveV3ConfigEngine.sol';
+import {IAaveV3ConfigEngine as IEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {GovV3Helpers} from 'aave-helpers/src/GovV3Helpers.sol';
-import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
+import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {ConfigEngineDeployer} from './utils/ConfigEngineDeployer.sol';
import {IPriceCapAdapter} from 'aave-capo/interfaces/IPriceCapAdapter.sol';
import {IPriceCapAdapterStable, IChainlinkAggregator} from 'aave-capo/interfaces/IPriceCapAdapterStable.sol';
@@ -26,7 +26,7 @@ contract RiskSteward_Capo_Test is Test {
event AddressRestricted(address indexed contractAddress, bool indexed isRestricted);
function setUp() public {
- vm.createSelectFork(vm.rpcUrl('mainnet'), 20439517);
+ vm.createSelectFork(vm.rpcUrl('mainnet'), 20934847);
IRiskSteward.RiskParamConfig memory defaultRiskParamConfig = IRiskSteward.RiskParamConfig({
minDelay: 5 days,
@@ -167,6 +167,15 @@ contract RiskSteward_Capo_Test is Test {
vm.startPrank(riskCouncil);
steward.updateLstPriceCaps(priceCapUpdates);
+ priceCapUpdates[0] = IRiskSteward.PriceCapLstUpdate({
+ oracle: AaveV3EthereumAssets.wstETH_ORACLE,
+ priceCapUpdateParams: IPriceCapAdapter.PriceCapUpdateParams({
+ snapshotTimestamp: uint48(block.timestamp - 1 * delay),
+ snapshotRatio: (currentRatio - 1),
+ maxYearlyRatioGrowthPercent: ((maxYearlyGrowthPercentBefore)).toUint16()
+ })
+ });
+
// expect revert as minimum time has not passed for next update
vm.expectRevert(IRiskSteward.DebounceNotRespected.selector);
steward.updateLstPriceCaps(priceCapUpdates);
@@ -321,6 +330,44 @@ contract RiskSteward_Capo_Test is Test {
steward.updateLstPriceCaps(priceCapUpdates);
}
+ function test_updateLstPriceCap_noSameUpdate() public {
+ uint256 maxYearlyGrowthPercentBefore = IPriceCapAdapter(AaveV3EthereumAssets.wstETH_ORACLE)
+ .getMaxYearlyGrowthRatePercent();
+ uint256 snapshotTsBefore = IPriceCapAdapter(AaveV3EthereumAssets.wstETH_ORACLE)
+ .getSnapshotTimestamp();
+
+ vm.startPrank(riskCouncil);
+ IRiskSteward.PriceCapLstUpdate[] memory priceCapUpdates = new IRiskSteward.PriceCapLstUpdate[](
+ 1
+ );
+ priceCapUpdates[0] = IRiskSteward.PriceCapLstUpdate({
+ oracle: AaveV3EthereumAssets.wstETH_ORACLE,
+ priceCapUpdateParams: IPriceCapAdapter.PriceCapUpdateParams({
+ snapshotTimestamp: uint48(snapshotTsBefore),
+ snapshotRatio: currentRatio,
+ maxYearlyRatioGrowthPercent: maxYearlyGrowthPercentBefore.toUint16() + 1
+ })
+ });
+
+ // if same snapshot timestamp is used reverts
+ vm.expectRevert(
+ abi.encodeWithSelector(IPriceCapAdapter.InvalidRatioTimestamp.selector, snapshotTsBefore)
+ );
+ steward.updateLstPriceCaps(priceCapUpdates);
+
+ priceCapUpdates[0] = IRiskSteward.PriceCapLstUpdate({
+ oracle: AaveV3EthereumAssets.wstETH_ORACLE,
+ priceCapUpdateParams: IPriceCapAdapter.PriceCapUpdateParams({
+ snapshotTimestamp: uint48(snapshotTsBefore) + 1,
+ snapshotRatio: currentRatio - 1,
+ maxYearlyRatioGrowthPercent: maxYearlyGrowthPercentBefore.toUint16()
+ })
+ });
+
+ // if same maxYearlyRatioGrowthPercent the steward should not revert
+ steward.updateLstPriceCaps(priceCapUpdates);
+ }
+
/* ----------------------------- Stable Price Cap Tests ----------------------------- */
function test_updateStablePriceCap() public {
@@ -386,6 +433,11 @@ contract RiskSteward_Capo_Test is Test {
vm.startPrank(riskCouncil);
steward.updateStablePriceCaps(priceCapUpdates);
+ priceCapUpdates[0] = IRiskSteward.PriceCapStableUpdate({
+ oracle: AaveV3EthereumAssets.USDT_ORACLE,
+ priceCap: ((priceCapBefore * 105) / 100)
+ });
+
// expect revert as minimum time has not passed for next update
vm.expectRevert(IRiskSteward.DebounceNotRespected.selector);
steward.updateStablePriceCaps(priceCapUpdates);
@@ -416,10 +468,6 @@ contract RiskSteward_Capo_Test is Test {
}
function test_updateStablePriceCap_keepCurrent_revert() public {
- uint256 priceCapBefore = IPriceCapAdapterStable(AaveV3EthereumAssets.USDT_ORACLE)
- .getPriceCap()
- .toUint256();
-
IRiskSteward.PriceCapStableUpdate[]
memory priceCapUpdates = new IRiskSteward.PriceCapStableUpdate[](1);
@@ -480,4 +528,27 @@ contract RiskSteward_Capo_Test is Test {
vm.stopPrank();
}
+
+ function test_updateStablePriceCap_sameUpdates() public {
+ uint256 priceCapBefore = IPriceCapAdapterStable(AaveV3EthereumAssets.USDT_ORACLE)
+ .getPriceCap()
+ .toUint256();
+
+ IRiskSteward.PriceCapStableUpdate[]
+ memory priceCapUpdates = new IRiskSteward.PriceCapStableUpdate[](1);
+
+ priceCapUpdates[0] = IRiskSteward.PriceCapStableUpdate({
+ oracle: AaveV3EthereumAssets.USDT_ORACLE,
+ priceCap: priceCapBefore
+ });
+
+ vm.startPrank(riskCouncil);
+ steward.updateStablePriceCaps(priceCapUpdates);
+
+ uint256 priceCapAfter = IPriceCapAdapterStable(AaveV3EthereumAssets.USDT_ORACLE)
+ .getPriceCap()
+ .toUint256();
+
+ assertEq(priceCapBefore, priceCapAfter);
+ }
}
diff --git a/tests/utils/ConfigEngineDeployer.sol b/tests/utils/ConfigEngineDeployer.sol
index f2b7856..50b8b8e 100644
--- a/tests/utils/ConfigEngineDeployer.sol
+++ b/tests/utils/ConfigEngineDeployer.sol
@@ -2,19 +2,19 @@
pragma solidity ^0.8.0;
import {Vm} from 'forge-std/Vm.sol';
-import {Create2Utils} from 'aave-v3-origin/deployments/contracts/utilities/Create2Utils.sol';
-import {AaveV3ConfigEngine, IAaveV3ConfigEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/AaveV3ConfigEngine.sol';
-import {IPoolAddressesProvider} from 'aave-v3-origin/core/contracts/interfaces/IPoolAddressesProvider.sol';
-import {IPool} from 'aave-v3-origin/core/contracts/interfaces/IPool.sol';
-import {IPoolConfigurator} from 'aave-v3-origin/core/contracts/interfaces/IPoolConfigurator.sol';
-import {IAaveOracle} from 'aave-v3-origin/core/contracts/interfaces/IAaveOracle.sol';
-import {CapsEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/libraries/CapsEngine.sol';
-import {BorrowEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/libraries/BorrowEngine.sol';
-import {CollateralEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/libraries/CollateralEngine.sol';
-import {RateEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/libraries/RateEngine.sol';
-import {PriceFeedEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/libraries/PriceFeedEngine.sol';
-import {EModeEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/libraries/EModeEngine.sol';
-import {ListingEngine} from 'aave-v3-origin/periphery/contracts/v3-config-engine/libraries/ListingEngine.sol';
+import {Create2Utils} from 'aave-v3-origin/src/deployments/contracts/utilities/Create2Utils.sol';
+import {AaveV3ConfigEngine, IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/AaveV3ConfigEngine.sol';
+import {IPoolAddressesProvider} from 'aave-v3-origin/src/contracts/interfaces/IPoolAddressesProvider.sol';
+import {IPool} from 'aave-v3-origin/src/contracts/interfaces/IPool.sol';
+import {IPoolConfigurator} from 'aave-v3-origin/src/contracts/interfaces/IPoolConfigurator.sol';
+import {IAaveOracle} from 'aave-v3-origin/src/contracts/interfaces/IAaveOracle.sol';
+import {CapsEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/libraries/CapsEngine.sol';
+import {BorrowEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/libraries/BorrowEngine.sol';
+import {CollateralEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/libraries/CollateralEngine.sol';
+import {RateEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/libraries/RateEngine.sol';
+import {PriceFeedEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/libraries/PriceFeedEngine.sol';
+import {EModeEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/libraries/EModeEngine.sol';
+import {ListingEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/libraries/ListingEngine.sol';
import {AaveV3Ethereum} from 'aave-address-book/AaveV3Ethereum.sol';
library ConfigEngineDeployer {
@@ -45,7 +45,6 @@ library ConfigEngineDeployer {
new AaveV3ConfigEngine(
AaveV3Ethereum.DEFAULT_A_TOKEN_IMPL_REV_1,
AaveV3Ethereum.DEFAULT_VARIABLE_DEBT_TOKEN_IMPL_REV_1,
- AaveV3Ethereum.DEFAULT_STABLE_DEBT_TOKEN_IMPL_REV_1,
engineConstants,
engineLibraries
)
diff --git a/yarn.lock b/yarn.lock
index 89607bf..1eaaeae 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -48,10 +48,10 @@
resolved "https://registry.npmjs.org/@bgd-labs/aave-address-book/-/aave-address-book-3.1.0.tgz"
integrity sha512-1wy6m5asQhDG13xhm+ZAhwsTy1CBhow0/kyQIaVFcfar5PKJBnZr8ZVw7iA+AHD2LTRyoviVO5iSN24kWUHf2Q==
-"@bgd-labs/aave-address-book@^3.2.1":
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/@bgd-labs/aave-address-book/-/aave-address-book-3.2.1.tgz#9ecdb92303170e754e048d25f7d6cd27312c530b"
- integrity sha512-IlXHWYAhcgbiteTU50I6v8Awl7PBAHfXGz2IyjR5MPXy4xvW4UXYKbCRUtOCFxY/sdLtu/Gb+GgypyF76sd4Qg==
+"@bgd-labs/aave-address-book@^4.3.1":
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/@bgd-labs/aave-address-book/-/aave-address-book-4.3.1.tgz#c77943b980ba4636989ad72df65ba7d41fb5fd9c"
+ integrity sha512-Rg5ak/OuXgT6Q0q+CcP4GvutsTVaR6HmSsyHogkjwPgrgApg4d8GxtKHqBj9oXbNHnW5/N7piEGZc8Jd5z7G/Q==
"@bgd-labs/aave-cli@^0.16.2":
version "0.16.2"