DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: medium
Valid

User can withdraws their bid (`unbid()`) after the final multiplier is calculated in `auctionEnd()`

Summary

The FjordAuction contract contains a potential race condition where both auctionEnd() and unbid() functions can be called in the last block of the auction.

This could lead to a situation where a user withdraws their bid (unbid()) after the final multiplier is calculated in auctionEnd(), but within the same block. This race condition may result in an incorrect final distribution of the auctioned tokens.

Vulnerability Details

The issue arises due to the overlapping conditions in the auctionEnd() and unbid() functions:

  • auctionEnd() can be called when block.timestamp = auctionEndTime.

  • unbid() can be called when block.timestamp = auctionEndTime.

Both function can be called at the last moment:

block.timestamp = auctionEndTime

Both functions can be executed, potentially in quick succession. A malicious user calls the auctionEnd() function. This function calculates multiplier.

multiplier = totalTokens.mul(PRECISION_18).div(totalBids);

As soon as multiplier is calculated, the same user calls the unbid() function and withdraw his bids.

In this way he can manipulate the multiplier.

Impact

A malicious user could strategically time their unbid() transaction to withdraw their bid after the multiplier is set. This will result in an inflated multiplier.

Tools Used

Visual Studio Code

Recommendations

Ensure that the user cannot call the unbid() function when block.timestamp = auctionEndTime.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Users can bid in the same block when the actionEnd could be called (`block.timestamp==actionEndTime`), depending on the order of txs in block they could lose funds

The protocol doesn't properly treat the `block.timestamp == auctionEndTime` case. Impact: High - There are at least two possible impacts here: 1. By chance, user bids could land in a block after the `auctionEnd()` is called, not including them in the multiplier calculation, leading to a situation where there are insufficient funds to pay everyone's claim; 2. By malice, where someone can use a script to call `auctionEnd()` + `bid(totalBids)` + `claimTokens()`, effectively depriving all good faith bidders from tokens. Likelihood: Low – The chances of getting a `block.timestamp == auctionEndTime` are pretty slim, but it’s definitely possible.

Support

FAQs

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