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

Allowlisted token proxy upgrade breaks pool accounting post-creation

Author Revealed upon completion

Description

The factory maintains an allowlist of stake tokens via allowedStakeToken mapping (Factory:31), checked once at createPool time (Factory:77). The pool assumes standard ERC20 behavior — no transfer fees, no rebasing. stake() (Pool:236-240) and contributeBonus() (Pool:275-279) use balance-diff accounting, which defends against fee-on-transfer at the moment of deposit but not against mid-lifecycle behavior changes.

If the stake token is a transparent-upgradeable proxy (USDC, USDT), the implementation can be upgraded after pool creation. A subsequent upgrade could introduce fee-on-transfer, blacklists, or pausability. The pool has no mechanism to eject or migrate away from a compromised token.

Factory:77 if (!allowedStakeToken[stakeToken]) revert StakeTokenNotAllowed();
// checked ONCE at creation, never re-verified
Pool:236 uint256 balanceBefore = stakeToken.balanceOf(address(this));
Pool:237 stakeToken.safeTransferFrom(msg.sender, address(this), amount);
Pool:239 uint256 balanceAfter = stakeToken.balanceOf(address(this));
Pool:240 uint256 received = balanceAfter > balanceBefore ? balanceAfter - balanceBefore : 0;
// balance-diff handles fees at time-of-transfer, not post-creation changes

Risk

Likelihood: Low. Requires the token proxy admin (a trusted external entity) to upgrade the implementation after pool creation. This is plausible for widely-used upgradeable tokens (USDC, USDT) where the Circle/Foundation could introduce new behavior.

Impact: High. Claim functions (claimSurvived, claimCorrupted, claimExpired) revert if the token pauses or blacklists the pool — permanently locking all staker funds. If fees are introduced, tracked liabilities diverge from actual balance, causing accounting corruption. The token address on the allowlist hasn't changed, so the factory provides no ongoing protection.

Mitigation

Add a pool-level escape mechanism: a setStakeToken function (owner-only, callable only pre-resolution) that allows migrating to a new token. Alternatively, document in the factory that only non-upgradeable tokens should be allowlisted and add a code-hash check or proxy-detection at allowlist time.

Support

FAQs

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

Give us feedback!