You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
... with both prices always summing to the market's basePrice.
However, the sum may less than basePrice.
PoC
ReputationMarket.sol
function _calcVotePrice(Market memorymarket, boolisPositive) privatepurereturns (uint256) {
// odds are in a ratio of N / 1e18uint256 odds = LMSR.getOdds(
market.votes[TRUST],
market.votes[DISTRUST],
market.liquidityParameter,
isPositive
);
// multiply odds by base price to get price; divide by 1e18 to get price in wei// round up for trust, down for distrust so that prices always equal basePricereturn1003: odds.mulDiv(market.basePrice, 1e18, isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil);
}
LMSR.sol
function getOdds(
uint256yesVotes,
uint256noVotes,
uint256liquidityParameter,
boolisYes
) publicpurereturns (uint256ratio) {
// Compute exponentials e^(yes/b) and e^(no/b)
(UD60x18 yesExp, UD60x18 noExp) =_getExponentials(yesVotes, noVotes, liquidityParameter);
// sumExp = e^(yes/b) + e^(no/b)
UD60x18 sumExp = yesExp.add(noExp);
// priceRatio = e^(yes/b)/(sumExp) if isYes, else e^(no/b)/(sumExp)70: UD60x18 priceRatio = isYes ? yesExp.div(sumExp) : noExp.div(sumExp);
// Unwrap to get scaled ratio
ratio =unwrap(priceRatio);
}
Because both getOdds(,,,1) and getOdds(,,,0) are rounded down, getOdds(,,,1) + getOdds(,,,0) may less than 1e18. calcVotePrice(,1) = floor(getOdds(,,,1) * basePrice / 1e18) calcVotePrice(,0) = ceil(getOdds(,,,0) * basePrice / 1e18)
Consequently, calcVotePrice(,1) + calcVotePrice(,0) may less than market.basePrice.
At least, if getOdds(,,,0) * basePrice % 1e18 := 0:
calcVotePrice(,1) + calcVotePrice(,0) =
= floor(getOdds(,,,1) * basePrice / 1e18) + (getOdds(,,,0) * basePrice / 1e18) <=
<= ( getOdds(,,,1) + getOdds(,,,0) ) * basePrice / 1e18 <=
<= (1e18-1) * basePrice / 1e18 < basePrice.
Let's examine how the sum is less than basePrice.
Assume: liquidity := 1000, basePrice := 0.01e18 + 3e14, and marketFunds = 0.2e18.
Regarding votes[0] and votes[1]: votes[0] := 61 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 102 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 349 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 395 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 461 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 539 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 621 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 651 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 898 votes[1] := 500 sumPrice = 10299999999999999 votes[0] := 939 votes[1] := 500 sumPrice = 10299999999999999
...
In these cases, sumPrice < basePrice.
Magnificent Tortilla Eel
Medium
TRUST + DISTRUST Price May Not Equal One
Summary
The condition
getOdds(isYes) + getOdds(isNo) < 1
may occur, leading to theTRUST price + DISTRUST price
being less than one.Root Cause
https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/utils/LMSR.sol#L70
Internal pre-conditions
N/A
External pre-conditions
N/A
Attack Path
N/A
Impact
In Details:
In ReputationMarket.sol:
However, the sum may less than basePrice.
PoC
Because both
getOdds(,,,1)
andgetOdds(,,,0)
are rounded down,getOdds(,,,1) + getOdds(,,,0)
may less than1e18
.calcVotePrice(,1) = floor(getOdds(,,,1) * basePrice / 1e18)
calcVotePrice(,0) = ceil(getOdds(,,,0) * basePrice / 1e18)
Consequently,
calcVotePrice(,1) + calcVotePrice(,0)
may less thanmarket.basePrice
.At least, if
getOdds(,,,0) * basePrice % 1e18 := 0
:calcVotePrice(,1) + calcVotePrice(,0) =
= floor(getOdds(,,,1) * basePrice / 1e18) + (getOdds(,,,0) * basePrice / 1e18) <=
<= ( getOdds(,,,1) + getOdds(,,,0) ) * basePrice / 1e18 <=
<= (1e18-1) * basePrice / 1e18 < basePrice.
Let's examine how the sum is less than basePrice.
Assume:
liquidity := 1000
,basePrice := 0.01e18 + 3e14
, andmarketFunds = 0.2e18
.Regarding votes[0] and votes[1]:
votes[0] := 61 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 102 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 349 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 395 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 461 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 539 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 621 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 651 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 898 votes[1] := 500 sumPrice = 10299999999999999
votes[0] := 939 votes[1] := 500 sumPrice = 10299999999999999
...
In these cases, sumPrice < basePrice.
Here is the Python code used for testing:
Mitigation
The text was updated successfully, but these errors were encountered: