Description
The DaiGoldAuction::recoverToken
function allows privileged accounts to recover ERC20 tokens from the smart contract, including TempleGold, if the auction has not started. However, since startTime
is only set at the beginning of an auction, it will never satisfy the condition if (info.startTime == 0)
. Furthermore, due to other conditions that become true once the auction starts or ends, it becomes impossible to recover any TempleGold. Currently, the only way to bypass these conditions is during the cooldown period, if it is not set to zero.
@> * @notice Recover auction tokens for last but not started auction.
...
*/
function recoverToken(
...
) external override onlyElevatedAccess {
...
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();
}
...
templeGold.safeTransfer(to, amount);
}
Risk
Likelyhood: High
Impact: High
Proof of Concept
Foundry PoC to add in DaiGoldAuction.t.sol
function test_failToRecoverToken_TempleGold() public {
_setVestingFactor(templeGold);
skip(1 days);
vm.startPrank(executor);
templeGold.mint();
uint256 mintAmount = templeGold.balanceOf(address(daiGoldAuction));
vm.expectRevert(
abi.encodeWithSelector(IAuctionBase.InvalidOperation.selector)
);
daiGoldAuction.recoverToken(address(templeGold), alice, mintAmount);
IDaiGoldAuction.AuctionConfig memory _config = _getAuctionConfig();
daiGoldAuction.setAuctionConfig(_config);
_startAuction();
IAuctionBase.EpochInfo memory info = daiGoldAuction.getEpochInfo(1);
vm.warp(info.startTime);
vm.expectRevert(
abi.encodeWithSelector(IAuctionBase.AuctionActive.selector)
);
daiGoldAuction.recoverToken(address(templeGold), alice, mintAmount);
vm.warp(info.endTime);
templeGold.mint();
vm.expectRevert(abi.encodeWithSelector(IAuctionBase.AuctionEnded.selector));
daiGoldAuction.recoverToken(address(templeGold), alice, mintAmount);
vm.warp(info.startTime - 10 seconds);
mintAmount = info.totalAuctionTokenAmount;
daiGoldAuction.recoverToken(address(templeGold), alice, mintAmount);
vm.stopPrank();
}
Recommended Mitigation
Remove the following condition :
- if (info.startTime == 0) {
- revert InvalidOperation();
- }
If recovery during the cooldown period is not intended, also check the block.timestamp
to prevent it.