Narrow Lipstick Moose
Medium
The sellVotes
function in the contract calculates the price per vote during a vote sell transaction by dividing proceedsBeforeFees
by votesToSell
. However, this calculation uses Solidity's default integer division, which truncates fractional results (rounding down), potentially leading to inaccurate price calculations. This rounding issue could lead to smaller than expected sale proceeds for the seller and misalignments in the vote price, especially in markets where fractional values are significant.
The issue can be observed here: https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L539-L578
function sellVotes(
uint256 profileId,
bool isPositive,
uint256 votesToSell,
uint256 minimumVotePrice
) public whenNotPaused activeMarket(profileId) nonReentrant {
_checkMarketExists(profileId);
(uint256 proceedsBeforeFees, uint256 protocolFee, uint256 proceedsAfterFees) = _calculateSell(
markets[profileId],
profileId,
isPositive,
votesToSell
);
uint256 pricePerVote = votesToSell > 0 ? proceedsBeforeFees / votesToSell : 0;
if (pricePerVote < minimumVotePrice) {
revert SellSlippageLimitExceeded(minimumVotePrice, pricePerVote);
}
markets[profileId].votes[isPositive ? TRUST : DISTRUST] -= votesToSell;
votesOwned[msg.sender][profileId].votes[isPositive ? TRUST : DISTRUST] -= votesToSell;
// tally market funds
marketFunds[profileId] -= proceedsBeforeFees;
// apply protocol fees
applyFees(protocolFee, 0, profileId);
// send the proceeds to the seller
_sendEth(proceedsAfterFees);
emit VotesSold(
profileId,
msg.sender,
isPositive,
votesToSell,
proceedsAfterFees,
block.timestamp
);
_emitMarketUpdate(profileId);
}
Here is the line:
uint256 pricePerVote = votesToSell > 0 ? proceedsBeforeFees / votesToSell : 0; //@audit
This division performs integer division, which truncates decimal values. This results in rounding down instead of rounding to the nearest integer. When calculating the price per vote, the function ignores fractional parts of the price, which may lead to a smaller price being applied than intended.
No response
No response
No response
Sellers receive less than expected from the sale of votes due to the rounding down of fractional values. Example: If the proceeds before fees are 5 ETH and votesToSell = 3, the price per vote should be 1.666... ETH, but it will be truncated to 1 ETH instead, resulting in a loss of 0.666... ETH per vote for the seller.
Let’s assume:
proceedsBeforeFees
= 5 ETH (5e18 Wei)
votesToSell
= 3 (3 votes to sell)
minimumVotePrice
= 1 ETH
When proceedsBeforeFees
is divided by votesToSell
:
pricePerVote
= 5 ETH / 3 = 1.6666666... ETH
However, Solidity will perform integer division, so the result will be truncated:
pricePerVote
= 1 ETH (due to truncation of fractional values)
Thus, even though the exact price should have been 1.666... ETH, the actual pricePerVote is 1 ETH, and this could cause the seller to receive less than expected.
Implement the use of mulDiv
with the appropriate rounding mode or enhance the price calculation with explicit rounding logic to ensure that rounding errors do not impact the fairness or integrity of vote transactions.