Stratax Contracts

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

Missing `address(0)` Validation For Critical Protocol Addresses In `initialize()`

Author Revealed upon completion

The Stratax contract is an upgradeable (Beacon Proxy) leveraged position management contract that interacts with Aave for flash loans and lending, and 1inch for token swaps. The initialize() function is the single entry point for setting all critical protocol addresses and can only be called once due to OpenZeppelin's initializer modifier. It accepts five address parameters (_aavePool, _aaveDataProvider, _oneInchRouter, _usdc, _strataxOracle) and assigns them directly to state variables without validating any of them against address(0).

This is inconsistent with validation applied elsewhere in the contract. The setStrataxOracle() function validates _strataxOracle != address(0), and transferOwnership() validates _newOwner != address(0). Despite this awareness of the zero-address risk, the initialize() function omits equivalent checks for all five parameters.

Furthermore, the contract provides setter functions only for strataxOracle (via setStrataxOracle()) and flashLoanFeeBps (via setFlashLoanFee()). The four remaining protocol addresses (aavePool, aaveDataProvider, oneInchRouter, USDC) have no setter functions and are immutable after initialisation. If any of these are set to address(0), all core functions such as createLeveragedPosition() and unwindPosition() that depend on these addresses will revert.

// src/Stratax.sol
function initialize(
address _aavePool,
address _aaveDataProvider,
address _oneInchRouter,
address _usdc,
address _strataxOracle
) external initializer {
aavePool = IPool(_aavePool); // @audit no address(0) check - no setter exists
aaveDataProvider = IProtocolDataProvider(_aaveDataProvider); // @audit no address(0) check - no setter exists
oneInchRouter = IAggregationRouter(_oneInchRouter); // @audit no address(0) check - no setter exists
USDC = _usdc; // @audit no address(0) check - no setter exists
strataxOracle = _strataxOracle; // @audit no address(0) check (setter exists with validation)
owner = msg.sender;
flashLoanFeeBps = 9;
}

In contrast, setStrataxOracle() includes the validation that is absent from initialize():

// src/Stratax.sol
function setStrataxOracle(address _strataxOracle) external onlyOwner {
require(_strataxOracle != address(0), "Invalid oracle address"); // @audit validation present here but absent in initialize()
strataxOracle = _strataxOracle;
}

While the Beacon Proxy pattern does provide a recovery path (the beacon owner could upgrade the implementation to introduce setter functions for the affected addresses), this is a non-trivial remediation that should not be relied upon as a substitute for proper input validation.

This issue has a low impact as the contract would be non-functional from deployment, meaning no user funds would be at risk before the issue is discovered. The recoverTokens() function can salvage any tokens if the owner was set correctly, and the beacon owner can upgrade the implementation as a recovery mechanism.

This issue has a low likelihood as it requires a deployment error by a trusted deployer, such as a misconfigured environment variable or automation bug. Deployers typically test contracts after deployment, which would reveal the issue before user interaction.

recommendation

Add address(0) validation for all five address parameters in the initialize() function, consistent with the validation already applied in setStrataxOracle() and transferOwnership(). Each parameter should be checked with a require statement ensuring it is not equal to address(0) before assignment.

Additionally, consider adding setter functions with appropriate access control and validation for aavePool, aaveDataProvider, oneInchRouter, and USDC to provide a recovery path if these addresses need to change in the future.

Support

FAQs

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

Give us feedback!