Skip to content

Commit a99d11a

Browse files
committed
fixed susde-related audit findings
1 parent 25569a9 commit a99d11a

File tree

3 files changed

+184
-133
lines changed

3 files changed

+184
-133
lines changed

aave-core/aave-oracle/sources/oracle.move

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,14 @@ module aave_oracle::oracle {
205205

206206
match(cap_info.type) {
207207
AdapterType::SUSDE => {
208-
let (assets_prices, _) = get_asset_prices_and_timestamps_internal(
209-
vector[asset, *option::borrow(&cap_info.mapped_asset_ratio_multiplier)]
210-
);
208+
// Get sUSDe/USDe exchange rate (already in 18 decimals) WITHOUT capping —
209+
// this is the adapter’s internal ratio, not a capped quote.
210+
let (underlying_asset_price, _) = get_asset_price_internal(asset);
211+
212+
// Get the mapped base asset (e.g., USDT) price THROUGH the capped path.
213+
let mapped = *option::borrow(&cap_info.mapped_asset_ratio_multiplier);
214+
let (asset_base_ratio, _) = get_asset_price_and_timestamp(mapped);
211215

212-
// It means "underlying_asset_price" is the price of sUSDe/USDe exchange rate
213-
// expressed in 18 decimals already
214-
let underlying_asset_price = assets_prices[0]; // sUSDe/USDe exchange rate
215-
let asset_base_ratio = assets_prices[1]; // USDT price
216216
let (_, is_capped) = get_capped_susde_price(
217217
underlying_asset_price, asset_base_ratio, &cap_info
218218
);
@@ -304,13 +304,17 @@ module aave_oracle::oracle {
304304

305305
match(cap_info.type) {
306306
AdapterType::SUSDE => {
307-
let (assets_prices, assets_timestamps) = get_asset_prices_and_timestamps_internal(
308-
vector[asset, *option::borrow(&cap_info.mapped_asset_ratio_multiplier)]
307+
// sUSDe/USDe exchange rate (18 decimals), from the adapter’s raw (uncapped) internal path
308+
let (underlying_asset_price, underlying_asset_timestamp) = get_asset_price_internal(
309+
asset
310+
);
311+
312+
// Mapped base asset (e.g., USDT) fetched through the capped path
313+
let mapped = *option::borrow(&cap_info.mapped_asset_ratio_multiplier);
314+
let (asset_base_ratio, asset_base_timestamp) = get_asset_price_and_timestamp(
315+
mapped
309316
);
310-
let underlying_asset_price = assets_prices[0];
311-
let asset_base_ratio = assets_prices[1];
312-
let underlying_asset_timestamp = assets_timestamps[0];
313-
let asset_base_timestamp = assets_timestamps[1];
317+
314318
let (underlying_asset_capped_price, _) = get_capped_susde_price(
315319
underlying_asset_price, asset_base_ratio, &cap_info
316320
);
@@ -564,11 +568,32 @@ module aave_oracle::oracle {
564568
only_asset_listing_or_pool_admin(account);
565569
assert!(custom_price > 0, error_config::get_ezero_asset_custom_price());
566570
if (is_asset_price_capped(asset)) {
567-
let (capped_price, _) = get_asset_price_and_timestamp(asset);
568-
assert!(
569-
custom_price <= capped_price,
570-
error_config::get_ecustom_price_above_price_cap()
571-
);
571+
// Inspect the adapter type so we compare apples-to-apples.
572+
let pod = borrow_global<PriceOracleData>(oracle_address());
573+
if (smart_table::contains(&pod.capped_assets_data, asset)) {
574+
let cap_info = *smart_table::borrow(&pod.capped_assets_data, asset);
575+
576+
match(cap_info.type) {
577+
// For STABLE: `custom_price` is a USD-denominated price → compare to USD capped price.
578+
AdapterType::STABLE => {
579+
let (capped_price, _) = get_asset_price_and_timestamp(asset);
580+
assert!(
581+
custom_price <= capped_price,
582+
error_config::get_ecustom_price_above_price_cap()
583+
);
584+
},
585+
586+
// For SUSDE: `custom_price` is a *ratio* (sUSDe/USDe, 18 dp).
587+
// Compare directly to the max allowed ratio (unit-consistent), NOT to the USD price.
588+
AdapterType::SUSDE => {
589+
let max_ratio = get_max_allowed_susde_ratio(&cap_info);
590+
assert!(
591+
custom_price <= max_ratio,
592+
error_config::get_ecustom_price_above_price_cap()
593+
);
594+
}
595+
}
596+
}
572597
};
573598
update_asset_custom_price(asset, custom_price);
574599
}
@@ -694,6 +719,20 @@ module aave_oracle::oracle {
694719
(prices, timestamps)
695720
}
696721

722+
/// Max allowed sUSDe/USDe ratio given the snapshot and growth parameters.
723+
/// Assumes parameters were validated in `set_susde_price_adapter`.
724+
/// @param cap Capped asset data
725+
/// @return The max allowed sUSDe/USDe ratio in 18 decimals
726+
fun get_max_allowed_susde_ratio(cap: &CappedAssetData): u256 {
727+
let snapshot_ratio = *option::borrow(&cap.snapshot_ratio);
728+
let snapshot_ts = *option::borrow(&cap.snapshot_timestamp);
729+
let growth_per_s = *option::borrow(&cap.max_ratio_growth_per_second);
730+
let now = timestamp::now_seconds() as u256;
731+
732+
// Saturating behavior not needed because params are pre-validated against overflow
733+
snapshot_ratio + growth_per_s * (now - snapshot_ts)
734+
}
735+
697736
// Private helper functions
698737
/// @notice Applies stable price capping logic to a base price
699738
/// @param base_price The original asset price before capping

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
"md:fix": "pnpm md:lint --fix"
1111
},
1212
"devDependencies": {
13-
"@commitlint/config-conventional": "^19.8.0",
14-
"commitlint": "^19.8.0",
15-
"markdownlint": "^0.38.0",
13+
"@commitlint/config-conventional": "^20.0.0",
14+
"commitlint": "^20.1.0",
15+
"markdownlint": "^0.39.0",
1616
"markdownlint-cli": "^0.45.0",
1717
"prettier": "^3.5.3",
1818
"prettier-plugin-sh": "^0.18.0",

0 commit comments

Comments
 (0)