The Snow constructor validates that the _collector address is non-zero before storing it as the fee recipient, guarding against a common deployment mistake.
The constructor validates _collector != address(0) but does not check _collector != address(this). If the collector is set to the Snow contract itself, collectFee() transfers WETH and ETH back to the contract — a no-op that silently appears to succeed while fees are permanently locked. changeCollector() has the same gap, allowing the role to be transferred to address(this) at any time after deployment.
Likelihood:
This requires either a deployment error (passing address(snow) as the collector address during construction) or a governance mistake when calling changeCollector() — both are operator errors rather than attacker-controlled conditions.
The changeCollector() gap extends the exposure window indefinitely post-deployment.
Impact:
No direct theft of funds. Fee revenue is directed to an unspendable address through operator error, permanently locking all future WETH and ETH fees in the contract with no recovery path.
Once set to address(this), all future fee collections become no-ops and accumulated fees cannot be rescued.
Place this test in test/ and run forge test --match-test testFeesLockedWhenCollectorIsSelf. The test demonstrates that deploying Snow with collector set to address(this) locks all collected fees in the contract with no external withdrawal path.
Add a if (_collector == address(this)) revert S__InvalidCollector() check in both the constructor and changeCollector(), alongside the existing zero-address guard in each, to prevent self-referential collector configuration at deployment and after any post-deployment role transfer.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.