Summary
If no bid is made by users, allocated TGLD for that epoch will stuck forever and cannot be claimed. It will be rare scenerio but there should be way to recover TGLD for this.
Relevant links -
https://github.com/Cyfrin/2024-07-templegold/blob/main/protocol/contracts/templegold/DaiGoldAuction.sol
Vulnerability Details
DaiGoldAuction
auction contract set X amount when auction start for particular epoch. As shown below.
function startAuction() external override {
if (auctionStarter != address(0) && msg.sender != auctionStarter) { revert CommonEventsAndErrors.InvalidAccess(); }
EpochInfo storage prevAuctionInfo = epochs[_currentEpochId];
if (!prevAuctionInfo.hasEnded()) { revert CannotStartAuction(); }
AuctionConfig storage config = auctionConfig;
if (_currentEpochId > 0 && (prevAuctionInfo.endTime + config.auctionsTimeDiff > block.timestamp)) {
revert CannotStartAuction();
}
_distributeGold();
@> uint256 totalGoldAmount = nextAuctionGoldAmount;
nextAuctionGoldAmount = 0;
@> uint256 epochId = _currentEpochId = _currentEpochId + 1;
if (totalGoldAmount < config.auctionMinimumDistributedGold) { revert LowGoldDistributed(totalGoldAmount); }
EpochInfo storage info = epochs[epochId];
@> info.totalAuctionTokenAmount = totalGoldAmount;
uint128 startTime = info.startTime = uint128(block.timestamp) + config.auctionStartCooldown;
uint128 endTime = info.endTime = startTime + AUCTION_DURATION;
emit AuctionStarted(epochId, msg.sender, startTime, endTime, totalGoldAmount);
}
As highlighted above, particular amount is set for given epoch when auction begins. If auction has no bid, contract has no mechanism to recover the particular amount.
Current recover token mechanism only allow recovering TGLD before auction starts, check the highlighted code below-
function recoverToken(
address token,
address to,
uint256 amount
) external override onlyElevatedAccess {
if (to == address(0)) { revert CommonEventsAndErrors.InvalidAddress(); }
if (amount == 0) { revert CommonEventsAndErrors.ExpectedNonZero(); }
if (token != address(templeGold)) {
emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
IERC20(token).safeTransfer(to, amount);
return;
}
uint256 epochId = _currentEpochId;
EpochInfo storage info = epochs[epochId];
if (info.startTime == 0) { revert InvalidOperation(); }
@> if (info.isActive()) { revert AuctionActive(); }
@> if (info.hasEnded()) { revert AuctionEnded(); }
uint256 _totalAuctionTokenAmount = info.totalAuctionTokenAmount;
if (amount > _totalAuctionTokenAmount) { revert CommonEventsAndErrors.InvalidAmount(token, amount); }
delete epochs[epochId];
unchecked {
nextAuctionGoldAmount += _totalAuctionTokenAmount - amount;
}
emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
templeGold.safeTransfer(to, amount);
}
Impact
Although this will be rare scenario, lack of recover mechanism will be loss of funds to the protocol.
Tools Used
Manual Review
Recommendations
Add a recover mechanism to handle this edge case.
function recoverTokenFromZeroBidAuction(uint256 epoch, address to) external onlyElevatedAccess {
uint256 currentId = _currentEpochId;
if(epoch >= currentId) {revert InvalidOperation();}
EpochInfo storage info = epochs[epochId];
if(info.totalBidTokenAmount !=0 ){ revert InvalidOperation();}
uint256 claimAmount = info.totalAuctionTokenAmount;
info.totalAuctionTokenAmount = 0;
templeGold.safeTransfer(to, claimAmount);
}