Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unique Currant Sheep - Incremental Cost Calculation for Voting Markets Using LMSR" #127

Open
sherlock-admin4 opened this issue Dec 30, 2024 · 0 comments

Comments

@sherlock-admin4
Copy link
Contributor

Unique Currant Sheep

High

Incremental Cost Calculation for Voting Markets Using LMSR"

Summary

In the current implementation of the buyVotes function within the smart contract, the process for purchasing votes does not properly account for the incremental increase in price as more votes are bought. The contract currently calculates the cost for a given number of votes (e.g., 8 votes) as if the price for all the votes is fixed, which leads to inaccurate cost calculations, particularly in markets where the price of votes increases as more votes are bought. This report identifies the issue, explains why it occurs, and provides a recommended solution to correct the logic.
The primary issue identified in the buyVotes function is that users are paying the same price for each vote, even though the expected behavior (according to the LMSR pricing model) is that the price should increase as more votes are bought.

Root Cause

Simplified Cost Calculation: The function _calcCost computes the cost for purchasing the total number of votes (e.g., 8 votes) without taking into account how the market conditions change with each additional vote purchased.
Fixed Price Assumption: The price is treated as constant for all votes, ignoring the fact that the cost per vote typically increases as more votes are purchased. This results in a miscalculation of the total cost, as the price is not adjusted to reflect the updated state of the market after each vote purchase.

LMSR is wrong


The relevant function used for cost calculation is _cost(), which implements the LMSR formula. This formula computes the cost based on the current number of "yes" and "no" votes, and the liquidity parameter (b). However, the calculation is done for the total number of votes, rather than incrementally for each vote as it is purchased.
The original code calculates the total cost by considering the initial state and the final state using the LMSR formula, without breaking it down into individual vote increments.

https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L440-L497

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

No response

Impact

Incorrect Pricing: The primary impact of this issue is that users will be able to purchase more votes than they can afford. This is because the contract assumes the price is fixed and does not adjust for the increasing price of votes. As a result, users may be charged less than they should be, and the market may not receive the correct amount of funds for the votes purchased.

Market Inaccuracy: The market state will not be updated correctly when multiple votes are bought. This means that the overall liquidity of the market is not accurately reflected, which could lead to issues with market balance, such as incorrect vote distribution and a mismatch between the number of votes bought and the available liquidity.

Potential Exploitation: In some scenarios, this could lead to users exploiting the pricing logic by purchasing a larger number of votes for less than the true cost, impacting the fairness and integrity of the vote-buying system.

PoC

Problem
You want to calculate the price of each individual vote and then sum them up. For each vote added, the price should be recalculated, as the price of votes changes with each increment.

Current Approach in Original Code
The original code calculates the cost between two states (the initial and the final) using the LMSR formula. This is done in a single calculation, not accounting for the incremental change in price for each vote.

Desired Approach
Instead of calculating the price in one go, we want to calculate the price for each vote individually and then accumulate the cost over all votes.

Steps in the Proof of Concept
Initialize starting state:

Begin with 0 yesVotes and 0 noVotes.
For each vote:

For every vote, you need to calculate the price change based on the current state (i.e., the number of yesVotes and noVotes at that point).
Accumulate cost:

After calculating the price for a vote, add it to the total cost.
Repeat until all votes are processed:

Each time you add a new vote, you need to recalculate the price based on the new number of yesVotes and noVotes.
Example Breakdown
Variables:
yesVotes: Number of "yes" votes.
noVotes: Number of "no" votes.
cost: Total accumulated cost.
Process:
Start with 0 votes:

yesVotes = 0
noVotes = 0
cost = 0
For each vote (i.e., vote 1, vote 2, ..., vote N):

For vote 1: Calculate the cost for 1 "yes" vote (yesVotes = 1 and noVotes = 0).
Use LMSR to calculate the price of 1 "yes" vote.
Add the calculated price to the total cost.
For vote 2: Now calculate the cost for 1 "yes" and 1 "no" vote (yesVotes = 1 and noVotes = 1).
Use LMSR to calculate the price with this new state.
Add this price to the total cost.
Repeat the process for each subsequent vote.
Result: The total cost will be the sum of the prices calculated for each individual vote.

Mitigation

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant