Stratax Contracts

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

No pause/guardian or circuit breaker

Author Revealed upon completion

Description

  • DeFi protocols typically include a circuit breaker (pause/guardian) so operators can quickly stop state‑changing actions (opens/unwinds/swaps) if a dependency (Aave, 1inch, oracle) misbehaves or is under attack. Pausing prevents new risk from being added while a fix or configuration change is rolled out.

  • Stratax has no pause mechanism. The public entry points that initiate flash loans and external swaps (createLeveragedPosition and unwindPosition) are always callable (subject only to onlyOwner). If an upstream integration (router, oracle, lending pool) is returning bad data or changes behavior, there’s no on‑chain switch to stop operations immediately.

// Stratax.sol
function createLeveragedPosition(...) public onlyOwner { ... } // @> no pause guard
function unwindPosition(...) external onlyOwner { ... } // @> no pause guard
// executeOperation(...) => continues per flow if called

Risk

Likelihood: Low

  • External dependency incidents (router upgrades, pool parameter changes, oracle anomalies) happen in production. During such windows, transactions that rely on those components will be submitted by bots/users.

  • Operational mistakes (e.g., mis‑set minReturn, fee mismatch, wrong router) are common; without a pause, these continue to propagate until off‑chain actors halt.

Impact: Low

  • Compounding loss or stuck positions: Users can keep opening/unwinding under bad conditions, creating unhealthy positions or failed flash‑loan paths.

  • Incident response delay: Without a contract‑level stop, you must coordinate off‑chain (halt bots/UIs), which is slower and error‑prone, especially cross‑ecosystem.

Proof of Concept

  • Conceptual pseudocode:

// t0: 1inch route changes; calldata path now returns less favorable amounts but still >= user minReturn.
while (market volatile) {
// Owner bot keeps calling:
stratax.createLeveragedPosition(... oneInchSwapData = staleOrBadPath ...);
// No on-chain pause ⇒ transactions keep executing; positions open with poor/slippage-heavy fills.
}
// Alternative incident:
// Chainlink feed anomaly / stale data period.
// Off-chain systems are slow to stop; users continue to call unwindPosition(...).
// No pause ⇒ contracts keep initiating flash loans & swaps based on bad prices.

Recommended Mitigation

  • Introduce a Pausable circuit breaker and guard risky entry points. Optionally add a guardian role distinct from owner for faster incident response.

- import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
+ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
-contract Stratax is Initializable {
+contract Stratax is Initializable, PausableUpgradeable {
address public owner;
function initialize(
address _aavePool,
address _aaveDataProvider,
address _oneInchRouter,
address _usdc,
address _strataxOracle
) external initializer {
aavePool = IPool(_aavePool);
aaveDataProvider = IProtocolDataProvider(_aaveDataProvider);
oneInchRouter = IAggregationRouter(_oneInchRouter);
USDC = _usdc;
strataxOracle = _strataxOracle;
owner = msg.sender;
flashLoanFeeBps = 9;
+ __Pausable_init();
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
+ /// @notice Pause/unpause circuit breaker (owner or guardian; start with owner only)
+ function pause() external onlyOwner { _pause(); }
+ function unpause() external onlyOwner { _unpause(); }
// ------------------- ENTRY POINTS -------------------
- function createLeveragedPosition(...) public onlyOwner {
+ function createLeveragedPosition(...) public onlyOwner whenNotPaused {
...
}
- function unwindPosition(...) external onlyOwner {
+ function unwindPosition(...) external onlyOwner whenNotPaused {
...
}
// (Optional) Restrict risky admin paths when paused, or require paused for recoverTokens
+ function recoverTokens(address _token, uint256 _amount) external onlyOwner /* whenPaused */ {
IERC20(_token).transfer(owner, _amount);
}
}

Support

FAQs

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

Give us feedback!