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

The auction tokens will be stuck in `FjordAuctionFactory.sol` if no one bids

Summary

The auction tokens will be stuck in FjordAuctionFactory.sol if no one bids.

Vulnerability Details

FjordAuction.sol::auctionEnd

function auctionEnd() external {
if (block.timestamp < auctionEndTime) {
revert AuctionNotYetEnded();
}
if (ended) {
revert AuctionEndAlreadyCalled();
}
ended = true;
emit AuctionEnded(totalBids, totalTokens);
@ if (totalBids == 0) {
@ auctionToken.transfer(owner, totalTokens);
@ return;
}
multiplier = totalTokens.mul(PRECISION_18).div(totalBids);
// Burn the FjordPoints held by the contract
uint256 pointsToBurn = fjordPoints.balanceOf(address(this));
fjordPoints.burn(pointsToBurn);
}

From code above, we can conclude if no one bids the auction tokens will be transfered to owner of FjordAuction.sol.
FjordAuction.sol::constructor

constructor(
address _fjordPoints,
address _auctionToken,
uint256 _biddingTime,
uint256 _totalTokens
) {
if (_fjordPoints == address(0)) {
revert InvalidFjordPointsAddress();
}
if (_auctionToken == address(0)) {
revert InvalidAuctionTokenAddress();
}
fjordPoints = ERC20Burnable(_fjordPoints);
auctionToken = IERC20(_auctionToken);
@ owner = msg.sender;
auctionEndTime = block.timestamp.add(_biddingTime);
totalTokens = _totalTokens;
}

From code above, the owner of FjordAuction.sol is the depolyer of FjordAuction.sol.
FjordAuctionFactory.sol::createAuction

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);
}

From code above, FjordAuctionFactory.sol deploys FjordAuction.sol. So the owner of FjordAuction.sol is FjordAuctionFactory.sol. If no one bids, the auction tokens will be transfered to FjordAuctionFactory.sol. However FjordAuctionFactory.sol has no function to tranfer tokens. The auction tokens will be stuck in FjordAuctionFactory.sol forever.

Impact

The auction tokens will be stuck in FjordAuctionFactory.sol forever if no one bids.

POC

Paste code below into test/unit/auction.t.sol.

function testAuctionEndFundStuckInAuctionFactory() public {
skip(biddingTime);
auction.auctionEnd();
// fund is stuck in auction factory
// assume auction factory == address(this)
assertEq(auctionToken.balanceOf(address(this)), totalTokens);
}

Tools Used

manual and foundry

Recommendations

Add a function with access control to transfer tokens out.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year 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.