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

Block Timestamp Manipulation Enables Post-Auction Bidding & Lack of Automatic Auction Closure

Summary

The FjordAuction contains a critical vulnerability which allows malicious actors(miners) to manipulate the block.timestamp, effectively allowing them to place bids after the official end time of the auction. This potentially leads to unfair advantages and incorrect distributions of tokens.And FjordAuction::auctionEnd function has to be called manually to end the auction ending process.

Vulnerability Details

The issue lies in both FjordAuction::bid and FjordAuction:: auctionEnd functions

function bid(uint256 amount) external {
// @issue : block.timestamp gets manipulated
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);
}
// @issue : auction end is not automatic
// @issue : even though the `bid` function stops bidding after auction end time, but -
// `bid` function relies on block.timestamp which can be manipulated.
function auctionEnd() external {
if (block.timestamp < auctionEndTime) {
revert AuctionNotYetEnded();
}
if (ended) {
revert AuctionEndAlreadyCalled();
}
ended = true;
emit AuctionEnded(totalBids, totalTokens);
if (totalBids == 0) {
auctionToken.transfer(owner, totalTokens);
return;
}
multiplier = totalTokens.mul(PRECISION_18).div(totalBids);
// Burn the FjordPoints held by the contract
uint256 pointsToBurn = fjordPoints.balanceOf(address(this));
fjordPoints.burn(pointsToBurn);
}

Impact

  1. Fairness of the auction process is compromised.

  2. Potential for unfair advantage in the auction.

  3. Risk of incorrect distribution of tokens after the auction.

  4. Loss of confidence in the integrity of the auction system.

Tools Used

Manual Review

Recommendations

Recommendation 1:

use chainlink keepers to automatically end the auction end process.

Recommendation 2:

use block.number instead of block.timestamp

+ uint256 public auctionEndBlock;
+ event AuctionStarted(uint256 durationInBlocks);
+ function startAuction(uint256 durationInBlocks) external onlyOwner{
+ auctionEndBlock = block.number + durationInBlocks;
+ emit AuctionStarted(durationInBlocks);
+ }
- function bid(uint256 amount) external {
+ function bid(uint256 amount) external ReentrancyGuard {
- if (block.timestamp > auctionEndTime) {
+ if (block.number > auctionEndBlock) { // this calls the auction end and stops the auction
+ auctionEnd();
+ return;
- 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);
}
- function auctionEnd() external {
+ function auctionEnd() external ReentrancyGuard {
- if (block.timestamp < auctionEndTime) {
+ if (block.number < auctionEndBlock) {
revert AuctionNotYetEnded();
}
if (ended) {
revert AuctionEndAlreadyCalled();
}
ended = true;
emit AuctionEnded(totalBids, totalTokens);
if (totalBids == 0) {
auctionToken.transfer(owner, totalTokens);
return;
}
multiplier = totalTokens.mul(PRECISION_18).div(totalBids);
// Burn the FjordPoints held by the contract
uint256 pointsToBurn = fjordPoints.balanceOf(address(this));
fjordPoints.burn(pointsToBurn);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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