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

FjordAuction::constructor allows empty auction tokens leading bidders to loss their fjord tokens

Summary

FjordAuction::constructor allows empty auction tokens leading bidders to loss their fjord tokens because there is no check on constructor parameter totalTokens

Vulnerability Details

FjordAuction::constructor doesn't have check for totalTokens parameter value, allowing it to have zero value, so all bidders that participate in the auction will receive 0 auction tokens, ie, burning all the bidded fjord tokens for nothing in return

constructor(
address _fjordPoints,
address _auctionToken,
uint256 _biddingTime,
uint256 _totalTokens
) {
//... snippet
totalTokens = _totalTokens;
}

The following proof of concept simulates an user bidding for a created auction with 0 auction tokens and when auction finished all his fjord tokens will be burned and will receive 0 auction tokens.

Create test file as emptyauction.t.sol in test/unit/ directory

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity =0.8.21;
import "forge-std/Test.sol";
import "src/FjordAuction.sol";
import { ERC20BurnableMock } from "../mocks/ERC20BurnableMock.sol";
import { SafeMath } from "lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol";
import { ERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import { ERC20Burnable } from
"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol";
contract TestEmptyAuction is Test {
using SafeMath for uint256;
FjordAuction public auction;
ERC20BurnableMock3 public fjordPoints;
ERC20BurnableMock public auctionToken;
address public owner = address(0x1);
uint256 public biddingTime = 1 weeks;
uint256 public totalTokens = 1000 ether;
function setUp() public {
fjordPoints = new ERC20BurnableMock3("FjordPoints", "fjoPTS");
auctionToken = new ERC20BurnableMock("AuctionToken", "AUCT");
}
function testEmptyAuctionFjordBurn() public {
// CREATE FJORD AUCTION
auction = new FjordAuction(
address(fjordPoints), address(auctionToken), biddingTime, 0
);
deal(address(auctionToken), address(auction), totalTokens);
// BIDDER CREATION
address bidder = address(0x2);
uint256 bidAmount = 100 ether;
vm.prank(address(fjordPoints));
fjordPoints.mint(bidder, bidAmount);
console.log("=========== Start ============");
console.log("fjordPoints.balanceOf(bidder) ",fjordPoints.balanceOf(bidder));
console.log("fjordPoints.totalSupply() ",fjordPoints.totalSupply());
console.log("auctionToken.balanceOf(bidder) ",auctionToken.balanceOf(bidder));
// BIDDER BIDS
vm.startPrank(bidder);
fjordPoints.approve(address(auction), bidAmount);
auction.bid(bidAmount);
vm.stopPrank();
// CHECK EFFECTS OF BIDS
console.log("=========== After bidding ============");
console.log("auction.bids(bidder) ",auction.bids(bidder));
console.log("fjordPoints.balanceOf(bidder) ",fjordPoints.balanceOf(bidder));
console.log("fjordPoints.balanceOf(address(auction)) ",fjordPoints.balanceOf(address(auction)));
// SKIP TIME & END AUCTION TO CLAIM TOKENS
console.log("=========== Ending Auction ============");
skip(biddingTime);
auction.auctionEnd();
vm.prank(bidder);
console.log("=========== Claiming Auction Tokens ============");
auction.claimTokens();
console.log("=========== After Claiming Tokens ============");
console.log("fjordPoints.balanceOf(bidder) ",fjordPoints.balanceOf(bidder));
console.log("fjordPoints.totalSupply() ",fjordPoints.totalSupply());
console.log("auctionToken.balanceOf(bidder) ",auctionToken.balanceOf(bidder));
}
}
contract ERC20BurnableMock3 is ERC20, ERC20Burnable {
constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) { }
function mint(address account, uint256 amount) public virtual {
_mint(account,amount);
}
}

Execute test file with

forge test --mt testEmptyAuctionFjordBurn

Observe user that bids lost all of fjord tokens and receive 0 auction tokens

Impact

Loss of funds, fjord tokens

Tools Used

Manual Review

Recommendations

Implement a check to not allow empty auction tokens

+ error InvalidAmount();
constructor(
address _fjordPoints,
address _auctionToken,
uint256 _biddingTime,
uint256 _totalTokens
) {
//snippet
+ if (_totalTokens == 0) revert InvalidAmount();
totalTokens = _totalTokens;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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