FoundrySolidityLayer 2
7.25 ETH
Submission Details
Impact: low
Likelihood: high

No extcodesize check on setPoolImplementation

Author Revealed upon completion

Description

setPoolImplementation() validates against address(0) (Factory:138) but does not check that the new implementation has contract code. OpenZeppelin's Clones.cloneDeterministic does not validate the implementation internally — it deploys minimal proxies that delegatecall to the given address. If the address is an EOA, the clone is deployed but all delegatecalls silently succeed with empty return data.

Factory:137 function setPoolImplementation(newPoolImplementation) external onlyOwner {
Factory:138 if (newPoolImplementation == address(0)) revert ZeroAddress();
// MISSING: if (newPoolImplementation.code.length == 0) revert EmptyImplementation();
Factory:140 poolImplementation = newPoolImplementation;
}

Risk

Likelihood: Low. The factory owner is DAO-controlled (trusted role). Requires an accidental fat-finger or a compromised owner to set an EOA.

Impact: High when triggered. Every new pool created afterward is non-functional — stake(), withdraw(), flagOutcome(), claim*() all delegatecall to an EOA and silently no-op. Existing pools are unaffected (they use the pre-change implementation).

Mitigation

Add an extcodesize check:

function setPoolImplementation(address newPoolImplementation) external onlyOwner {
if (newPoolImplementation == address(0)) revert ZeroAddress();
+ if (newPoolImplementation.code.length == 0) revert EmptyImplementation();
poolImplementation = newPoolImplementation;
}

Support

FAQs

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

Give us feedback!