Stratax Contracts

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

`calculateUnwindParams()` and `_executeUnwindOperation()` use different formulas, and user-provided `collateralToWithdraw` parameter is completely ignored

Author Revealed upon completion

Root + Impact

The calculateUnwindParams() and _executeUnwindOperation() use different formulas to calculate collateralToWithdraw (15.4% discrepancy). Additionally, the user-provided collateralToWithdraw parameter is completely ignored — a local variable shadows the struct field.

Description

  • calculateUnwindParams() (L464-468) uses debtValue × 1.05 (5% buffer), while _executeUnwindOperation() (L575-577) uses debtValue × (10000 / liqThreshold)× 1.212 for ETH — a 15.4% difference between preview and execution.

  • The _collateralToWithdraw parameter in unwindPosition() is stored in the UnwindParams struct and passed through the flash loan, but at line 575, a local variable uint256 collateralToWithdraw = (...) shadows unwindParams.collateralToWithdraw — the struct field is never read.

// calculateUnwindParams() L464-468:
collateralToWithdraw = (collateralToWithdraw * 1050) / 1000; // @> ×1.05
// _executeUnwindOperation() L575-577:
uint256 collateralToWithdraw = (
_amount * debtTokenPrice * (10 ** IERC20(unwindParams.collateralToken).decimals()) * LTV_PRECISION
) / (collateralTokenPrice * (10 ** IERC20(_asset).decimals()) * liqThreshold); // @> ×1.212
// L556: unwindParams.collateralToWithdraw is decoded but NEVER READ

Risk

Likelihood: This inconsistency exists in every unwind call flow. The local variable shadowing is deterministic.

Impact: No direct fund loss — the contract uses its own calculation. However, the API parameter is functionally useless (wastes gas, misleads integrators), and frontends show previews that differ ~15% from execution.

Proof of Concept

For ETH (liqThreshold=8250): calculateUnwindParams returns debtValue × 1.05, while _executeUnwindOperation computes debtValue × 1.2121 — a 15.4% gap. The user parameter traces through encoding → flash loan → decoding, but the struct field is shadowed at line 575 and never referenced.

// Formula comparison: 1.2121 / 1.05 = 15.4% discrepancy
// Parameter trace: unwindPosition(X) → encode → flash loan → decode →
// unwindParams.collateralToWithdraw = X (AVAILABLE)
// uint256 collateralToWithdraw = (...) ← LOCAL shadows struct, X ignored

Recommended Mitigation

Either use the user-provided value (Option A) to align execution with the preview, or remove the unused parameter (Option B) to clean up the API.

// Option A: Use user value
- uint256 collateralToWithdraw = (...internal calculation...);
+ uint256 collateralToWithdraw = unwindParams.collateralToWithdraw;
// Option B: Remove unused parameter
function unwindPosition(
address _collateralToken,
- uint256 _collateralToWithdraw,
address _debtToken,
...
) external onlyOwner { ... }

Support

FAQs

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

Give us feedback!