Thunder Loan

AI First Flight #7
Beginner FriendlyFoundryDeFiOracle
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Missing address(0) validation in initialize() and __Oracle_init_unchained allows permanently bricking the oracle wiring and all flash loans

Missing address(0) validation in initialize() and __Oracle_init_unchained allows permanently bricking the oracle wiring and all flash loans

Description

  • Normal behavior: initialization parameters are validated so a misconfiguration cannot permanently disable core protocol functionality.

  • Specific issue: initialize(address tswapAddress) (ThunderLoan.sol:139) accepts address(0) with no check, and __Oracle_init_unchained (OracleUpgradeable.sol:15-17) assigns s_poolFactory = poolFactoryAddress without zero-address validation. A zero factory makes every getPriceInWeth() lookup fail, so getCalculatedFee() and therefore flashloan() can never execute.

// ThunderLoan.sol
@> function initialize(address tswapAddress) public initializer { // :139 — no zero-address check
__ThunderLoan_init(tswapAddress);
}
// OracleUpgradeable.sol
function __Oracle_init_unchained(address poolFactoryAddress) internal onlyInitializing {
@> s_poolFactory = poolFactoryAddress; // :15-17 — zero address stored as-is
}

Risk

Likelihood:

  • Requires a deployer misconfiguration (passing the zero address) — rare, but nothing in the init chain detects it at deploy time.

  • The mistake is silent: deployment and deposits succeed, the breakage appears only at the first flash loan.

Impact:

  • getPriceInWeth() reverts or returns garbage on every call, so getCalculatedFee() and flashloan() are permanently unusable — protocol DoS until an owner-driven upgrade.

  • No direct loss of funds, but depositor liquidity is stranded in a bricked protocol.

Proof of Concept

Explanation: deploy the proxy and call initialize(address(0)) — the call succeeds; setAllowedToken() and deposit() also succeed; the first flashloan() (or a direct getCalculatedFee()) reverts inside the zero-address pool lookup, bricking the protocol with no on-chain way to recover except an upgrade. PoC requires forge verification.

// 1. ThunderLoan(proxy).initialize(address(0)); // succeeds — no validation
// 2. setAllowedToken(weth, true); deposit(weth, 1000e18); // succeed
// 3. vm.expectRevert(); // zero-address pool lookup
// thunderLoan.flashloan(receiver, weth, 100e18, ""); // bricked forever

Recommended Mitigation

Explanation: zero-address checks at both initialization entry points make the misconfiguration impossible at deploy time, which is far cheaper than an emergency upgrade after deposits are stranded.

--- a/src/protocol/OracleUpgradeable.sol
+++ b/src/protocol/OracleUpgradeable.sol
@@ __Oracle_init_unchained @@
function __Oracle_init_unchained(address poolFactoryAddress) internal onlyInitializing {
+ require(poolFactoryAddress != address(0), "Oracle__ZeroAddress");
s_poolFactory = poolFactoryAddress;
--- a/src/protocol/ThunderLoan.sol
+++ b/src/protocol/ThunderLoan.sol
@@ initialize @@
function initialize(address tswapAddress) public initializer {
+ require(tswapAddress != address(0), "ThunderLoan__ZeroAddress");
__ThunderLoan_init(tswapAddress);
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 1 hour ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!