You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The choice to apply the LMSR formula on a per-transaction “spot price” basis rather than enforcing the integral cost for each user's net position will cause an arbitrage exploit for the market participants as an attacker can buy and sell multiple times to net out a guaranteed profit.
Root Cause
The choice to track each user’s shares separately and let them trade at the “spot” LMSR price is a mistake as it breaks LMSR’s no-arbitrage assumption.
By design, LMSR needs to charge the integral cost from the old market state to the new one for every position change, ensuring no free profit. When the code treats each trade independently at the current spot price, it allows certain multi-step trades to extract risk-free profit.
in another terms:
In a standard single-pool LMSR design, no-arbitrage is guaranteed only if all traders pay or receive the integral (cumulative) cost difference when they change their positions. This means that the protocol calculates the cost of going from one global state (𝑞 oldYes, 𝑞 oldNo) to a new global state (𝑞 newYes, 𝑞 newNo) and charges or rebates that exact difference for the entire trade.
However, in this implementation:
Spot Pricing Instead of Integral Cost
The protocol appears to use a “spot” LMSR price for each incremental buy or sell transaction rather than integrating over the user’s full position change in a single step. Essentially, if a user wants to buy n shares, the cost is computed as if they’re buying each share one at a time at the spot price and summing those marginal costs—rather than using the difference cost(𝑞 old + 𝑛) − cost(𝑞 old).
Individual Balances vs. Single Collateral Pool
Each user holds personal “yes” or “no” vote balances. When they buy or sell, the protocol re-prices the entire market based on the new total “yes” and “no” votes—but does not enforce a single, unified cost basis for each user’s net position. This lets a user (or multiple addresses controlled by the same attacker) shuffle partial buys/sells in a way that manipulates the spot price back and forth, capturing a risk-free profit.
Breaking LMSR’s No-Arbitrage Assumption
LMSR no-arbitrage relies on the idea that any partial shift in the market state should cost or refund the entire difference in cost function values. By allowing repeated incremental buys/sells at spot prices (and ignoring the integral cost each user accrued), the system inadvertently opens an arbitrage route. The attacker can:
First, buy negative votes to push positive votes cheaper,
Then, buy (or sell) positive votes at a favorable rate,
Continue toggling the market’s “yes/no” ratio until eventually selling out at a higher price than their overall cost.
As a result, the attacker ends up with more ETH (or whatever currency is used) than they started with, extracting it from the collateral or from other users in the pool—thus violating the no-arbitrage principle that LMSR is supposed to guarantee.
There must be at least two users interacting with the same market.
External Pre-conditions
No external conditions need to shift drastically; this exploit relies purely on the contract’s internal pricing logic.
Gas costs remain low enough that repeated trades are feasible for the attacker. which is true in base layer 2.
Attack Path
Attacker (User B) buys a small number of “no” shares, nudging the global price in favor of “yes.”
Attacker immediately buys a large amount of “yes” shares at a relatively low “spot” price.
The attacker (or another user) then reverses the shift (e.g., buys “no” or sells “yes”) so the global price changes again, making the attacker’s “yes” shares more valuable.
Attacker sells the large “yes” position at the new higher price, pocketing more than they originally paid due to the incorrect per-transaction pricing.
Impact
The protocol (and possibly other honest traders) suffer a loss equivalent to the difference between the attacker’s buy and sell prices that LMSR should have recaptured as integral cost. The attacker gains that difference as risk-free profit.
PoC
add this code to one of the test like test/reputationMarket/rep.market.test.ts
this is just a numerical example of how shares can be bought. other amount of shares are acceptable.
the attacker balance increased even though attacker paid for fees.
Mitigation
Enforce the full integral cost for each user’s net position changes.
Store users’ total positions and charge them according to the difference in the LMSR cost function from their old position to their new position.
Avoid using LMSR spot pricing for partial buy/sell orders in isolation.
The text was updated successfully, but these errors were encountered:
Radiant Peach Rook
High
Arbitrage Vulnerability in LMSR-Based Market
Summary
The choice to apply the LMSR formula on a per-transaction “spot price” basis rather than enforcing the integral cost for each user's net position will cause an arbitrage exploit for the market participants as an attacker can buy and sell multiple times to net out a guaranteed profit.
Root Cause
The choice to track each user’s shares separately and let them trade at the “spot” LMSR price is a mistake as it breaks LMSR’s no-arbitrage assumption.
By design, LMSR needs to charge the integral cost from the old market state to the new one for every position change, ensuring no free profit. When the code treats each trade independently at the current spot price, it allows certain multi-step trades to extract risk-free profit.
in another terms:
In a standard single-pool LMSR design, no-arbitrage is guaranteed only if all traders pay or receive the integral (cumulative) cost difference when they change their positions. This means that the protocol calculates the cost of going from one global state (𝑞 oldYes, 𝑞 oldNo) to a new global state (𝑞 newYes, 𝑞 newNo) and charges or rebates that exact difference for the entire trade.
However, in this implementation:
Spot Pricing Instead of Integral Cost
The protocol appears to use a “spot” LMSR price for each incremental buy or sell transaction rather than integrating over the user’s full position change in a single step. Essentially, if a user wants to buy n shares, the cost is computed as if they’re buying each share one at a time at the spot price and summing those marginal costs—rather than using the difference cost(𝑞 old + 𝑛) − cost(𝑞 old).
Individual Balances vs. Single Collateral Pool
Each user holds personal “yes” or “no” vote balances. When they buy or sell, the protocol re-prices the entire market based on the new total “yes” and “no” votes—but does not enforce a single, unified cost basis for each user’s net position. This lets a user (or multiple addresses controlled by the same attacker) shuffle partial buys/sells in a way that manipulates the spot price back and forth, capturing a risk-free profit.
Breaking LMSR’s No-Arbitrage Assumption
LMSR no-arbitrage relies on the idea that any partial shift in the market state should cost or refund the entire difference in cost function values. By allowing repeated incremental buys/sells at spot prices (and ignoring the integral cost each user accrued), the system inadvertently opens an arbitrage route. The attacker can:
As a result, the attacker ends up with more ETH (or whatever currency is used) than they started with, extracting it from the collateral or from other users in the pool—thus violating the no-arbitrage principle that LMSR is supposed to guarantee.
https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/utils/LMSR.sol?plain=1#L93-L103
Internal Pre-conditions
External Pre-conditions
Attack Path
Impact
The protocol (and possibly other honest traders) suffer a loss equivalent to the difference between the attacker’s buy and sell prices that LMSR should have recaptured as integral cost. The attacker gains that difference as risk-free profit.
PoC
add this code to one of the test like test/reputationMarket/rep.market.test.ts
this is just a numerical example of how shares can be bought. other amount of shares are acceptable.
output:
the attacker balance increased even though attacker paid for fees.
Mitigation
Enforce the full integral cost for each user’s net position changes.
Store users’ total positions and charge them according to the difference in the LMSR cost function from their old position to their new position.
Avoid using LMSR spot pricing for partial buy/sell orders in isolation.
The text was updated successfully, but these errors were encountered: