Skip to content

Commit 650d51e

Browse files
feat: stewards injector robot (#13)
* fix: no same updates or all keep current * fix: no same update for capo * feat: add edge risk steward * chore: fix lint * chore: add risk oracle contract * feat: add stewards injector and fix remappings * test: stewards injector * test: isUpdatedIdExecuted * test: expiredUpdate * test: some more injector tests * test: edge risk steward * refactor: rename method * feat: add writeup * test: add missing tests * fix: script for coverage * chore: deploy scripts * fix: deploy script * feat: simplify injector * fix: update contracts to v3.2 and adjust imports and remappings * chore: update readme * fix: certora remapping * chore: update readme from suggestion Co-authored-by: Ernesto Boado <[email protected]> * chore: update readme from suggestion Co-authored-by: Ernesto Boado <[email protected]> * chore: update readme from suggestion Co-authored-by: Ernesto Boado <[email protected]> * fix: remove revert on sameUpdate and all keepCurrent * chore: fix lint * chore: fix typo in writeup * fix: generator imports and update address book * fix: stewards base * chore: add risk oracle to deploy script --------- Co-authored-by: Ernesto Boado <[email protected]>
1 parent 3fd7b75 commit 650d51e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1544
-132
lines changed

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ update:; forge update
88
coverage :; forge coverage --report lcov && \
99
lcov --remove ./lcov.info -o ./lcov.info.p \
1010
'tests/*' \
11+
'src/contracts/dependencies/*' \
12+
'src/contracts/examples/*' \
13+
'src/contracts/updates/*' \
14+
'scripts/*' \
1115
&& genhtml ./lcov.info.p -o report --branch-coverage \
1216
&& coverage=$$(awk -F '[<>]' '/headerCovTableEntryHi/{print $3}' ./report/index.html | sed 's/[^0-9.]//g' | head -n 1); \
1317
wget -O ./report/coverage.svg "https://img.shields.io/badge/coverage-$${coverage}%25-brightgreen"
@@ -27,4 +31,4 @@ git-diff :
2731
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)
2832
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)
2933

30-
run-script:; FOUNDRY_PROFILE=${network} forge script ${contract_path} --rpc-url ${network} --sig "run(bool)" ${broadcast} -vv
34+
run-script:; FOUNDRY_PROFILE=${network} forge script ${contract} --rpc-url ${network} --sig "run(bool)" ${broadcast} -vv

README.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Expanding from the scope of CapsPlusRiskSteward, we now introduce the new RiskSt
88

99
## Specification
1010

11-
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.
11+
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.
1212

1313
_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._
1414

@@ -39,10 +39,10 @@ For each risk param, `minDelay` can be configured, which is the minimum amount o
3939

4040
#### Max Percent Change:
4141

42-
For each risk param, `maxPercentChange` which is the maximum percent change allowed (both upwards and downwards) for the risk param using the RiskStewards.
42+
For each risk param, `maxPercentChange` is the maximum percent change allowed (both upwards and downwards) for the risk param using the RiskStewards.
4343

44-
- Supply cap, Borrow cap and Debt ceiling: The `maxPercentChange` is relative and is denominated in BPS. (Ex. `50_00` for +-50% relative change).
45-
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.
44+
- Supply cap, Borrow cap, and Debt ceiling: The `maxPercentChange` is relative and is denominated in BPS. (Ex. `50_00` for +-50% relative change).
45+
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.
4646

4747
- LTV, LT, LB: The `maxPercentChange` is in absolute values and is also denominated in BPS. (Ex. `5_00` for +-5% change in LTV).
4848
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
6666

6767
<br>
6868

69+
## AGRS + Edge Infrastructure (Risk Oracles)
70+
71+
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`.
72+
73+
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.
74+
75+
The following is a simple diagram of how the system works as a whole:
76+
77+
<img src="./docs/agrs-edge.png" alt="AGRS and Risk Oracle Diagram Flow" width="100%" height="100%">
78+
79+
### AaveStewardsInjector
80+
81+
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.
82+
83+
The `AaveStewardsInjector` has the following major functions:
84+
85+
- `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.
86+
87+
- `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.
88+
89+
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.
90+
91+
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.
92+
6993
## Security
7094

7195
- Certora security review: [2024-07-10](./audits/10-07-2024_Certora_AaveV3-Risk-Steward.pdf)

certora/confs/rules.conf

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"files": [
33
"certora/munged/src/contracts/RiskSteward.sol",
4-
"lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/periphery/contracts/v3-config-engine/AaveV3ConfigEngine.sol"
4+
"lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/contracts/extensions/v3-config-engine/AaveV3ConfigEngine.sol"
55
],
66
"link": [
77
"RiskSteward:CONFIG_ENGINE=AaveV3ConfigEngine",
@@ -10,10 +10,9 @@
1010
"aave-helpers=lib/aave-helpers",
1111
"forge-std=lib/aave-helpers/lib/forge-std/src",
1212
"aave-address-book=lib/aave-helpers/lib/aave-address-book/src",
13-
"solidity-utils=lib/aave-helpers/lib/solidity-utils/src",
14-
"aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
15-
"aave-v3-core=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/core",
16-
"aave-v3-periphery=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/periphery",
13+
"solidity-utils=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/src",
14+
"lib/aave-helpers:aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
15+
"aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin",
1716
"aave-capo=lib/aave-capo/src",
1817
"lib/aave-capo:cl-synchronicity-price-adapter=lib/aave-capo/lib/cl-synchronicity-price-adapter/src"
1918
],

certora/confs/sanity.conf

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
"aave-helpers=lib/aave-helpers",
77
"forge-std=lib/aave-helpers/lib/forge-std/src",
88
"aave-address-book=lib/aave-helpers/lib/aave-address-book/src",
9-
"solidity-utils=lib/aave-helpers/lib/solidity-utils/src",
10-
"aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
11-
"aave-v3-core=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/core",
12-
"aave-v3-periphery=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src/periphery",
9+
"solidity-utils=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/lib/solidity-utils/src",
10+
"lib/aave-helpers:aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin/src",
11+
"aave-v3-origin=lib/aave-helpers/lib/aave-address-book/lib/aave-v3-origin",
1312
"aave-capo=lib/aave-capo/src",
1413
"lib/aave-capo:cl-synchronicity-price-adapter=lib/aave-capo/lib/cl-synchronicity-price-adapter/src"
1514
],

docs/agrs-edge.png

66.8 KB
Loading

foundry.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ src = 'src'
33
test = 'tests'
44
script = 'scripts'
55
out = 'out'
6-
solc = '0.8.19'
6+
solc = '0.8.20'
77
libs = ['lib']
88
remappings = [
99
]
@@ -16,10 +16,10 @@ src = 'zksync'
1616
test = 'zksync'
1717
script = 'scripts'
1818
libs = ['lib']
19-
solc = '0.8.19'
19+
solc = '0.8.20'
2020
fs_permissions = [{ access = "write", path = "./reports" }]
2121
ffi = true
22-
evm_version = 'paris'
22+
evm_version = 'shanghai'
2323

2424
[profile.zksync.zksync]
2525
compile = true

generator/features/__snapshots__/capUpdates.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pragma solidity ^0.8.0;
2828
2929
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
3030
import {RiskStewardsEthereum} from '../../../../scripts/networks/RiskStewardsEthereum.s.sol';
31-
import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
31+
import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
3232
3333
/**
3434
* @title test

generator/features/__snapshots__/collateralUpdates.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ pragma solidity ^0.8.0;
3939
4040
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
4141
import {RiskStewardsEthereum} from '../../../../scripts/networks/RiskStewardsEthereum.s.sol';
42-
import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
43-
import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
42+
import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
43+
import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
4444
4545
/**
4646
* @title test

generator/features/__snapshots__/rateUpdates.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ pragma solidity ^0.8.0;
7676
7777
import {AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
7878
import {RiskStewardsEthereum} from '../../../../scripts/networks/RiskStewardsEthereum.s.sol';
79-
import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';
80-
import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';
79+
import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';
80+
import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
8181
8282
/**
8383
* @title test

generator/utils/importsResolver.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ describe('prefixWithImports', () => {
1717

1818
it('should detect v3 Engine imports', () => {
1919
expect(prefixWithImports(`EngineFlags.KEEP_CURRENT`)).toContain(
20-
`import {EngineFlags} from 'aave-v3-periphery/contracts/v3-config-engine/EngineFlags.sol';`,
20+
`import {EngineFlags} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/EngineFlags.sol';`,
2121
);
2222

2323
expect(prefixWithImports('IAaveV3ConfigEngine.CapsUpdate')).toContain(
24-
`import {IAaveV3ConfigEngine} from 'aave-v3-periphery/contracts/v3-config-engine/IAaveV3ConfigEngine.sol';`,
24+
`import {IAaveV3ConfigEngine} from 'aave-v3-origin/src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';`,
2525
);
2626
});
2727

0 commit comments

Comments
 (0)