Skip to content

fix: correct constant-product pricing math for selling in PredictionMarketAMM - #3

Open
mrnetwork0001 wants to merge 1 commit into
circlefin:masterfrom
mrnetwork0001:fix/amm-pricing
Open

fix: correct constant-product pricing math for selling in PredictionMarketAMM#3
mrnetwork0001 wants to merge 1 commit into
circlefin:masterfrom
mrnetwork0001:fix/amm-pricing

Conversation

@mrnetwork0001

Copy link
Copy Markdown

Description

This PR fixes a critical mathematical vulnerability in the PredictionMarketAMM contract.

The Problem

In the original implementation, the sellYes and sellNo functions used the standard constant-product formula to swap the sold asset into the pool to calculate the output shares ($noOut$ or $yesOut$), and then immediately redeemed those output shares for the underlying collateral (USDC).

This redemption process burns the shares, which subtracts $noOut$/$yesOut$ from both reserves (YES and NO). Because subtracting the same quantity from both sides of a product does not preserve the product ($R_Y \cdot R_N$), the constant-product invariant ($k$) decreased on every sell transaction. This created a severe arbitrage loop where users could buy shares and immediately sell them back to receive more USDC than they paid, draining the AMM's liquidity.

The Solution

To preserve the constant-product invariant $k$ (and allow it to increase when fees are active), we must solve for the exact USDC payout $x$ that satisfies the invariant after the swap and redemption:
$$(R_Y + Y_{eff} - x) \cdot (R_N - x) = R_Y \cdot R_N$$

Expanding this yields the quadratic equation:
$$x^2 - (R_Y + R_N + Y_{eff}) \cdot x + Y_{eff} \cdot R_N = 0$$

Solving for the smaller root gives the correct payout amount:
$$x = \frac{b - \sqrt{b^2 - 4c}}{2}$$
where:

  • $b = R_Y + R_N + Y_{eff}$
  • $c = Y_{eff} \cdot R_N$

This PR:

  1. Imports OpenZeppelin's Math.sol and uses Math.sqrt to solve the quadratic equation on-chain.
  2. Updates sellYes, sellNo, calcSellYes, and calcSellNo to use this pricing logic.
  3. Fixes the pool drain vulnerability and ensures that the constant-product invariant is preserved (or increases when fees are active).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant