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

All of `bid()`, `unbid()` and `auctionEnd()` functions can be executed at the same `auctionEndTime`.

Summary

All of bid(), unbid() and auctionEnd() functions can be executed at the same auctionEndTime within the same block.
In particular, if bid() or unbid() is executed after auctionEnd(), it causes serious problems to the protocol.

Vulnerability Details

FjordAuction.bid(), FjordAuction.unbid() and FjordAuction.auctionEnd() functions are following.

function bid(uint256 amount) external {
144: if (block.timestamp > auctionEndTime) {
revert AuctionAlreadyEnded();
}
--- SKIP ---
}
function unbid(uint256 amount) external {
160: if (block.timestamp > auctionEndTime) {
revert AuctionAlreadyEnded();
}
--- SKIP ---
}
function auctionEnd() external {
182: if (block.timestamp < auctionEndTime) {
revert AuctionNotYetEnded();
}
--- SKIP ---
}

As can be seen, all of bid(), unbid() and auctionEnd() functions can be executed at auctionEndTime.
From this, the following scenarios are available. To simplify, we ignore precision factor in the following calculations.

Scenario 1 of bid():

  1. Assume that total auction token is 1000 and user1 bids 100 points.

  2. At auctionEndTime, auctionEnd() is executed and multiplier is determined as 1000 / 100 = 10.

  3. In the same block of auctionEnd()'s tx but after that, user2's bid() tx is executed with 100 points.

  4. If user2 claims first, 100 * 10 = 1000 auction tokens are transferred to user2.

  5. After that, user1 can't claim auction tokens because there are no more auction tokens left.

Scenario 2 of unbid():

  1. Assume that total auction token is 1000 and user1 bids 40 points and user2 bids 60 points so the total bids are 40 + 60 = 100.

  2. At auctionEndTime, auctionEnd() is executed and multiplier is determined as 10.

  3. In the same block of auctionEnd()'s tx but after that, user2's unbid() tx is executed with 60 points.

  4. user1 claims his 400 auction tokens.

  5. 1000 - 400 = 600 auction tokens will be locked in FjordAuction contract.

Attacker can even use scenario 2 to lock almost auction tokens in the contract and damage other users with huge amount of points.

Impact

Users can't claim tokens after auction finishes or some tokens may be locked in FjordAuction contract.
Attacker can lock almost auction tokens in the contract and damage other users.

Tools Used

Manual Review

Recommendations

Modify FjordAuction.auctionEnd() function as follows.

function auctionEnd() external {
-- if (block.timestamp < auctionEndTime) {
++ if (block.timestamp <= auctionEndTime) {
revert AuctionNotYetEnded();
}
--- SKIP ---
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 12 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.