There exists a race condition in the FjordAuction contract where the auction end time and bidding process can overlap at block.timestamp == auctionEndTime
:
If the auctionEnd()
function is called at the exact auctionEndTime
and a user calls bid()
at the same timestamp, the fjordPoints from the bid won't be accounted for in the multiplier calculation:
This would lead to an inaccurate distribution of tokens and allow the attacker to claim more tokens than they should.
Attacker can game the auction and claim more tokens than they should, stealing tokens from other users.
Setup is totalTokens
= 1000, totalBids
= 1000
Attacker calls auctionEnd()
at block.timestamp == auctionEndTime
multiplier is calculated as 1000 * 1e18 / 1000 = 1e18
Attacker calls bid()
at the exact block.timestamp == auctionEndTime
with amount = 1000
multiplier should be 1000 * 1e18 / 2000 = 0.5e18, but it has already been calculated as 1e18
Attacker claims their tokens with claimTokens()
Attacker gets 1000 * 1e18 = 1000 tokens
But should gets only 1000 * 0.5e18 = 500 tokens
Modify the bid()
and unbid()
functions to check if block.timestamp >= auctionEndTime
instead of block.timestamp > auctionEndTime
.
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.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.