Fluffy Charcoal Toad
High
Missing slippage validation on final vote count will cause financial losses for users as attackers can front-run large trades to force unfavorable execution prices. buyVotes
decreases votes until they fit within msg.value without checking minVotesToBuy.
In ReputationMarket.sol:buyVotes
there is a missing validation check between the final number of votes purchased and the user's specified minimum:
// if the cost is greater than the maximum votes to buy,
// decrement vote count and recalculate until we identify the max number of votes they can afford
while (totalCostIncludingFees > msg.value) {
currentVotesToBuy--;
(purchaseCostBeforeFees, protocolFee, donation, totalCostIncludingFees) = _calculateBuy(
markets[profileId],
isPositive,
currentVotesToBuy
);
- User needs to submit a buyVotes transaction with maxVotesToBuy significantly higher than minVotesToBuy
- Transaction needs to include sufficient ETH for minVotesToBuy at current prices
- Market needs to have enough liquidity to execute large trades
- Network needs to have sufficient congestion to allow front-running
- Attacker monitors mempool for large buyVotes transactions
- When victim transaction detected with large maxVotesToBuy, attacker submits front-running trade with higher gas price
- Attacker's trade executes first, driving up market price
- Victim's transaction executes but receives fewer votes than minVotesToBuy due to price impact
- Attacker can then sell votes at higher price
Users suffer losses due to unfavorable trade execution, potentially receiving significantly fewer votes than their specified minimum.
function testSlippageExploit() public {
// Setup market
uint256 profileId = 1;
market.createMarket{value: 1 ether}();
// Victim transaction parameters
uint256 maxVotes = 1000;
uint256 minVotes = 800;
uint256 victimETH = 10 ether;
// Attacker front-runs
vm.prank(attacker);
market.buyVotes{value: 20 ether}(profileId, true, 2000);
// Victim transaction executes
vm.prank(victim);
market.buyVotes{value: victimETH}(profileId, true, maxVotes, minVotes);
// Check victim's received votes
uint256 actualVotes = market.getUserVotes(victim, profileId).trustVotes;
assertLt(actualVotes, minVotes); // Victim receives fewer votes than minimum
}
- Add slippage check before executing trade
- Add deadline parameter to prevent stale trades
- Implement price oracle with TWAP to reduce manipulation