Stratax Contracts

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

Missing `supplyCollateral`, `borrowDebt`, and `repayDebt` functions leave positions unmanageable and exposed to liquidation

Author Revealed upon completion

Missing supplyCollateral, borrowDebt, and repayDebt functions leave positions unmanageable and exposed to liquidation

Description

The README specifies the position owner's powers as:

Can create leveraged positions, unwind positions, supply additional collateral, withdraw collateral, borrow debt tokens, and repay debt

However, only three of these six functions are implemented:

Documented Power Implemented Function
Create leveraged positions Yes createLeveragedPosition
Unwind positions Yes unwindPosition
Withdraw collateral Yes withdrawCollateral
Supply additional collateral No
Borrow debt tokens No
Repay debt No

The contract's IPool interface already defines supply, borrow, and repay — and the contract itself uses all three internally during flash loan callbacks — but none are exposed as owner-callable external functions.

The most critical missing function is supplyCollateral. When a leveraged position's health factor declines toward the liquidation threshold (e.g., due to collateral price dropping or debt token price rising), the only way to prevent Aave liquidation is to supply additional collateral. Without this function, the owner is forced to watch their position get liquidated with no ability to intervene.

Similarly, the absence of repayDebt means an owner cannot partially de-risk a position by repaying some debt to improve the health factor — the only option is a full unwindPosition, which is an all-or-nothing operation requiring a flash loan and a DEX swap.

The missing borrowDebt function prevents the owner from borrowing against excess collateral in their Aave position, reducing capital flexibility.

// The only collateral/debt management function exposed to the owner:
function withdrawCollateral(address _collateralToken, uint256 _amount) external onlyOwner {
aavePool.withdraw(_collateralToken, _amount, msg.sender);
}
// Missing — no way for the owner to call:
// aavePool.supply(token, amount, address(this), 0) — supply additional collateral
// aavePool.repay(token, amount, 2, address(this)) — repay debt
// aavePool.borrow(token, amount, 2, 0, address(this)) — borrow against collateral

Risk

Likelihood:

Leveraged positions on Aave are inherently volatile — the health factor is a function of both collateral and debt token prices, which move continuously. Any position held for more than a short period will experience health factor fluctuations. During a market downturn, the owner needs to act quickly to add collateral or repay debt. Since the contract provides no mechanism for either action, the owner's only option when the health factor drops is to fully unwind the position (which itself may fail due to DEX slippage or flash loan availability during volatile markets) or accept liquidation. This is not an edge case: it is the normal lifecycle of a leveraged position.

Impact:

Positions approaching the liquidation threshold cannot be rescued. The owner has no way to supply additional collateral to improve the health factor, and no way to partially repay debt to reduce exposure. The only available action — full unwind — requires favorable market conditions to execute (DEX liquidity, acceptable slippage), which are precisely the conditions that are absent during the sharp price moves that cause health factor deterioration. This creates a scenario where positions are most likely to be liquidated exactly when the tools to prevent it are most needed and least available. Liquidation on Aave V3 incurs a 5–10% liquidation bonus penalty on the collateral, representing a direct and significant loss to the user.

Proof Of Concept

If you look through the Stratax.sol contract you can clearly see these functions are missing.

Recommended Mitigation

Add the three missing owner-callable functions:

+ function supplyCollateral(address _collateralToken, uint256 _amount) external onlyOwner {
+ IERC20(_collateralToken).transferFrom(msg.sender, address(this), _amount);
+ IERC20(_collateralToken).approve(address(aavePool), _amount);
+ aavePool.supply(_collateralToken, _amount, address(this), 0);
+ }
+
+ function repayDebt(address _debtToken, uint256 _amount) external onlyOwner {
+ IERC20(_debtToken).transferFrom(msg.sender, address(this), _amount);
+ IERC20(_debtToken).approve(address(aavePool), _amount);
+ aavePool.repay(_debtToken, _amount, 2, address(this));
+ }
+
+ function borrowDebt(address _debtToken, uint256 _amount) external onlyOwner {
+ aavePool.borrow(_debtToken, _amount, 2, 0, address(this));
+ IERC20(_debtToken).transfer(msg.sender, _amount);
+ }

Support

FAQs

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

Give us feedback!