Skip to content

Latest commit

 

History

History
104 lines (84 loc) · 4.3 KB

File metadata and controls

104 lines (84 loc) · 4.3 KB

Cool Mango Bobcat

Medium

Base L2 Reorg Vulnerability in LMSR Market State Management

Summary

The ReputationMarket contract's use of the LMSR (Logarithmic Market Scoring Rule) library is vulnerable to state inconsistencies during Base L2 chain reorganizations. While the LMSR library implements secure mathematical calculations, the market state used as input to these calculations can become invalid during reorgs when the sequencer's state is invalidated by L1 batch data.

When Base nodes process conflicting L1 data, they automatically reorg to follow that data, which can result in changed vote counts in markets[profileId].votes. This creates a window where trades could execute based on incorrect vote counts, price calculations could use invalidated state, and vote balances could temporarily reflect incorrect amounts.

This vulnerability affects core market mechanics and user funds through incorrect pricing, as the mathematical LMSR model remains correct but operates on potentially invalid state data.

Proof of Concept

The vulnerability manifests in the core interaction between ReputationMarket and LMSR:

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

// ReputationMarket.sol uses potentially unstable state for LMSR calculations
function _calcVotePrice(Market memory market, bool isPositive) private pure returns (uint256) {
    uint256 odds = LMSR.getOdds(
        market.votes[TRUST],        // Could change during reorg
        market.votes[DISTRUST],     // Could change during reorg
        market.liquidityParameter,
        isPositive
    );
}

The LMSR calculations themselves are mathematically sound:

// LMSR.sol - mathematically correct but depends on stable inputs
function getOdds(
    uint256 yesVotes,
    uint256 noVotes,
    uint256 liquidityParameter,
    bool isYes
) public pure returns (uint256 ratio)

An attacker can exploit this by monitoring market state in unsafe blocks. Upon identifying a potential reorg condition, they submit transactions based on current vote counts. If a reorg occurs, these transactions execute with outdated state assumptions, creating arbitrage opportunities from the price discrepancy.

Recommended mitigation steps

The mitigation strategy requires a fundamental rework of how ReputationMarket interacts with LMSR to ensure stable state inputs.

At the contract level, market state readings should integrate with Base's block confirmation system:

contract ReputationMarket {
    // Add block confirmation requirement
    uint256 public constant SAFE_BLOCK_CONFIRMATIONS = 15; // Can be adjusted based on Base's specs

    // Add safe block check modifier
    modifier onlySafeBlock(uint256 blockNumber) {
        require(
            block.number - blockNumber >= SAFE_BLOCK_CONFIRMATIONS,
            "Block not safe"
        );
        _;
    }

    // Modify trading functions to operate on safe blocks
    function buyVotes(
        uint256 profileId,
        bool isPositive,
        uint256 maxVotesToBuy,
        uint256 minVotesToBuy
    ) public payable whenNotPaused activeMarket(profileId) nonReentrant {
        // Store current block for validation
        uint256 tradeBlock = block.number;
        
        // Queue the trade
        QueuedTrade memory trade = QueuedTrade({
            profileId: profileId,
            isPositive: isPositive,
            amount: maxVotesToBuy,
            minAmount: minVotesToBuy,
            blockNumber: tradeBlock,
            trader: msg.sender,
            value: msg.value
        });
        
        queuedTrades.push(trade);
        emit TradeQueued(profileId, tradeBlock, msg.sender);
    }

    // Add settlement function
    function settleTrade(uint256 tradeIndex) public onlySafeBlock(queuedTrades[tradeIndex].blockNumber) {
        QueuedTrade memory trade = queuedTrades[tradeIndex];
        // Execute trade using confirmed state
        _executeTrade(trade);
        delete queuedTrades[tradeIndex];
    }
}

This fix:

  1. Queues trades instead of executing immediately
  2. Waits for block confirmations before settlement
  3. Uses confirmed state for LMSR calculations
  4. Effective against reorg vulnerabilities

The tradeoff is some delay in trade execution, but it ensures safety of the market operations during reorg events.