DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: medium
Valid

Auction tokens will permanently stuck in `FjordAuctionFactory` if an auction ends with no bids

Vulnerability Details

The FjordAuction contract facilitates auctions where users bid using FjordPoints to acquire auction tokens. If an auction concludes with no bids, the auction tokens are transferred to the owner:

function auctionEnd() external {
// --SNIP
ended = true;
emit AuctionEnded(totalBids, totalTokens);
if (totalBids == 0) {
>>> auctionToken.transfer(owner, totalTokens);
return;
}
// --SNIP
}

In the code above, the tokens are transferred to the owner. However, the issue is that the owner is set to the FjordAuctionFactory contract during deployment, as it is the one that creates the auction contracts. The problem is that the FjordAuctionFactory contract, which receives the tokens, has no mechanism to utilize them because creating a new auction always requires transferring tokens from the factory's owner:

function createAuction(
address auctionToken,
uint256 biddingTime,
uint256 totalTokens,
bytes32 salt
) external onlyOwner {
address auctionAddress = address(
new FjordAuction{ salt: salt }(fjordPoints, auctionToken, biddingTime, totalTokens)
);
// Transfer the auction tokens from the msg.sender to the new auction contract
>>> IERC20(auctionToken).transferFrom(msg.sender, auctionAddress, totalTokens);
emit AuctionCreated(auctionAddress);
}

As a result, any auction tokens sent back to the factory contract after an auction ends with no bids become stuck and cannot be reused or recovered.

Impact

If an auction concludes with no bids, the auction tokens are permanently stuck in the FjordAuctionFactory contract. This renders the tokens unusable.

Tools Used

Manual Review

Recommendations

Consider either transferring the auction tokens to the owner wallet instead of the factory, or utilize them in the createAuction as suggested below:

function createAuction(
address auctionToken,
uint256 biddingTime,
uint256 totalTokens,
bytes32 salt
) external onlyOwner {
address auctionAddress = address(
new FjordAuction{ salt: salt }(fjordPoints, auctionToken, biddingTime, totalTokens)
);
// Transfer the auction tokens from the msg.sender to the new auction contract
+ uint factoryBalance = IERC20(auctionToken).balanceOf(address(this));
+ if( factoryBalance > 0) {
+ if (factoryBalance >= totalTokens) IERC20(auctionToken).transferFrom(address(this), auctionAddress, totalTokens);
+ else IERC20(auctionToken).transferFrom(msg.sender, auctionAddress, totalTokens - factoryBalance);
+ }else {
+ IERC20(auctionToken).transferFrom(msg.sender, auctionAddress, totalTokens);
+ }
- IERC20(auctionToken).transferFrom(msg.sender, auctionAddress, totalTokens);
emit AuctionCreated(auctionAddress);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

If no bids are placed during the auction, the `auctionToken` will be permanently locked within the `AuctionFactory`

An auction with 0 bids will get the `totalTokens` stuck inside the contract. Impact: High - Tokens are forever lost Likelihood - Low - Super small chances of happening, but not impossible

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.