Stratax::calculateUnwindParams is the public view function users and frontends call to determine how much collateral will be withdrawn during an unwind, so they can prepare the corresponding 1inch swap calldata. Stratax::_executeUnwindOperation is the internal function that performs the actual withdrawal.
The two functions compute collateralToWithdraw using entirely different formulas. calculateUnwindParams applies a flat 5% slippage buffer on top of the price-ratio amount. _executeUnwindOperation applies an LTV-precision scaling using liqThreshold. For typical Aave parameters (ltv=7500, liqThreshold=8000), the execution function withdraws ~19% more collateral than the view function predicted. The _collateralToWithdraw parameter accepted by unwindPosition is stored in UnwindParams but is never read by _executeUnwindOperation — it is silently dead code.
Likelihood:
Every user who calls calculateUnwindParams to prepare 1inch swap calldata and then calls unwindPosition with that calldata triggers the mismatch — this is the documented and intended usage pattern.
The discrepancy is always present regardless of market conditions, position size, or token pair, because it is structural: the two formulas differ by a constant factor of (LTV_PRECISION / liqThreshold) / 1.05.
Impact:
The contract withdraws ~19% more collateral from Aave than the user's 1inch calldata was built for. If 1inch interprets the calldata as an exact-input swap, the extra collateral cannot be swapped, the flash loan repayment falls short, and unwindPosition reverts — the user's position is permanently locked until they construct calldata for the correct amount, which calculateUnwindParams cannot provide.
Even if the swap does not revert, the extra collateral withdrawn but not swapped remains stranded in the contract and is not returned to the user, causing a direct financial loss of ~$190 per $1000 USDC of debt at typical parameters.
The _collateralToWithdraw parameter in unwindPosition is dead code: it is accepted, validated, and stored, but _executeUnwindOperation ignores it entirely and recalculates its own value. Any value the user passes — including the output of calculateUnwindParams — has no effect on execution.
Both functions must use the same formula so that the calldata prepared from the view function matches what the execution function will actually withdraw. Alternatively, make _executeUnwindOperation read unwindParams.collateralToWithdraw directly instead of recalculating.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.