Skip to content

Latest commit

 

History

History
94 lines (66 loc) · 5.19 KB

File metadata and controls

94 lines (66 loc) · 5.19 KB

Sticky Sand Porpoise

Medium

Initial buyer can manipulate the market for a small cost or even profit in some cases

Summary

Attacker can manipulate the reputation of a profile to be close to neutral for a cost equal to the fees related to buying votes or even for a profit in volatile markets.

Root Cause

LMSR AMM design.

  • Attacker can initially buy large amount of positive and negative votes in 1:1 ratio.
  • When other users buy votes, attacker sells his position in the same vote as the one user has bought.
  • This will keep the ratio of positive to negative 1:1, basically not giving real information about a profile trustworthiness.
  • Attacker is winning on his sell, while normal user incur losses.

LSMR Library: https://github.com/sherlock-audit/2024-12-ethos-update/blob/c3a2b007d0ddfcb476f300f8b766808f0e3e2dfd/ethos/packages/contracts/contracts/utils/LMSR.sol#L42

Internal Pre-conditions

New market is created

External Pre-conditions

N/A

Attack Path

Scenario described in Root Cause section has some nuances regarding the profitability of attacker:

Scenario where users heavily vote only in one direction after initial buy of attacker:

  • Attacker is winning on selling his position of the favorable direction, but he does not win on the votes on the other direction.
  • Market exceptions do not show the real trustworthiness of a profile, making the whole idea of the reputation market useless.
  • If attacker decides that he no longer want to keep the manipulated ratio for the market at 1:1, he can sell all his votes at a cost similar to the cost he has paid to get those votes (will show example in PoC).
  • This scenario is more a grief attack, since attacker does not gain any financial profits and will have to pay the entry/exit fees for his votes.

Scenario where users vote on both directions of a market:

  • Attacker is winning on both of his positions buy selling what different people buy.
  • As in the previous scenario, market exceptions do not show the real trustworthiness of a profile.
  • Instead of just griefing, the attacker makes financial gains buy selling both of his positions.

Impact

Manipulated market expections. In best case scenario, attacker makes profit from this market manipulation. In worst case scenario, attacker need to pay only the fees associated with buying/selling votes.

Another impact which is part of the design of LMSR is that with higher liquidity, the cost for buying votes increases and votes affect the market less making it even harder for a market to give the right expectation of reputation of a profile.

PoC

Two tests show the described best and worst case scenario for the attack. You can add them in lmsr.test.ts file and run with npm run test:contracts.

The main idea of the test is to show the cost for certain trades using the LMSR AMM, cost is essentially what users pay/get from buying/selling votes (scaled with basePrace parameter).

Examples show only the final outcome of trades since it is the same to buy 100 x 1 vote and but 1 x 100 votes.

    it.only('best case scenario for attacker', async () => {
      // Initial votes when market is created
      const votes = 1;

      // Cost for initial buy of positive and negative in 1:1 ratio
      const initialCost = await lmsr.getCost(votes, votes, votes + 100, votes + 100, LIQUIDITY_PARAMETER);

      // Normal users has bought 100 positive votes, attacker sells his 100 positive votes
      const sellPositiveCost = await lmsr.getCost(votes + 200, votes + 100, votes + 100, votes + 100, LIQUIDITY_PARAMETER);

      // Normal users has bought 100 negative votes, attacker sells his 100 negative votes
      const sellNegativeCost = await lmsr.getCost(votes+ 100, votes + 200, votes + 100, votes + 100, LIQUIDITY_PARAMETER);

      expect(initialCost).to.be.lt(-(sellPositiveCost + sellNegativeCost));
      console.log("Initial cost: %s", initialCost);
      console.log("Selling cost: %s", -(sellPositiveCost + sellNegativeCost));
    });

    it.only('worst case scenario for attacker', async () => {
      // Initial votes when market is created
      const votes = 1;

      // Cost for initial buy of positive and negative in 1:1 ratio
      const initialCost = await lmsr.getCost(votes, votes, votes + 100, votes + 100, LIQUIDITY_PARAMETER);

      // Normal users has bought 100 positive votes, attacker sells his 100 positive votes
      const sellPositiveCost = await lmsr.getCost(votes + 200, votes + 100, votes + 100, votes + 100, LIQUIDITY_PARAMETER);

      // Attacker decides to sell his negative votes and exit the market
      const sellNegativeCost = await lmsr.getCost(votes + 100, votes + 100, votes + 100, votes, LIQUIDITY_PARAMETER);

      expect(initialCost).to.be.closeTo(-(sellPositiveCost + sellNegativeCost), 10000);
      console.log("Initial cost: %s", initialCost);
      console.log("Selling cost: %s", -(sellPositiveCost + sellNegativeCost));
    });

Mitigation

I do not think LMSR is well suited for a perpetuals prediction market because of this issue with single user initially buying large amount of votes in both directions. This will make the market impossible to show a real users voting towards positive or negative regarding a profile reputation + attacker can make profit from this.