Summary
While claiming the bid token, the user will get auction token. In SpiceAuction.sol
file.
In recoverToken
, we didn't update _totalAuctionTokenAllocation[token]
, but In recoverAuctionTokenForZeroBidAuction
, we update _totalAuctionTokenAllocation[auctionToken]
. It will affect user's claim amount calculation.
* @notice Recover auction tokens for last but not started auction
* @param token Token to recover
* @param to Recipient
* @param amount Amount to auction tokens
*/
function recoverToken(
address token,
address to,
uint256 amount
) external override onlyDAOExecutor {
...
if (amount > maxRecoverAmount) { revert CommonEventsAndErrors.InvalidParam(); }
>>
>> IERC20(token).safeTransfer(to, amount);
emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
}
* @notice Recover auction tokens for epoch with zero bids
* @param epochId Epoch Id
* @param to Recipient
*/
function recoverAuctionTokenForZeroBidAuction(uint256 epochId, address to) external override onlyDAOExecutor {
...
uint256 amount = epochInfo.totalAuctionTokenAmount;
>> _totalAuctionTokenAllocation[auctionToken] -= amount;
emit CommonEventsAndErrors.TokenRecovered(to, auctionToken, amount);
>> IERC20(auctionToken).safeTransfer(to, amount);
}
Vulnerability Details
In SpiceAuction, we calculate claimAmount like the below.
* @notice Claim (retro) rewards for an epoch . Cannot claim for a live epoch auction
* @param epochId Epoch to claim for
*/
function claim(uint256 epochId) external virtual override {
...
>> uint256 claimAmount = bidTokenAmount.mulDivRound(info.totalAuctionTokenAmount, info.totalBidTokenAmount, false);
...
}
But if we don't update _totalAuctionTokenAllocation[token] when we call recoverToken, it will affect the next auction's totalAuctionTokenAmount calculation.
* @notice Start auction. Checks caller is set config starter. Address zero for anyone to call
*/
function startAuction() external override {
...
(,address auctionToken) = _getBidAndAuctionTokens(config);
uint256 totalAuctionTokenAllocation = _totalAuctionTokenAllocation[auctionToken];
uint256 balance = IERC20(auctionToken).balanceOf(address(this));
>> uint256 epochAuctionTokenAmount = balance - (totalAuctionTokenAllocation - _claimedAuctionTokens[auctionToken]);
...
>> info.totalAuctionTokenAmount = epochAuctionTokenAmount;
_totalAuctionTokenAllocation[auctionToken] = totalAuctionTokenAllocation + epochAuctionTokenAmount;
emit AuctionStarted(epochId, msg.sender, startTime, endTime, epochAuctionTokenAmount);
}
So claimAmount
will be bigger than it should be.
Impact
The user will receive more auction token than user expected after DAO executor call recoverToken in the SpiceAuction
Tools Used
Manual review
Recommendations
In the SpiceAuction.sol, there is recoverToken function. We should add _totalAuctionTokenAllocation[token] -= amount;
/**
* @notice Recover auction tokens for last but not started auction
* @param token Token to recover
* @param to Recipient
* @param amount Amount to auction tokens
*/
function recoverToken(
address token,
address to,
uint256 amount
) external override onlyDAOExecutor {
...
if (amount > maxRecoverAmount) { revert CommonEventsAndErrors.InvalidParam(); }
+ _totalAuctionTokenAllocation[token] -= amount;
IERC20(token).safeTransfer(to, amount);
emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
}