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);
uint256 pointsToBurn = fjordPoints.balanceOf(address(this));
fjordPoints.burn(pointsToBurn);
}
pragma solidity =0.8.21;
import "../src/FjordStaking.sol";
import { FjordPoints } from "../src/FjordPoints.sol";
import { Test,Vm,console } from "forge-std/Test.sol";
import { MockERC20 } from "solmate/test/utils/mocks/MockERC20.sol";
import {AuctionFactory} from "../src/FjordAuctionFactory.sol";
import {FjordAuction} from "../src/FjordAuction.sol";
contract PocDust is Test {
AuctionFactory public auctionFactory;
FjordPointsWithMint public fjordPoints;
MockERC20 public auctionToken;
address public owner = makeAddr("owner");
uint256 auctionAmount = 12e18;
function setUp() public {
auctionToken = new MockERC20("ATOKEN","ATOKEN",18);
vm.startPrank(owner);
fjordPoints = new FjordPointsWithMint();
auctionFactory = new AuctionFactory(address(fjordPoints));
vm.stopPrank();
assertEq(owner,auctionFactory.owner());
assertEq(owner,fjordPoints.owner());
auctionToken.mint(owner,100e18);
assertEq(auctionToken.balanceOf(owner),100e18);
}
function test_FjordAuction_dust() public {
address auctionAddress = owner_createAuction();
uint256 bidAmount = 0.33e18;
for (uint256 i = 1; i < 33; ++i) {
FjordPointsWithMint(fjordPoints).mint(address(uint160(i)),bidAmount);
vm.startPrank(address(uint160(i)));
fjordPoints.approve(address(auctionAddress),bidAmount);
FjordAuction(auctionAddress).bid(bidAmount);
vm.stopPrank();
}
assertEq(FjordAuction(auctionAddress).totalBids(),bidAmount * 32);
assertEq(FjordAuction(auctionAddress).totalTokens(),auctionAmount);
vm.warp(11);
FjordAuction(auctionAddress).auctionEnd();
for (uint256 i = 1; i < 33; ++i) {
vm.prank(address(uint160(i)));
FjordAuction(auctionAddress).claimTokens();
}
if (auctionToken.balanceOf(address(auctionAddress)) > 0) {
console.log("Dust");
}
if (auctionToken.balanceOf(address(auctionAddress)) == 0) {
console.log("No dust");
}
assertEq(fjordPoints.balanceOf(address(auctionAddress)),0);
}
function owner_createAuction() public returns (address auctionAddress) {
vm.startPrank(owner);
auctionToken.approve(address(auctionFactory),auctionAmount);
vm.recordLogs();
auctionFactory.createAuction(address(auctionToken),10,auctionAmount,"");
vm.stopPrank();
Vm.Log[] memory logs = vm.getRecordedLogs();
for (uint256 i = 0; i < logs.length; i++) {
if (logs[i].topics[0] == keccak256("AuctionCreated(address)")) {
auctionAddress = address(uint160(uint256(logs[i].topics[1])));
break;
}
}
}
}
contract FjordPointsWithMint is FjordPoints {
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
}
If the auction tokens are not evenly divisible by the total bids, leftover token dust may remain in the FjordAuction
contract
function claimTokens() external {
if (!ended) {
revert AuctionNotYetEnded();
}
uint256 userBids = bids[msg.sender];
if (userBids == 0) {
revert NoTokensToClaim();
}
uint256 claimable = userBids.mul(multiplier).div(PRECISION_18);
bids[msg.sender] = 0;
auctionToken.transfer(msg.sender, claimable);
emit TokensClaimed(msg.sender, claimable);
+ totalBids = totalBids.sub(userBids);
+ if ( totalBids == 0 ) {
+ auctionToken.transfer(owner, auctionToken.balanceOf(address(this)));
+ }
}