Stratax Contracts

First Flight #57
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: medium
Likelihood: medium

Incorrect use of liquidation threshold instead of LTV causes insufficient collateral withdrawal during position unwind

Author Revealed upon completion

Normal behavior:
When unwinding a leveraged position, the contract should withdraw sufficient collateral from Aave to cover the flash loan repayment after swapping. The withdrawal amount must be calculated using the asset's loan-to-value (LTV) ratio, which determines how much debt can be backed by a given collateral amount.

Problem:
The _executeUnwindOperation() function incorrectly uses the liquidation threshold (index 2) instead of the LTV ratio (index 1) from Aave's reserve configuration data when calculating collateral to withdraw. Since liquidation thresholds are typically higher than LTV ratios (e.g., 82.5% vs 80%), this causes the contract to withdraw less collateral than required, potentially failing to generate enough proceeds to repay the flash loan.

https://github.com/CodeHawks-Contests/2026-02-stratax-contracts/blob/f6525334ddeb7910733432a992daecb0a8041430/src/Stratax.sol#L380-420

function _executeUnwindOperation(address _asset, uint256 _amount, uint256 _premium, bytes calldata _params)
internal
returns (bool)
{
// Step 2: Calculate and withdraw only the collateral that backed the repaid debt
uint256 withdrawnAmount;
{
// Get LTV from Aave for the collateral token
@> (,, uint256 liqThreshold,,,,,,,) = // WRONG: Fetches liquidation threshold (index 2)
aaveDataProvider.getReserveConfigurationData(unwindParams.collateralToken);
// Get prices and decimals
uint256 debtTokenPrice = IStrataxOracle(strataxOracle).getPrice(_asset);
uint256 collateralTokenPrice = IStrataxOracle(strataxOracle).getPrice(unwindParams.collateralToken);
require(debtTokenPrice > 0 && collateralTokenPrice > 0, "Invalid prices");
// Calculate collateral to withdraw: (debtAmount * debtPrice * collateralDec * LTV_PRECISION) / (collateralPrice * debtDec * ltv)
uint256 collateralToWithdraw = (
_amount * debtTokenPrice * (10 ** IERC20(unwindParams.collateralToken).decimals()) * LTV_PRECISION
) / (collateralTokenPrice * (10 ** IERC20(_asset).decimals()) * liqThreshold); // Uses wrong parameter

Risk


Proof of Concept

// Realistic scenario: Unwinding USDC-collateralized ETH borrow position
// Aave USDC configuration: LTV = 80% (8000), liquidation threshold = 82.5% (8250)
// Debt to repay: 10,000 USDC flash loan
// CORRECT calculation using LTV (80%):
// collateralNeeded = (10,000 USDC * 1.0 USD/USDC * 1e6 USDC decimals * 10,000)
// / (3,000 USD/ETH * 1e18 ETH decimals * 8,000)
// = 0.004166... ETH → 4.166e15 wei
// INCORRECT calculation using liquidation threshold (82.5%):
// collateralNeeded = (... * 10,000) / (... * 8,250)
// = 0.004040... ETH → 4.040e15 wei (3% less)
// Result: Withdraws 3% less ETH than required
// After 1% swap slippage: 4.040e15 * 0.99 = 3.999e15 ETH value in USDC
// Required repayment: 10,000 USDC + 9 USDC fee = 10,009 USDC
// Shortfall: ~10 USDC → transaction reverts with "Insufficient funds to repay flash loan"

Recommended Mitigation

- function _executeUnwindOperation(address _asset, uint256 _amount, uint256 _premium, bytes calldata _params)
internal
returns (bool)
{
// ...
uint256 withdrawnAmount;
{
- // Get LTV from Aave for the collateral token
- (,, uint256 liqThreshold,,,,,,,) =
+ // Get LTV (index 1) not liquidation threshold (index 2) for debt-backed collateral calculation
+ (, uint256 ltv,,,,,,,,) =
aaveDataProvider.getReserveConfigurationData(unwindParams.collateralToken);
uint256 debtTokenPrice = IStrataxOracle(strataxOracle).getPrice(_asset);
uint256 collateralTokenPrice = IStrataxOracle(strataxOracle).getPrice(unwindParams.collateralToken);
require(debtTokenPrice > 0 && collateralTokenPrice > 0, "Invalid prices");
uint256 collateralToWithdraw = (
_amount * debtTokenPrice * (10 ** IERC20(unwindParams.collateralToken).decimals()) * LTV_PRECISION
- ) / (collateralTokenPrice * (10 ** IERC20(_asset).decimals()) * liqThreshold);
+ ) / (collateralTokenPrice * (10 ** IERC20(_asset).decimals()) * ltv);
withdrawnAmount = aavePool.withdraw(unwindParams.collateralToken, collateralToWithdraw, address(this));
}
// ...
}
+

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!