Decent Currant Halibut
Medium
In the ReputationMarket
contract, the buyVotes
function does not check if adding votes would exceed the safe operational limit set by the LMSR library (votes ≤ 133 * liquidityParameter)
. Although the LMSR library internally checks for this limit and reverts if exceeded, the absence of this check at the ReputationMarket
level can lead to unnecessary transaction failures, confusion, and wasted gas.
function buyVotes(
uint256 profileId,
bool isPositive,
uint256 maxVotesToBuy,
uint256 minVotesToBuy
) public payable whenNotPaused activeMarket(profileId) nonReentrant {
// ... (code not shown)
uint256 currentVotesToBuy = maxVotesToBuy;
while (totalCostIncludingFees > msg.value) {
currentVotesToBuy--;
// ... (code not shown)
}
// ... (code not shown)
}
No response
No response
No response
User Experience: Users might repeatedly attempt transactions that fail due to vote limits, leading to frustration and potential abandonment of the platform.
Gas Waste: Each failed transaction consumes gas, unnecessarily increasing costs for users and adding to network congestion. Market Efficiency: Markets might become temporarily inoperable if they consistently hit their vote cap without users understanding why their transactions are failing.
Security: While not directly exploitable, a lack of proactive error handling could be used in a denial-of-service context by initiating numerous failed transactions.
Here's a test scenario illustrating how a purchase might exceed the LMSR limit:
function test_BuyVotes_ExceedsLMSRLimit() public {
uint256 profileId = 1;
bool isPositive = true;
uint256 liquidityParameter = 1000; // Example liquidity parameter
uint256 maxSafeVotes = 133 * liquidityParameter; // Theoretical max safe votes
uint256 votesToBuy = maxSafeVotes + 1; // Attempt to buy one vote over the limit
// Mock market state
markets[profileId].liquidityParameter = liquidityParameter;
markets[profileId].votes[isPositive ? TRUST : DISTRUST] = maxSafeVotes; // Market already at limit
// Attempt to buy votes, expect revert
vm.expectRevert(abi.encodeWithSelector(VotesExceedSafeLimit.selector, maxSafeVotes + 1, liquidityParameter, maxSafeVotes));
buyVotes{value: 1 ether}(profileId, isPositive, votesToBuy, 1);
}
Implement a check in buyVotes
or _calcCost
to ensure the new vote count does not exceed the LMSR safe limit
function _calcCost(
Market memory market,
bool isPositive,
bool isBuy,
uint256 amount
) private pure returns (uint256 cost) {
uint256 maxSafeVotes = 133 * market.liquidityParameter;
uint256 currentVotes = isPositive ? market.votes[TRUST] : market.votes[DISTRUST];
uint256 newVotes = isBuy ? currentVotes + amount : currentVotes - amount;
if (newVotes > maxSafeVotes) {
revert VotesExceedSafeLimit(newVotes, market.liquidityParameter, maxSafeVotes);
}
// ... rest of the function ...
}
Or directly in buyVotes
:
function buyVotes(
uint256 profileId,
bool isPositive,
uint256 maxVotesToBuy,
uint256 minVotesToBuy
) public payable whenNotPaused activeMarket(profileId) nonReentrant {
_checkMarketExists(profileId);
uint256 maxSafeVotes = 133 * markets[profileId].liquidityParameter;
uint256 currentVotes = markets[profileId].votes[isPositive ? TRUST : DISTRUST];
if (currentVotes + maxVotesToBuy > maxSafeVotes) {
revert VotesExceedSafeLimit(currentVotes + maxVotesToBuy, markets[profileId].liquidityParameter, maxSafeVotes);
}
// ... rest of the function ...
}