Skip to content

Latest commit

 

History

History
89 lines (65 loc) · 3.22 KB

File metadata and controls

89 lines (65 loc) · 3.22 KB

Raspy Black Giraffe

Medium

SolverVault Coordinator Will Cause Losses for Vault Users via Unprotected Rebalancing

Summary

The lack of slippage controls in the rebalance function will cause financial losses for vault users as the coordinator's transactions can be front-run or executed under unfavorable market conditions.

Root Cause

In SolverVault.sol there is no slippage checks

function rebalance(IMarket from, IMarket to, UFixed6 amount) external onlyCoordinator {
        if (!_isRegistered(from) || !_isRegistered(to)) revert SolverVaultNotRegisteredError();
        from.update(address(this), Fixed6Lib.ZERO, Fixed6Lib.from(-1, amount), address(0));
        to.update(address(this), Fixed6Lib.ZERO, Fixed6Lib.from(1, amount), address(0));
    }

Internal Pre-conditions

1- Coordinator needs to call rebalance() to move funds between markets. 2- Vault must hold sufficient collateral in the from market.

External Pre-conditions

1- Market prices for the involved assets must be volatile 2- The rebalance transaction is delayed, allowing market conditions to change unfavorably.

Attack Path

1- Coordinator calls rebalance(fromMarket, toMarket, 1000 USDC) to adjust positions.

2- Attacker detects the transaction in the mempool and front-runs it by: Artificially inflating the price of toMarket via a large buy order. Artificially deflating the price of fromMarket via a large sell order.

3- The coordinator’s rebalance executes at manipulated prices: Vault sells fromMarket at a lower-than-expected price. Vault buys toMarket at a higher-than-expected price.

Impact

Vault users suffer losses proportional to the manipulated price difference during rebalancing. For example:

If the attacker causes a 5% price impact, users lose ~5% of the rebalanced amount. Attacker gains profit by closing their manipulated positions after the vault’s trade.

PoC

// Simplified Foundry test showcasing front-running
function testFrontrunRebalance() public {
    // 1. Setup: Vault holds 1000 USDC in MarketA
    _depositToVault(1000e6); 
    
    // 2. Attacker manipulates MarketB’s price upward
    vm.startPrank(attacker);
    marketB.trade(attacker, 1_000_000e6); // Inflate price
    vm.stopPrank();

    // 3. Coordinator’s rebalance executes at bad prices
    vm.prank(coordinator);
    solverVault.rebalance(marketA, marketB, 1000e6);

    // 4. Verify loss: Vault’s total assets decrease
    assertLt(solverVault.totalAssets(), 1000e6); 
}

Mitigation

Add slippage controls to the rebalance function :

function rebalance(
    IMarket from,
    IMarket to,
    UFixed6 amount,
    UFixed6 minAmountFrom, // Minimum received from `from` market
    UFixed6 minAmountTo,   // Minimum received to `to` market
    uint256 deadline       // Transaction expiry timestamp
) external onlyCoordinator {
    if (block.timestamp > deadline) revert Expired();
    (UFixed6 actualFrom, UFixed6 actualTo) = _executeRebalance(from, to, amount);
    if (actualFrom < minAmountFrom || actualTo < minAmountTo) revert SlippageExceeded();
}