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

Small bidders will receive zero tokens due to precision loss in `FjordAuction::claimTokens`

Summary:

The claimTokens() function in the FjordAuction contract suffers from precision loss due to integer division, potentially resulting in small bidders receiving zero tokens despite their participation in the auction.

Vulnerability Details

The FjordAuction contract implements a token auction system where users can bid using FjordPoints and later claim auction tokens proportional to their bids. The token distribution mechanism is implemented in two steps:

  1. In the auctionEnd() function, a multiplier is calculated:

multiplier = totalTokens.mul(PRECISION_18).div(totalBids);
  1. In the claimTokens() function, the claimable amount for each user is calculated:

uint256 claimable = userBids.mul(multiplier).div(PRECISION_18);

The issue arises from the order of operations and the use of integer division in these calculations. When userBids is small relative to totalBids, the multiplication with multiplier might not be large enough to overcome the subsequent division by PRECISION_18, resulting in claimable being rounded down to zero.

This precision loss is particularly problematic for small bidders, who may end up receiving no tokens despite their participation in the auction. The problem is exacerbated when there's a large disparity between the smallest and largest bids.

Impact

Small bidders may receive zero tokens, effectively being excluded from the token distribution despite their participation. This unfair distribution undermines the integrity of the auction process and may discourage participation from smaller players in future auctions.

Moreover, the cumulative effect of this precision loss could result in a significant portion of the totalTokens remaining undistributed and potentially stuck in the contract.

Proof of Concept

Consider the following scenario:

  1. The auction is set up with totalTokens = 1,000,000 and PRECISION_18 = 1e18

  2. After the bidding period, totalBids = 1,000,000,000

  3. Alice, a small bidder, participates with userBids = 999

When Alice calls claimTokens(), the calculations proceed as follows:

  1. multiplier = (1,000,000 * 1e18) / 1,000,000,000 = 1e12

  2. claimable = (999 * 1e12) / 1e18 = 0.999 ≈ 0 (due to integer division)

As a result, Alice receives 0 tokens despite bidding 999 FjordPoints.

Tools Used

Manual review

Recommendation

To address this issue, the order of operations in the claimTokens() function should be changed to minimize precision loss. Instead of using the pre-calculated multiplier, the function should directly calculate the proportion of totalTokens based on the user's bid:

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);
+ uint256 claimable = userBids.mul(totalTokens).div(totalBids);
bids[msg.sender] = 0;
auctionToken.transfer(msg.sender, claimable);
emit TokensClaimed(msg.sender, claimable);
}

This change ensures that every bidder, regardless of the size of their bid, receives a fair proportion of tokens based on their contribution to the total bids. While there may still be some dust due to integer division, this approach significantly reduces the likelihood of small bidders receiving zero tokens.

Updates

Lead Judging Commences

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

Low decimal tokens or super small bids can lead to 0 claims

Support

FAQs

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