Skip to content
Request an audit

Foundation recall will inflate governance power for earnest voters

TitleFoundation recall will inflate governance power for earnest voters
Reward$381, 14 dups
ContestSummer.fi - 29 Sep 2025 on Sherlock
AuthorElroi
ContextWrong variable tracking

This is a straightforward example of functionality the protocol overlooked. It is often a trivial finding, but it touches business logic, so many people miss it.

The SummerVestingWalletEscrow mints xSUMR (governance power) when a user stakes, and locks the tokens in their SummerVestingWallet.

solidity
function _stakeFromFactory(
    IMinimalVestingFactory _vestingFactory,
    address _user
) internal {
    // Prevent double-staking same factory to preserve invariant on accounting maps
    address factoryAddress = address(_vestingFactory);
    if (
        _userStakedVestingFactoriesBalance[_user].contains(factoryAddress)
    ) {
        revert Staking_FactoryAlreadyStaked();
    }

    // Resolve vesting wallet and validate escrow ownership
    // vestingWallets map can't be modified, always keeps the original owner
    address vestingWallet = _vestingFactory.vestingWallets(_user);
    if (vestingWallet == address(0)) {
        revert Staking_InvalidAddress("Vesting wallet not found");
    }

    _validateVestingWalletOwner(vestingWallet);

    // Snapshot SUMR balance and released amount at time of stake
    uint256 balance = SUMMER_TOKEN.balanceOf(vestingWallet);
    if (balance == 0) {
        revert Staking_ZeroBalance();
    }
    uint256 released = IMinimalVestingWallet(vestingWallet).released(
        address(SUMMER_TOKEN)
    );

    // Persist stake metadata for this user/factory pair
    _userStakedVestingFactoriesBalance[_user].set(factoryAddress, balance);
    _userStakedVestingFactoriesReleased[_user].set(
        factoryAddress,
        released
    );

    // Mint governance power (xSUMR) equal to vesting wallet SUMR balance
    STAKED_SUMMER_TOKEN.mint(_user, balance);

    emit StakedVestingWallet(_user, factoryAddress, balance, released);
}

However, SummerVestingWallet offers the option to withdraw all the tokens, while the user keeps the governance tokens that were minted — which are then left unbacked.

solidity
function recallUnvestedTokens() external onlyFactoryOwner {
    if (isRecalled) {
        revert TokensAlreadyRecalled();
    }

    // Get ALL tokens from this wallet - no calculations needed
    uint256 totalBalance = IERC20(token).balanceOf(address(this));

    // Brick the wallet permanently
    isRecalled = true;

    // Transfer ALL tokens to admin (vested + unvested = everything)
    if (totalBalance > 0) {
        IERC20(token).safeTransfer(msg.sender, totalBalance);
    }

    emit UnvestedTokensRecalled(totalBalance);
}

Conclusion

This finding would earn you $381, which is fair since it is very simple: the protocol team completely overlooked the governance tokens left in circulation.

Full Report

Message on Telegram All 62 posts