Skip to content

Latest commit

 

History

History
106 lines (75 loc) · 3.62 KB

File metadata and controls

106 lines (75 loc) · 3.62 KB

Main Honeysuckle Tarantula

Medium

Incorrect calculation of funds on buyVotes could DoS withdrawGraduatedMarketFunds

Summary

Consider the withdrawGraduatedMarketFunds function. This function withdraws marketFunds eth from the contract.

However, if there is no such amount of ETH on the contract, the function will not work.

function withdrawGraduatedMarketFunds(uint256 profileId) public whenNotPaused {
    address authorizedAddress = contractAddressManager.getContractAddressForName(
      "GRADUATION_WITHDRAWAL"
    );
    if (msg.sender != authorizedAddress) {
      revert UnauthorizedWithdrawal();
    }
    _checkMarketExists(profileId);
    if (!graduatedMarkets[profileId]) {
      revert MarketNotGraduated();
    }
    if (marketFunds[profileId] == 0) {
      revert InsufficientFunds();
    }

    _sendEth(marketFunds[profileId]);
    emit MarketFundsWithdrawn(profileId, msg.sender, marketFunds[profileId]);
    marketFunds[profileId] = 0;
  }

Now let's look at how market funds are changing.

  1. marketFunds is assigned an initial value in _createMarket.
marketFunds[profileId] = initialLiquidityRequired;
  1. marketFunds is increases when buyVotes is purchased
marketFunds[profileId] += fundsPaid;
  1. marketFunds [decreases](https://github.com/sherlock-audit/2024-11-ethos-network-ii/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L522 when sellVotes are sold.
marketFunds[profileId] -= fundsReceived;

However, in the buyVotes function, marketFunds does not change by the number of eths that are on the contract. Let's look at what fundsPaid consists of.

As you can see from the _calculateBuy function - fundsPaid = priceForShares + protocolFee + donation

while (fundsAvailable >= votePrice) {
      fundsAvailable -= votePrice;
      fundsPaid += votePrice;
      votesBought++;

      market.votes[isPositive ? TRUST : DISTRUST] += 1;
      votePrice = _calcVotePrice(market, isPositive);
 }
fundsPaid += protocolFee + donation;

However, protocolFee is immediately sent to the feeAddress, i.e., it is not stored on the contract. Donation can be instantly output in the withdrawDonations function

Thus, the actual amount of eth stored on the contract is less than that specified in marketFunds, so the withdrawGraduatedMarketFunds function will not work in extreme cases where there is no oversupply of ether.

Root Cause

ETH that is not stored on the contract is recorded in marketFunds and is assumed to be ETH that is stored on the contract.

Protocol Fee, Donation in buyVotes should not go into marketFunds

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

Let initial liquidity be 100. The price per 1 share is 90. User buys 1 share and its msg.value = 100.

So 5 is protocol fee (max 5%), 5 is donation (max 5%), price = 90.

marketFunds = 200. However, the contract will only hold 190, because protocolFee and donation will be withdrawn

Impact

  • DoS of core function
  • incorrect funds calculation

PoC

No response

Mitigation

marketFunds = fundsPaid - protocolFee - donation

Or just don't add these variables to fundsPaid in the ‘calculateBuy` function