User can grief bootstrap process by sending the cap amount of unlocked tokens to it.
mediumLines of code
Vulnerability details
Impact
The bootstrap process relies on users locking up all their tokens as it will only use tokens that are marked locked in totals[MIM].locked and totals[USDB].locked.
BlastOnboardingBoot.sol#L96-L127
solidityfunction bootstrap(uint256 minAmountOut) external onlyOwner onlyState(State.Closed) returns (address, address, uint256) { if (pool != address(0)) { revert ErrAlreadyBootstrapped(); } => uint256 baseAmount = totals[MIM].locked; => uint256 quoteAmount = totals[USDB].locked; MIM.safeApprove(address(router), type(uint256).max); USDB.safeApprove(address(router), type(uint256).max); (pool, totalPoolShares) = router.createPool(MIM, USDB, FEE_RATE, I, K, address(this), baseAmount, quoteAmount); if (totalPoolShares < minAmountOut) { revert ErrInsufficientAmountOut(); } // Create staking contract // 3x boosting for locker, 7 days reward duration, 13 weeks lp locking // make this contract temporary the owner the set it as an operator // for permissionned `stakeFor` during the claiming process and then // transfer the ownership to the onboarding owner. staking = new LockingMultiRewards(pool, 30_000, 7 days, 13 weeks, address(this)); staking.setOperator(address(this), true); staking.transferOwnership(owner); // Approve staking contract pool.safeApprove(address(staking), totalPoolShares); emit LogLiquidityBootstrapped(pool, address(staking), totalPoolShares); return (pool, address(staking), totalPoolShares); }
However, it is possible that these values can be zero because there is a cap on the amount of tokens that can be stored in the contract.
solidityfunction deposit(address token, uint256 amount, bool lock_) external whenNotPaused onlyState(State.Opened) onlySupportedTokens(token) { token.safeTransferFrom(msg.sender, address(this), amount); if (lock_) { totals[token].locked += amount; balances[msg.sender][token].locked += amount; } else { totals[token].unlocked += amount; balances[msg.sender][token].unlocked += amount; } totals[token].total += amount; if (caps[token] > 0 && totals[token].total > caps[token]) { revert ErrCapReached(); } balances[msg.sender][token].total += amount; emit LogDeposit(msg.sender, token, amount, lock_); }
In the code above, notice that there is a cap on the amount of tokens that can be sent to the contract caps[token]. The problem here is that it is checked against the total token amount totals[token].total rather than the locked amount totals[token].locked.
Therefore a griefer can deposit tokens up to the caps[token] with lock_ = false. The result, is that no one else can deposit tokens into the contract.
During bootstrap, since these tokens are still considered unlocked, then totals[MIM].locked = 0 and totals[USDB].locked = 0, therefore there won't be any locked tokens available for the bootstrapping process.
Tools Used
Manual Review
Recommended Mitigation Steps
Instead of checking caps[token] against totals[token].total, it should be checked against totals[token].locked.
Assessed type
Other
