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

Post-auction bid exploit allows unfair reward distribution

Summary

After the end of the auction there is still an opportunity for a user to make a bid and get a bigger reward or even the whole reawrd of the auction if he match the totalBids.

Vulnerability Details

In the FjordAuction::bidand unbid functions it's checked if block.timestamp > auctionEndTimeand only if this is true, the function reverts with error AuctionAlreadyEnded. This means that if block.temstamp = auctionEndTimethe function wont revert and the user will be able to bid. In the same time the FjordAuction::auctionEndfunction the condition to revert the function is block.temstamp < auctionEndTimewhich also means that the function wont revert if block.temstamp = auctionEndTime. This allows a user to call both functions in the same block, first ending the auction and then biding. For example:* *

  • There is an auction with a price of 1 weth.

  • Ann bids 100 points.

  • Bob submits a transaction in a block where block.timestamp = auctionEndTime where he is calling three functions in the order below:

    • auctionEnd -> multiplier = 1e18(weth) * 1e18(precision) / 100(totalBids) = 1e34;

    • bid -> Bob bids 100 points;

    • claimTokens -> using the formula: claimable = userBids.mul(multiplier).div(PRECISION_18

      where claimable = 100 * 1e34 / 1e18 = 1e18which means Bob will be able to get the whole reward;

Impact

Some users can get the whole reward or a bigger part than they deserve, just by bidding after the end of the auction.

Tools Used

Manual Review

Recommendations

Make and additional check:

function bid(uint256 amount) external {
- if (block.timestamp > auctionEndTime) {
+ if (block.timestamp > auctionEndTime || ended) {
revert AuctionAlreadyEnded();
}
bids[msg.sender] = bids[msg.sender].add(amount);
totalBids = totalBids.add(amount);
fjordPoints.transferFrom(msg.sender, address(this), amount);
emit BidAdded(msg.sender, amount);
}

OR

Change the checks in the bid and unbid functions to:

function bid(uint256 amount) external {
- if (block.timestamp > auctionEndTime) {
+ if (block.timestamp >= auctionEndTime) {
revert AuctionAlreadyEnded();
}
bids[msg.sender] = bids[msg.sender].add(amount);
totalBids = totalBids.add(amount);
fjordPoints.transferFrom(msg.sender, address(this), amount);
emit BidAdded(msg.sender, amount);
}
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.