Skip to content

Commit 6e8fef4

Browse files
feat: pendle discount rate injector (aave-dao#47)
* feat: add discountRate injector * feat: EdgeRiskStewardDiscountRate * chore: add deploy script * test: discountRate injector * fix: discount rate in 1e18 format * fix: use structs for deploy inputs * chore: fix other deploy scripts too * Revert "chore: fix other deploy scripts too" This reverts commit 8905ed4. * Update tests/AaveStewardInjectorDiscountRate.t.sol Co-authored-by: Andrey <[email protected]> * chore: rename better --------- Co-authored-by: Andrey <[email protected]>
1 parent 1203f3a commit 6e8fef4

File tree

7 files changed

+600
-20
lines changed

7 files changed

+600
-20
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import 'solidity-utils/contracts/utils/ScriptUtils.sol';
5+
import {MiscEthereum} from 'aave-address-book/MiscEthereum.sol';
6+
import {AaveV3Ethereum, AaveV3EthereumAssets} from 'aave-address-book/AaveV3Ethereum.sol';
7+
import {GovernanceV3Ethereum} from 'aave-address-book/GovernanceV3Ethereum.sol';
8+
import {ICreate3Factory} from 'solidity-utils/contracts/create3/interfaces/ICreate3Factory.sol';
9+
import {EdgeRiskStewardDiscountRate, IRiskSteward} from '../../src/contracts/EdgeRiskStewardDiscountRate.sol';
10+
import {AaveStewardInjectorDiscountRate} from '../../src/contracts/AaveStewardInjectorDiscountRate.sol';
11+
12+
library DeployStewardContracts {
13+
struct DeployStewardInput {
14+
address pool;
15+
address configEngine;
16+
address riskCouncil;
17+
address owner;
18+
}
19+
20+
struct DeployInjectorInput {
21+
address create3Factory;
22+
bytes32 salt;
23+
address riskSteward;
24+
address aaveOracle;
25+
address edgeRiskOracle;
26+
address owner;
27+
address guardian;
28+
address[] whitelistedMarkets;
29+
}
30+
31+
function _deployRiskStewards(
32+
DeployStewardInput memory input
33+
) internal returns (address) {
34+
address riskSteward = address(
35+
new EdgeRiskStewardDiscountRate(input.pool, input.configEngine, input.riskCouncil, input.owner, _getRiskConfig())
36+
);
37+
return riskSteward;
38+
}
39+
40+
function _deployDiscountRateStewardInjector(
41+
DeployInjectorInput memory input
42+
) internal returns (address) {
43+
address stewardInjector = ICreate3Factory(input.create3Factory).create(
44+
input.salt,
45+
abi.encodePacked(
46+
type(AaveStewardInjectorDiscountRate).creationCode,
47+
abi.encode(input.aaveOracle, input.edgeRiskOracle, input.riskSteward, input.whitelistedMarkets, input.owner, input.guardian)
48+
)
49+
);
50+
return stewardInjector;
51+
}
52+
53+
function _getRiskConfig() internal pure returns (IRiskSteward.Config memory) {
54+
IRiskSteward.Config memory config;
55+
config.priceCapConfig.discountRatePendle = IRiskSteward.RiskParamConfig({
56+
minDelay: 2 days,
57+
maxPercentChange: 0.01e18 // 1%
58+
});
59+
60+
return config;
61+
}
62+
}
63+
64+
// make deploy-ledger contract=scripts/deploy/DeployDiscountRateInjector.s.sol:DeployEthereum chain=mainnet
65+
contract DeployEthereum is EthereumScript {
66+
address constant GUARDIAN = 0xff37939808EcF199A2D599ef91D699Fb13dab7F7;
67+
address constant EDGE_RISK_ORACLE = 0x7ABB46C690C52E919687D19ebF89C81A6136C1F2;
68+
69+
function run() external {
70+
vm.startBroadcast();
71+
bytes32 salt = 'DiscountRateStewardInjector';
72+
address predictedStewardsInjector = ICreate3Factory(MiscEthereum.CREATE_3_FACTORY)
73+
.predictAddress(msg.sender, salt);
74+
75+
address riskSteward = DeployStewardContracts._deployRiskStewards(
76+
DeployStewardContracts.DeployStewardInput({
77+
pool: address(AaveV3Ethereum.POOL),
78+
configEngine: AaveV3Ethereum.CONFIG_ENGINE,
79+
riskCouncil: predictedStewardsInjector,
80+
owner: GovernanceV3Ethereum.EXECUTOR_LVL_1
81+
})
82+
);
83+
84+
address[] memory whitelistedPendleAssets = new address[](3);
85+
whitelistedPendleAssets[0] = AaveV3EthereumAssets.PT_sUSDE_31JUL2025_UNDERLYING;
86+
whitelistedPendleAssets[1] = AaveV3EthereumAssets.PT_USDe_31JUL2025_UNDERLYING;
87+
whitelistedPendleAssets[2] = AaveV3EthereumAssets.PT_eUSDE_14AUG2025_UNDERLYING;
88+
89+
DeployStewardContracts._deployDiscountRateStewardInjector(
90+
DeployStewardContracts.DeployInjectorInput({
91+
create3Factory: MiscEthereum.CREATE_3_FACTORY,
92+
salt: salt,
93+
riskSteward: riskSteward,
94+
aaveOracle: address(AaveV3Ethereum.ORACLE),
95+
edgeRiskOracle: EDGE_RISK_ORACLE,
96+
owner: GovernanceV3Ethereum.EXECUTOR_LVL_1,
97+
guardian: GUARDIAN,
98+
whitelistedMarkets: whitelistedPendleAssets
99+
})
100+
);
101+
vm.stopBroadcast();
102+
}
103+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import {IRiskOracle} from './dependencies/IRiskOracle.sol';
5+
import {IRiskSteward} from '../interfaces/IRiskSteward.sol';
6+
import {AaveStewardInjectorBase} from './AaveStewardInjectorBase.sol';
7+
import {IAaveOracle} from 'aave-v3-origin/src/contracts/interfaces/IAaveOracle.sol';
8+
9+
/**
10+
* @title AaveStewardInjectorDiscountRate
11+
* @author BGD Labs
12+
* @notice Aave chainlink automation-keeper-compatible contract to perform pendle discountRate update injection
13+
* on risk steward using the edge risk oracle.
14+
*/
15+
contract AaveStewardInjectorDiscountRate is AaveStewardInjectorBase {
16+
IAaveOracle public immutable AAVE_ORACLE;
17+
18+
/**
19+
* @param aaveOracle address of the aave oracle of the instance.
20+
* @param riskOracle address of the edge risk oracle contract.
21+
* @param riskSteward address of the risk steward contract.
22+
* @param markets list of market addresses to allow.
23+
* @param owner address of the owner of the stewards injector.
24+
* @param guardian address of the guardian of the stewards injector.
25+
*/
26+
constructor(
27+
address aaveOracle,
28+
address riskOracle,
29+
address riskSteward,
30+
address[] memory markets,
31+
address owner,
32+
address guardian
33+
) AaveStewardInjectorBase(riskOracle, riskSteward, markets, owner, guardian) {
34+
AAVE_ORACLE = IAaveOracle(aaveOracle);
35+
}
36+
37+
/// @inheritdoc AaveStewardInjectorBase
38+
function getUpdateTypes() public pure override returns (string[] memory updateTypes) {
39+
updateTypes = new string[](1);
40+
updateTypes[0] = 'PendleDiscountRateUpdate_Core';
41+
}
42+
43+
/// @inheritdoc AaveStewardInjectorBase
44+
function _injectUpdate(IRiskOracle.RiskParameterUpdate memory riskParams) internal override {
45+
uint256 discountRate = abi.decode(
46+
abi.encodePacked(new bytes(32 - riskParams.newValue.length), riskParams.newValue),
47+
(uint256)
48+
);
49+
50+
IRiskSteward.DiscountRatePendleUpdate[] memory discountRateUpdate = new IRiskSteward.DiscountRatePendleUpdate[](1);
51+
discountRateUpdate[0] = IRiskSteward.DiscountRatePendleUpdate({
52+
oracle: AAVE_ORACLE.getSourceOfAsset(riskParams.market), // reserve address is encoded in the market address
53+
discountRate: discountRate
54+
});
55+
IRiskSteward(RISK_STEWARD).updatePendleDiscountRates(discountRateUpdate);
56+
}
57+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import './RiskSteward.sol';
5+
6+
/**
7+
* @title EdgeRiskStewardDiscountRate
8+
* @author BGD labs
9+
* @notice Contract to manage the PendleDiscountRate updates within configured bound on aave v3 pool.
10+
* To be triggered by the Aave Steward Injector Contract in a automated way via the Edge Risk Oracle.
11+
*/
12+
contract EdgeRiskStewardDiscountRate is RiskSteward {
13+
/**
14+
* @param pool the aave pool to be controlled by the steward
15+
* @param engine the config engine to be used by the steward
16+
* @param riskCouncil the safe address of the council being able to interact with the steward
17+
* @param owner the owner of the risk steward being able to set configs and mark items as restricted
18+
* @param riskConfig the risk configuration to setup for each individual risk param
19+
*/
20+
constructor(
21+
address pool,
22+
address engine,
23+
address riskCouncil,
24+
address owner,
25+
Config memory riskConfig
26+
) RiskSteward(pool, engine, riskCouncil, owner, riskConfig) {}
27+
28+
/// @inheritdoc IRiskSteward
29+
function updateRates(
30+
IEngine.RateStrategyUpdate[] calldata
31+
) external virtual override onlyRiskCouncil {
32+
revert UpdateNotAllowed();
33+
}
34+
35+
/// @inheritdoc IRiskSteward
36+
function updateCaps(IEngine.CapsUpdate[] calldata) external virtual override onlyRiskCouncil {
37+
revert UpdateNotAllowed();
38+
}
39+
40+
/// @inheritdoc IRiskSteward
41+
function updateCollateralSide(
42+
IEngine.CollateralUpdate[] calldata
43+
) external virtual override onlyRiskCouncil {
44+
revert UpdateNotAllowed();
45+
}
46+
47+
/// @inheritdoc IRiskSteward
48+
function updateEModeCategories(
49+
IEngine.EModeCategoryUpdate[] calldata
50+
) external virtual override onlyRiskCouncil {
51+
revert UpdateNotAllowed();
52+
}
53+
54+
/// @inheritdoc IRiskSteward
55+
function updateLstPriceCaps(
56+
PriceCapLstUpdate[] calldata
57+
) external virtual override onlyRiskCouncil {
58+
revert UpdateNotAllowed();
59+
}
60+
61+
/// @inheritdoc IRiskSteward
62+
function updateStablePriceCaps(
63+
PriceCapStableUpdate[] calldata
64+
) external virtual override onlyRiskCouncil {
65+
revert UpdateNotAllowed();
66+
}
67+
}

0 commit comments

Comments
 (0)