Skip to content

Latest commit

 

History

History
82 lines (66 loc) · 2.65 KB

File metadata and controls

82 lines (66 loc) · 2.65 KB

Urban Obsidian Jay

Medium

yes + no price may not be one

yes + no price may not be one

Summary

In LMSR.sol::L70, TRUST_priceRatio and DISTRUST_priceRatio are all rounding down. Thus the sum of these values could be 1e18-1. As a result, ReputationMarket.sol::L1003, TRUST_VotePrice + DISTRUST_VotePrice could be less than market.basePrice.

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 Summary:

Are there any limitations on values set by admins (or other roles) in the codebase, including restrictions on array lengths?

  • ...
  • Must maintain LMSR invariant (yes + no price sum to 1)

However, the sum may not be one.

PoC

ReputationMarket.sol
    function _calcVotePrice(Market memory market, bool isPositive) private pure returns (uint256) {
        // odds are in a ratio of N / 1e18
        uint256 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 basePrice
        return
1003        odds.mulDiv(market.basePrice, 1e18, isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil);
    }

LMSR.sol
    function getOdds(
        uint256 yesVotes,
        uint256 noVotes,
        uint256 liquidityParameter,
        bool isYes
    ) public pure returns (uint256 ratio) {
        // 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);
    }

For example: Assuming that : basePrice := 0.01e18, TRUST_priceRatio := 0.9e18-1, DISTRUST_priceRatio :=0.1e18. At this point, - TRUST_VotePrice = floor(TRUST_priceRatio * basePrice / 1e18) = 0.9e16 - 1. - DISTRUST_VotePrice = ceil(DISTRUST_priceRatio * basePrice / 1e18) = 0.1e16. As a result, TRUST_VotePrice + DISTRUST_VotePrice = 1e16 - 1 = basePrice - 1.

Mitigation

-       UD60x18 priceRatio = isYes ? yesExp.div(sumExp) : noExp.div(sumExp);
+       UD60x18 priceRatio = isYes ? yesExp.div(sumExp) : 1e18 - yesExp.div(sumExp);