FjordAuction::constructor allows empty auction tokens leading bidders to loss their fjord tokens because there is no check on constructor parameter totalTokens
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
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.
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 {
auction = new FjordAuction(
address(fjordPoints), address(auctionToken), biddingTime, 0
);
deal(address(auctionToken), address(auction), totalTokens);
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));
vm.startPrank(bidder);
fjordPoints.approve(address(auction), bidAmount);
auction.bid(bidAmount);
vm.stopPrank();
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)));
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);
}
}