Summary
After reviewing Auction.sol, I confirm a critical vulnerability exists where lack of time parameter validation can lead to permanent auction failure through division by zero.
Vulnerable Code
constructor(
address _zenoAddress,
address _usdcAddress,
address _businessAddress,
uint256 _startTime,
uint256 _endTime,
uint256 _startingPrice,
uint256 _reservePrice,
uint256 _totalAllocated,
address _initialOwner
) Ownable(_initialOwner) {
}
function getPrice() public view returns (uint256) {
return state.startingPrice - (
(state.startingPrice - state.reservePrice) *
(block.timestamp - state.startTime) /
(state.endTime - state.startTime)
);
}
Impact
Permanent Auction Failure
function brickAuction() {
new Auction(
zenoAddr,
usdcAddr,
businessAddr,
block.timestamp,
block.timestamp,
1e6,
1e6,
1000e18,
owner
);
auction.getPrice();
auction.buy(1);
}
Economic Impact
Proof of Concept
contract AuctionTest is Test {
function testDivisionByZero() public {
uint256 currentTime = block.timestamp;
vm.expectRevert();
Auction auction = new Auction(
address(zeno),
address(usdc),
address(business),
currentTime,
currentTime,
100e6,
90e6,
1000e18,
address(this)
);
auction.getPrice();
}
}
Recommended Mitigation
contract Auction is IAuction, Ownable {
uint256 public constant MIN_AUCTION_DURATION = 1 hours;
constructor(
address _zenoAddress,
address _usdcAddress,
address _businessAddress,
uint256 _startTime,
uint256 _endTime,
uint256 _startingPrice,
uint256 _reservePrice,
uint256 _totalAllocated,
address _initialOwner
) Ownable(_initialOwner) {
if (_startTime >= _endTime) revert InvalidAuctionTime();
if (_endTime - _startTime < MIN_AUCTION_DURATION) revert AuctionTooShort();
if (_startTime < block.timestamp) revert StartTimeInPast();
if (_startingPrice < _reservePrice) revert InvalidPrices();
if (_totalAllocated == 0) revert ZeroAllocation();
state = AuctionState({
startTime: _startTime,
endTime: _endTime,
startingPrice: _startingPrice,
reservePrice: _reservePrice,
totalAllocated: _totalAllocated,
totalRemaining: _totalAllocated,
lastBidTime: 0,
lastBidder: address(0)
});
emit AuctionInitialized(
_startTime,
_endTime,
_startingPrice,
_reservePrice,
_totalAllocated
);
}
}
Additional Recommendations
Add validation events:
event AuctionInitialized(
uint256 startTime,
uint256 endTime,
uint256 startingPrice,
uint256 reservePrice,
uint256 totalAllocated
);
Implement emergency pause:
bool public paused;
modifier whenNotPaused() {
require(!paused, "Auction: paused");
_;
}
Add safety checks in price calculation:
function getPrice() public view returns (uint256) {
if (block.timestamp < state.startTime) return state.startingPrice;
if (block.timestamp >= state.endTime) return state.reservePrice;
uint256 timeElapsed = block.timestamp - state.startTime;
uint256 duration = state.endTime - state.startTime;
assert(duration > 0);
return state.startingPrice - (
(state.startingPrice - state.reservePrice) *
timeElapsed /
duration
);
}