Skip to content

Latest commit

 

History

History
82 lines (56 loc) · 5.44 KB

015.md

File metadata and controls

82 lines (56 loc) · 5.44 KB

Salty Bone Skunk

High

Lack of similar data types in NounsAuctionHouseV2::createBid() and NounsAuctionHouseV3::createBid() will permanently break the auction bidding functionality for all users when block.timestamp exceeds the maximum value of uint40 (approximately 34 years)

Summary

The lack of similar data types in the function createBid() in NounsAuctionHouseV3.sol and in NounsAuctionHouseV2.sol will cause the require statement

require(block.timestamp < _auction.endTime, 'Auction expired');

to permanently fail when block.timestamp is legitimately greater than _auction.endTime. This is due to a type mismatch between block.timestamp (which is always uint256) and the auction's endTime (which is defined as a uint40), causing all bid attempts to revert when the timestamp exceeds the maximum value of uint40 which is ~34 years.

Root Cause

_auction.endTime in INounsAuctionHouseV3.sol and in INounsAuctionHouseV2.sol is a uint40 digit meant to indicate the time that an auction is scheduled to end. Each NFT auction ends at a different time, i.e noun 1's auction ended at a different time that noun 30's auction. block.timestamp will always be a uint256 digit.

For that matter, _auction.endTime has to be adjusted for every new Noun NFT to reflect the time when that NFT auction will end. It will reach a time in the future when block.timestamp will be greater than _auction.endTime as block.timestamp increases with every new block added to the blockchain. Consider an NFT in the future whose _auction.endTime is type(uint40).max and the block.timestamp is type(uint40).max + 100. The problem is that the require statement in #L155 in NounsAuctionHouseV3.sol and also in #L139 in NounsAuctionHouseV2.sol will fail cause block.timestamp will be greater than _auction.endTime.

require(block.timestamp < _auction.endTime, 'Auction expired');

This means the contract breaks for every single person as no one can create a bid and consequently no bids can be settled rendering the protocol useless for everyone.

Internal pre-conditions

No response

External pre-conditions

  • The ethereum blockchain has progressed to a block timestamp greater than type(uint40).max, i.e ~34 years.

Attack Path

  • Wait for ethereum's blockchain timestamp to exceed type(uint40).max which is approximately 34 years.
  • Attempt to create a bid by calling NounsAuctionHouseV2::createBid() or NounsAuctionHouseV3::createBid() for an NFT whose scheduled time to end is type(uint40.max).
  • The transaction will consistently revert due to timestamp comparison, i.e require(block.timestamp < _auction.endTime, 'Auction expired'); will always fail.

Impact

  • Complete denial of service for the Nouns DAO auction mechanism.
  • Permanent inability to create auctions.
  • The protocol becomes unusable for everyone.
  • Breaks the One Noun, Every Day, Forever mantra of the Nouns DAO as NFT bidding creation will stop after type(uint40).max (approximately 34 years).

PoC

No response

Mitigation

  1. Change _auction.endTime to use a uint256 data type, i.e change to the following in INounsAuctionHouseV2.sol and in INounsAuctionHouseV3.sol
    struct AuctionV2 {
        // ID for the Noun (ERC721 token ID)
        uint96 nounId;
        // ID of the client that facilitated the latest bid, used for client rewards
        uint32 clientId;
        // The current highest bid amount
        uint128 amount;
        // The time that the auction started
        uint40 startTime;
        // The time that the auction is scheduled to end
        uint256 endTime;    /// @audit-tag change from uint40 to uint256
        // The address of the current highest bid
        address payable bidder;
        // Whether or not the auction has been settled
        bool settled;
    }
  1. Or constrain block.timestamp to a uint40 in the require statement, i.e
require(uint40(block.timestamp) < _auction.endTime, 'Auction expired');