F-2025-0017·missing-ltv-check

Silo and Dolomite lack LTV limit, increasing liquidation risk

Acknowledgedvaultetfstrategyd2-contracts
TL;DR

Aave_Module::aave_borrow enforces a MAX_LTV_FACTOR check on every borrow, but the equivalent Silo and Dolomite borrow functions do not enforce any LTV limit. A reckless or compromised trader can open higher-LTV positions on those venues than the strategy's risk model allows.

Severity
LOW
Impact
MEDIUM
Likelihood
LOW
Method
MManual review
CAT.
Complexity
LOW
Exploitability
LOW
02Section · Description

Description

When borrowing from Aave, there is a check in Aave_Module::aave_borrow to ensure that the loan-to-value (LTV) ratio remains below MAX_LTV_FACTOR (80%):

solidity
pool.borrow(asset, amount, interestRateMode, referralCode, onBehalfOf);
(uint256 totalCollateralBase, uint256 totalDebtBase, , , uint256 ltv, ) =
pool.getUserAccountData(onBehalfOf);
uint256 maxDebtBase = (totalCollateralBase * ltv * MAX_LTV_FACTOR) / (BASIS_FACTOR * MANTISSA_FACTOR);
require(totalDebtBase <= maxDebtBase, "borrow amount exceeds max LTV");

However, this LTV check is missing from Silo_Module::silo_borrow and Dolomite_Module::dolomite_openBorrowPosition / dolomite_transferBetweenAccounts, which are also lending protocols. As a result, positions with a higher LTV than 80% can be opened on these platforms.

03Section · Impact

Impact

Since Silo and Dolomite do not enforce the same 80% LTV limit, higher-risk positions can be entered. This increases the likelihood of unexpected liquidations, exposing users to unnecessary financial risk.

04Section · Recommendation

Recommendation

Consider implementing the same LTV checks for Silo and Dolomite, reading each venue's getUserAccountData (or its protocol-specific equivalent) and applying the same MAX_LTV_FACTOR ceiling.

05Section · Resolution

Resolution

D2: Ignored "Silo and Dolomite lack LTV limit increasing liquidation risk", will assume the trader is not reckless.

Cyfrin: Acknowledged.

F-2025-0017