Skip to content

Latest commit

 

History

History
122 lines (91 loc) · 4.18 KB

File metadata and controls

122 lines (91 loc) · 4.18 KB

Tangy Tortilla Fox

Medium

_calcCost rounds in favor of the user causing insolvency

Summary

_calcCost is used to get the price for each sell/buy. Where that function is made to round up or down, depending on if the user buys TRUST or DISTRUST.

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

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

However that rounding is wrong and the function should round depending on if the user buys or sells votes. As currently on every TRUST sell/buy would round down and with every DISTRUST buy/sell it would round up, possibly with time causing the contracts to be insolvent, more on it bellow.

In order for insolvency to be caused there must be a pool with 0 creationCost. That is not only possible, but likely as admins can create pools with whatever creation cost they want, which intuitively suggest that they would create them with 0 creationCost, as what's the point in spending the system funds on markets when they can utilize them in another way.

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

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

Inside this market, if insolvency is achieved the funds would be taken from other markets, causing another market to become insolvent. And if that market also has 0 creationCost (many market will be made this way, especially if admins need to make some for mock profiles) these funds would be taken from the buy/sell amount. This means that if the users from the second market decide to sell their votes, all but one last user would be able to exit it.

Root Cause

_calcCost rounds based on if the user buys TRUST or DISTRUST

  function _calcCost(
    Market memory market,
    bool isPositive,
    bool isBuy,
    uint256 amount
  ) private pure returns (uint256 cost) {
    uint256[] memory voteDelta = new uint256[](2);

    if (isBuy) {
      if (isPositive) {
        voteDelta[0] = market.votes[TRUST] + amount;
        voteDelta[1] = market.votes[DISTRUST];
      } else {
        voteDelta[0] = market.votes[TRUST];
        voteDelta[1] = market.votes[DISTRUST] + amount;
      }
    } else {
      if (isPositive) {
        voteDelta[0] = market.votes[TRUST] - amount;
        voteDelta[1] = market.votes[DISTRUST];
      } else {
        voteDelta[0] = market.votes[TRUST];
        voteDelta[1] = market.votes[DISTRUST] - amount;
      }
    }

    // cost(new) - cost(old)
    int256 costRatio = LMSR.getCost(
      market.votes[TRUST],
      market.votes[DISTRUST],
      voteDelta[0],
      voteDelta[1],
      market.liquidityParameter
    );

    uint256 positiveCostRatio = costRatio > 0 ? uint256(costRatio) : uint256(costRatio * -1);


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

Internal Pre-conditions

  1. An admin creates a market with 0 creationFee

External Pre-conditions

  1. Users to use the admin created pool

Attack Path

  1. Admin creates a few markets for big entities/personas with 0 creation cost
  2. Users use these markets
  3. After some time every users sells all their votes from a certain market
  4. Due to _calcCost rounding he function would round a few wei down
  5. The other expensive market becomes insolvent, as the few wei comes from it's own buy/sell funds
  6. It's last user is unable to withdraw

Impact

Pools may become insolvent, or the least take funds from other pools, breaking the core invariant

PoC

No response

Mitigation

Fix is extremely simple, just change from isPositive to isBuy in order to rounds up on buys and down on sells.

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