Stratax Contracts

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

Incorrect Parameter Used in Collateral Withdrawal Calculation

Author Revealed upon completion

Root + Impact

Description

When unwinding a leveraged position, the protocol calculates how much collateral can be withdrawn after repaying debt. The calculation should use the LTV (Loan-to-Value) ratio, which represents the maximum borrowing power of collateral. However, the code incorrectly uses liquidationThreshold, which is a higher threshold used for liquidation risk assessment. The liquidation threshold is typically 5-10% higher than LTV (e.g., LTV = 80%, liquidation threshold = 85%), meaning the formula extracts less collateral than it should. Additionally, the code comments explicitly state that LTV should be used, creating a discrepancy between documentation and implementation.

// src/Stratax.sol:565-577
// @> Comment says "Get LTV" but actually retrieves liquidationThreshold
// Get LTV from Aave for the collateral token
(,, uint256 liqThreshold,,,,,,,) =
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");
// @> Comment says calculation uses "ltv" but code uses "liqThreshold"
// 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);

Risk

Likelihood:

  • This calculation runs on every unwindPosition call where the protocol calculates collateral withdrawal internally.

  • The code comments explicitly state LTV should be used, indicating this was the intended design, making the use of liquidation threshold a clear implementation error.

Impact:

  • Reduced Withdrawal Amount: Users receive less collateral than they should when unwinding positions. For example, if LTV = 80% and liquidation threshold = 85%, repaying $1000 debt would allow withdrawal of $1250 collateral (using LTV) but only $1176.47 collateral (using liquidation threshold), a difference of $73.53 per $1000 debt.

  • Code Documentation Mismatch: The discrepancy between comments and implementation creates confusion and makes the code harder to maintain and audit.

Proof of Concept

// Assumptions:
// LTV = 80% (8000 basis points)
// Liquidation Threshold = 85% (8500 basis points)
// Debt to repay = $1000 USDC
// Collateral token = WETH at $2000
// Current implementation (using liquidation threshold):
// collateralValue = debtValue / liquidationThreshold
// collateralValue = $1000 / 0.85 = $1176.47
// WETH amount = $1176.47 / $2000 = 0.588 WETH
// Correct implementation (using LTV):
// collateralValue = debtValue / LTV
// collateralValue = $1000 / 0.80 = $1250
// WETH amount = $1250 / $2000 = 0.625 WETH
// Difference: 0.625 - 0.588 = 0.037 WETH = $74 locked per $1000 debt repaid

Recommended Mitigation

Fix the code to use LTV as indicated by the comments:

// 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,,,,,,,) =
+ // Get LTV from Aave for the collateral token
+ (, uint256 ltv,,,,,,,) =
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);
+ ) / (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!