Skip to content

Latest commit

 

History

History
94 lines (75 loc) · 3.39 KB

File metadata and controls

94 lines (75 loc) · 3.39 KB

Rich Charcoal Sardine

Medium

Incorrect rounding in the _calcCost function.

Summary

Rounding must be in favor of the protocol. In the _calcCost function, however, rounding is incorrectly applied, which could lead to a DoS of subsequent market actions.

Root Cause

In the _calcCost function, rounding is done based on isPositive, which is not in favor of the protocol. https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L1054-L1058

    cost = positiveCostRatio.mulDiv(
      market.basePrice,
      1e18,
      isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
    );

And, the marketFunds of the market is updated by using the cost. https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L480

    marketFunds[profileId] += purchaseCostBeforeFees;

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

    marketFunds[profileId] -= proceedsBeforeFees;

So, a bit bigger amount can be reduced from marketFunds, if a buyer buys more than one UNTRUST votes at once and sells them individually. Because rounding up is done only once when buying, but more than one times of rounding up are done when selling them. This may lead to a DoS for selling the last votes.

Consider the following scenario:

  1. A market is created with 0 createCost.
  2. Alice buys 10 UNTRUST votes at once.
  3. Alice sells all of them by selling one vote at a time.

As a result, selling the last vote of the market can be DoSed due to overflow from rounding error.

This DoS is possible in two kind of markets, where the initial marketFunds[profileId] is 0.

First, markets created by an ADMIN has no minimum creation cost. https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L341-L342

      // when an admin creates a market, there is no minimum creation cost; use whatever they sent
      marketFunds[profileId] = msg.value;

Second, this DoS can well happen in markets with 0 creationCost, because there is no minimum check for creationCost. https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L366-L382

  function addMarketConfig(
    uint256 liquidity,
    uint256 basePrice,
    uint256 creationCost
  ) public onlyAdmin whenNotPaused returns (uint256) {
    if (liquidity < 100) revert InvalidMarketConfigOption("Min liquidity not met");

    if (basePrice < MINIMUM_BASE_PRICE) revert InvalidMarketConfigOption("Insufficient base price");

    marketConfigs.push(
      MarketConfig({ liquidity: liquidity, basePrice: basePrice, creationCost: creationCost })
    );

    uint256 configIndex = marketConfigs.length - 1;
    emit MarketConfigAdded(configIndex, marketConfigs[configIndex]);
    return configIndex;
  }

Internal pre-conditions

none

External pre-conditions

none

Attack Path

none

Impact

Last vote of a market cannot be sold.

PoC

Mitigation

Rounding should be done based on isBuy.

    cost = positiveCostRatio.mulDiv(
      market.basePrice,
      1e18,
-     isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
+     isBuy ? Math.Rounding.Ceil : Math.Rounding.Floor
    );