11// SPDX-License-Identifier: BUSL-1.1
22pragma solidity ^ 0.8.0 ;
33
4+ import {console2 as console} from 'forge-std/console2.sol ' ;
5+
46import {Test, Vm} from 'forge-std/Test.sol ' ;
7+ import {IAaveOracle} from '../../src/contracts/interfaces/IAaveOracle.sol ' ;
8+ import {IPool} from '../../src/contracts/interfaces/IPool.sol ' ;
9+ import {AggregatorInterface} from '../../src/contracts/dependencies/chainlink/AggregatorInterface.sol ' ;
10+
511import {AaveV3HorizonEthereum} from './utils/AaveV3HorizonEthereum.sol ' ;
612
713/// forge-config: default.evm_version = "cancun"
814contract OracleDynamicBoundsTest is Test {
9- function setUp () public {
10- vm.createSelectFork ('mainnet ' , 23469081 );
11- }
15+ address constant USTB_NEW_AGGREGATOR = 0x267D0DD05fbc989565C521e0B8882f61027FF32A ;
16+ address constant USCC_NEW_AGGREGATOR = 0x2d7Cd12f24bD28684847bF3e4317899a4Db53c58 ;
17+ address constant USYC_NEW_AGGREGATOR = 0x3C405e1FE8a6BE5d9b714B8C88Ad913F236B1639 ;
18+ address constant JTRSY_NEW_AGGREGATOR = 0xcf8683fFdFC4b871DF35D05bc763F239612e7272 ;
19+ address constant JAAA_NEW_AGGREGATOR = 0x3a8E8491236368a582b651786bEdA49BD5c3BA7B ;
20+ address constant VBILL_NEW_AGGREGATOR = 0x04d81C346252E31Ee888393AF6E2037a9a4d70Af ;
21+
22+ // read admin addresses found on-chain
23+ address constant USTB_READ_ADMIN = 0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf ;
24+ address constant USCC_READ_ADMIN = 0x69D55D504BC9556E377b340D19818E736bbB318b ;
25+ address constant USYC_READ_ADMIN = 0x69D55D504BC9556E377b340D19818E736bbB318b ;
26+ address constant JTRSY_READ_ADMIN = 0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf ;
27+ address constant JAAA_READ_ADMIN = 0x69D55D504BC9556E377b340D19818E736bbB318b ;
28+ address constant VBILL_READ_ADMIN = 0x5ed77a9D9b7cc80E9d0D7711024AF38C2643C1c4 ;
29+
1230 struct ExpectedParams {
1331 uint64 maxExpectedApy;
1432 uint32 upperBoundTolerance;
@@ -20,6 +38,56 @@ contract OracleDynamicBoundsTest is Test {
2038 bool isActionTakingEnabled;
2139 }
2240
41+ struct NewAggregator {
42+ address aggregator;
43+ address readAdmin;
44+ }
45+
46+ mapping (address => ExpectedParams) internal expectedParams; // asset => expected params
47+ mapping (address => NewAggregator) internal newAggregators; // asset => new aggregator address
48+
49+ IAaveOracle internal oracle;
50+ function setUp () public {
51+ vm.createSelectFork ('mainnet ' , 23469081 );
52+ _initEnvironment ();
53+ }
54+
55+ function _initEnvironment () internal {
56+ expectedParams[AaveV3HorizonEthereum.USTB_ADDRESS] = USTB_EXPECTED_PARAMS;
57+ expectedParams[AaveV3HorizonEthereum.USCC_ADDRESS] = USCC_EXPECTED_PARAMS;
58+ expectedParams[AaveV3HorizonEthereum.USYC_ADDRESS] = USYC_EXPECTED_PARAMS;
59+ expectedParams[AaveV3HorizonEthereum.JTRSY_ADDRESS] = JTRSY_EXPECTED_PARAMS;
60+ expectedParams[AaveV3HorizonEthereum.JAAA_ADDRESS] = JAAA_EXPECTED_PARAMS;
61+ expectedParams[AaveV3HorizonEthereum.VBILL_ADDRESS] = VBILL_EXPECTED_PARAMS;
62+
63+ newAggregators[AaveV3HorizonEthereum.USTB_ADDRESS] = NewAggregator ({
64+ aggregator: USTB_NEW_AGGREGATOR,
65+ readAdmin: USTB_READ_ADMIN
66+ });
67+ newAggregators[AaveV3HorizonEthereum.USCC_ADDRESS] = NewAggregator ({
68+ aggregator: USCC_NEW_AGGREGATOR,
69+ readAdmin: USCC_READ_ADMIN
70+ });
71+ newAggregators[AaveV3HorizonEthereum.USYC_ADDRESS] = NewAggregator ({
72+ aggregator: USYC_NEW_AGGREGATOR,
73+ readAdmin: USYC_READ_ADMIN
74+ });
75+ newAggregators[AaveV3HorizonEthereum.JTRSY_ADDRESS] = NewAggregator ({
76+ aggregator: JTRSY_NEW_AGGREGATOR,
77+ readAdmin: JTRSY_READ_ADMIN
78+ });
79+ newAggregators[AaveV3HorizonEthereum.JAAA_ADDRESS] = NewAggregator ({
80+ aggregator: JAAA_NEW_AGGREGATOR,
81+ readAdmin: JAAA_READ_ADMIN
82+ });
83+ newAggregators[AaveV3HorizonEthereum.VBILL_ADDRESS] = NewAggregator ({
84+ aggregator: VBILL_NEW_AGGREGATOR,
85+ readAdmin: VBILL_READ_ADMIN
86+ });
87+
88+ oracle = IAaveOracle (IPool (AaveV3HorizonEthereum.POOL).ADDRESSES_PROVIDER ().getPriceOracle ());
89+ }
90+
2391 ExpectedParams internal USTB_EXPECTED_PARAMS =
2492 ExpectedParams ({
2593 maxExpectedApy: 415 ,
@@ -103,67 +171,44 @@ contract OracleDynamicBoundsTest is Test {
103171 assertEq (updater, AaveV3HorizonEthereum.HORIZON_OPS, 'updater ' );
104172 }
105173
106- function test_ustb_oracle () external {
107- test_horizon_adapter (
108- AaveV3HorizonEthereum.USTB_ADDRESS,
109- AaveV3HorizonEthereum.USTB_PRICE_FEED_ADAPTER,
110- true
111- );
112- test_registry_params (AaveV3HorizonEthereum.USTB_ADDRESS, USTB_EXPECTED_PARAMS);
113- test_lookback_data (AaveV3HorizonEthereum.USTB_ADDRESS);
174+ function test_ustb () external {
175+ address oracleSource = oracle.getSourceOfAsset (AaveV3HorizonEthereum.USTB_ADDRESS);
176+ test_asset (AaveV3HorizonEthereum.USTB_ADDRESS, oracleSource, true );
114177 }
115178
116- function test_uscc_oracle () external {
117- test_horizon_adapter (
118- AaveV3HorizonEthereum.USCC_ADDRESS,
119- AaveV3HorizonEthereum.USCC_PRICE_FEED_ADAPTER,
120- true
121- );
122- test_registry_params (AaveV3HorizonEthereum.USCC_ADDRESS, USCC_EXPECTED_PARAMS);
123- test_lookback_data (AaveV3HorizonEthereum.USCC_ADDRESS);
179+ function test_uscc () external {
180+ address oracleSource = oracle.getSourceOfAsset (AaveV3HorizonEthereum.USCC_ADDRESS);
181+ test_asset (AaveV3HorizonEthereum.USCC_ADDRESS, oracleSource, true );
124182 }
125183
126- function test_usyc_oracle () external {
127- test_horizon_adapter (
128- AaveV3HorizonEthereum.USYC_ADDRESS,
129- AaveV3HorizonEthereum.USYC_PRICE_FEED,
130- false
131- );
132- test_registry_params (AaveV3HorizonEthereum.USYC_ADDRESS, USYC_EXPECTED_PARAMS);
133- test_lookback_data (AaveV3HorizonEthereum.USYC_ADDRESS);
184+ function test_usyc () external {
185+ address oracleSource = oracle.getSourceOfAsset (AaveV3HorizonEthereum.USYC_ADDRESS);
186+ test_asset (AaveV3HorizonEthereum.USYC_ADDRESS, oracleSource, false );
134187 }
135188
136- function test_jtrsy_oracle () external {
137- test_horizon_adapter (
138- AaveV3HorizonEthereum.JTRSY_ADDRESS,
139- AaveV3HorizonEthereum.JTRSY_PRICE_FEED_ADAPTER,
140- true
141- );
142- test_registry_params (AaveV3HorizonEthereum.JTRSY_ADDRESS, JTRSY_EXPECTED_PARAMS);
143- test_lookback_data (AaveV3HorizonEthereum.JTRSY_ADDRESS);
189+ function test_jtrsy () external {
190+ address oracleSource = oracle.getSourceOfAsset (AaveV3HorizonEthereum.JTRSY_ADDRESS);
191+ test_asset (AaveV3HorizonEthereum.JTRSY_ADDRESS, oracleSource, true );
144192 }
145193
146- function test_jaaa_oracle () external {
147- test_horizon_adapter (
148- AaveV3HorizonEthereum.JAAA_ADDRESS,
149- AaveV3HorizonEthereum.JAAA_PRICE_FEED_ADAPTER,
150- true
151- );
152- test_registry_params (AaveV3HorizonEthereum.JAAA_ADDRESS, JAAA_EXPECTED_PARAMS);
153- test_lookback_data (AaveV3HorizonEthereum.JAAA_ADDRESS);
194+ function test_jaaa () external {
195+ address oracleSource = oracle.getSourceOfAsset (AaveV3HorizonEthereum.JAAA_ADDRESS);
196+ test_asset (AaveV3HorizonEthereum.JAAA_ADDRESS, oracleSource, true );
154197 }
155198
156199 function test_vbill () external {
157- test_horizon_adapter (
158- AaveV3HorizonEthereum.VBILL_ADDRESS,
159- AaveV3HorizonEthereum.VBILL_PRICE_FEED,
160- false
161- );
162- test_registry_params (AaveV3HorizonEthereum.VBILL_ADDRESS, VBILL_EXPECTED_PARAMS);
163- test_lookback_data (AaveV3HorizonEthereum.VBILL_ADDRESS);
200+ // VBILL not deployed yet, get price feed directly from lib
201+ test_asset (AaveV3HorizonEthereum.VBILL_ADDRESS, AaveV3HorizonEthereum.VBILL_PRICE_FEED, false );
164202 }
165203
166- function test_registry_params (address asset , ExpectedParams memory expectedParams ) internal {
204+ function test_asset (address asset , address oracleSource , bool isAdapter ) internal {
205+ test_horizon_adapter (asset, oracleSource, isAdapter);
206+ test_registry_params (asset);
207+ test_lookback_data (asset);
208+ test_new_aggregator (asset);
209+ }
210+
211+ function test_registry_params (address asset ) internal {
167212 bool success;
168213 bytes memory data;
169214
@@ -179,6 +224,8 @@ contract OracleDynamicBoundsTest is Test {
179224 );
180225 require (success, 'Failed to call getParametersForAsset() ' );
181226
227+ ExpectedParams memory expectedParam = expectedParams[asset];
228+
182229 (
183230 uint64 maxExpectedApy ,
184231 uint32 upperBoundTolerance ,
@@ -190,36 +237,31 @@ contract OracleDynamicBoundsTest is Test {
190237 bool isActionTakingEnabled
191238 ) = abi.decode (data, (uint64 , uint32 , uint32 , uint32 , uint80 , bool , bool , bool ));
192239
193- assertEq (maxExpectedApy, expectedParams .maxExpectedApy, 'maxExpectedApy ' );
194- assertEq (upperBoundTolerance, expectedParams .upperBoundTolerance, 'upperBoundTolerance ' );
195- assertEq (lowerBoundTolerance, expectedParams .lowerBoundTolerance, 'lowerBoundTolerance ' );
196- assertEq (maxDiscount, expectedParams .maxDiscount, 'maxDiscount ' );
197- assertEq (lookbackWindowSize, expectedParams .lookbackWindowSize, 'lookbackWindowSize ' );
198- assertEq (isUpperBoundEnabled, expectedParams .isUpperBoundEnabled, 'isUpperBoundEnabled ' );
199- assertEq (isLowerBoundEnabled, expectedParams .isLowerBoundEnabled, 'isLowerBoundEnabled ' );
200- assertEq (isActionTakingEnabled, expectedParams .isActionTakingEnabled, 'isActionTakingEnabled ' );
240+ assertEq (maxExpectedApy, expectedParam .maxExpectedApy, 'maxExpectedApy ' );
241+ assertEq (upperBoundTolerance, expectedParam .upperBoundTolerance, 'upperBoundTolerance ' );
242+ assertEq (lowerBoundTolerance, expectedParam .lowerBoundTolerance, 'lowerBoundTolerance ' );
243+ assertEq (maxDiscount, expectedParam .maxDiscount, 'maxDiscount ' );
244+ assertEq (lookbackWindowSize, expectedParam .lookbackWindowSize, 'lookbackWindowSize ' );
245+ assertEq (isUpperBoundEnabled, expectedParam .isUpperBoundEnabled, 'isUpperBoundEnabled ' );
246+ assertEq (isLowerBoundEnabled, expectedParam .isLowerBoundEnabled, 'isLowerBoundEnabled ' );
247+ assertEq (isActionTakingEnabled, expectedParam .isActionTakingEnabled, 'isActionTakingEnabled ' );
201248 }
202249
203- function test_horizon_adapter (address asset , address source , bool isAdapter ) internal {
250+ /// test that the oracle source from horizon adapter/oracle source is the same as the oracle address from the param registry
251+ function test_horizon_adapter (address asset , address oracleSource , bool isAdapter ) internal {
204252 bool success;
205253 bytes memory data;
206254 if (isAdapter) {
207- // oracle source from horizon adapter
208- (success, data) = source .call (abi.encodeWithSignature ('source() ' ));
255+ // if adapter, get oracle source from horizon adapter source
256+ (success, data) = oracleSource .call (abi.encodeWithSignature ('source() ' ));
209257 require (success, 'Failed to call source() ' );
210- source = abi.decode (data, (address ));
258+ oracleSource = abi.decode (data, (address ));
211259 }
212-
213- // oracle address from param registry
214- (success, data) = AaveV3HorizonEthereum.PARAM_REGISTRY.call (
215- abi.encodeWithSignature ('getOracle(address) ' , asset)
216- );
217- require (success, 'Failed to call getOracle() ' );
218- address oracle = abi.decode (data, (address ));
219-
220- assertEq (source, oracle, 'source ' );
260+ address oracle = _getParamRegistryOracle (asset);
261+ assertEq (oracleSource, oracle, 'source ' );
221262 }
222263
264+ // read look back data from param registry
223265 function test_lookback_data (address asset ) internal {
224266 bool success;
225267 bytes memory data;
@@ -229,6 +271,7 @@ contract OracleDynamicBoundsTest is Test {
229271 );
230272 require (success, 'Failed to call getLookbackData() ' );
231273
274+ // reads from old aggregator data
232275 (
233276 uint80 roundId ,
234277 int256 answer ,
@@ -239,8 +282,56 @@ contract OracleDynamicBoundsTest is Test {
239282
240283 assertGt (roundId, 0 , 'roundId ' );
241284 assertGt (answer, 0 , 'answer ' );
242- assertApproxEqRel (startedAt, vm.getBlockTimestamp () - 4 * 1 days, 1e14 , 'startedAt ' );
243- assertApproxEqRel (updatedAt, vm.getBlockTimestamp () - 4 * 1 days, 1e14 , 'updatedAt ' );
285+ assertApproxEqRel (
286+ startedAt,
287+ vm.getBlockTimestamp () - expectedParams[asset].lookbackWindowSize * 1 days, // within expected lookback window
288+ 1e15 ,
289+ 'startedAt '
290+ );
291+ assertApproxEqRel (
292+ updatedAt,
293+ vm.getBlockTimestamp () - expectedParams[asset].lookbackWindowSize * 1 days, // within expected lookback window
294+ 1e15 ,
295+ 'updatedAt '
296+ );
244297 assertGt (answeredInRound, 0 , 'answeredInRound ' );
245298 }
299+
300+ function test_new_aggregator (address asset ) internal {
301+ // new aggregator data
302+
303+ vm.prank (newAggregators[asset].readAdmin); // has access to price feed
304+ (
305+ uint80 roundId ,
306+ int256 answer ,
307+ uint256 startedAt ,
308+ uint256 updatedAt ,
309+ uint80 answeredInRound
310+ ) = AggregatorInterface (newAggregators[asset].aggregator).latestRoundData ();
311+
312+ assertGt (roundId, expectedParams[asset].lookbackWindowSize, 'roundId ' );
313+ assertGt (answer, 0 , 'answer ' );
314+ assertApproxEqRel (
315+ startedAt,
316+ vm.getBlockTimestamp () - expectedParams[asset].lookbackWindowSize * 1 days, // within expected lookback window
317+ 1e15 ,
318+ 'startedAt '
319+ );
320+ assertApproxEqRel (
321+ updatedAt,
322+ vm.getBlockTimestamp () - expectedParams[asset].lookbackWindowSize * 1 days, // within expected lookback window
323+ 1e15 ,
324+ 'updatedAt '
325+ );
326+ assertGt (answeredInRound, expectedParams[asset].lookbackWindowSize, 'answeredInRound ' );
327+ }
328+
329+ // read oracle address from param registry
330+ function _getParamRegistryOracle (address asset ) internal returns (address ) {
331+ (bool success , bytes memory data ) = AaveV3HorizonEthereum.PARAM_REGISTRY.call (
332+ abi.encodeWithSignature ('getOracle(address) ' , asset)
333+ );
334+ require (success, 'Failed to call getOracle() ' );
335+ return abi.decode (data, (address ));
336+ }
246337}
0 commit comments