<!-- canonical: https://0xsimao.com/findings/yieldoor-symmetric-spacing-sided-inequality -->

# Strategy main ticks are not symmetric when the tick spacing is one due to incorrect isLowerSided inequality

Medium · Sherlock · CLMM · 24th February 2025

Finding M-3 of the Yieldoor competition.

- Protocol: https://audits.sherlock.xyz/contests/791
- Report: /reports/yieldoor
- Codebase: https://github.com/0xsimao/2025-02-yieldoor/tree/b5a0f779dce4236b02665606adb610099451a51a
- Source: https://github.com/sherlock-audit/2025-02-yieldoor-judging/issues/94

---

### Summary

Strategy main position ticks are set according to:
```solidity
function _setMainTicks(int24 tick) internal {
    int24 halfWidth = int24(positionWidth / 2);
    int24 modulo = tick % tickSpacing;
    if (modulo < 0) modulo += tickSpacing; // if tick is negative, modulo is also negative
    bool isLowerSided = modulo < (tickSpacing / 2);

    int24 tickBorder = tick - modulo;
    if (!isLowerSided) tickBorder += tickSpacing;
    mainPosition.tickLower = tickBorder - halfWidth;
    mainPosition.tickUpper = tickBorder + halfWidth;

    emit NewMainTicks(tickBorder - halfWidth, tickBorder + halfWidth);
}
```
As can be seen, when the tick spacing is 1, the tick will be deemed lower sided, which adds a tick spacing (1) to the tick border. So, if the current tick is -1769, the ticks will be -1770 to -1766, which is not symmetric and will miss out on fees.

### Root Cause

In `Strategy:236`, is lower sided [check](https://github.com/sherlock-audit/2025-02-yieldoor/blob/main/yieldoor/src/Strategy.sol#L236) is incorrect.

### Internal Pre-conditions

None.

### External Pre-conditions

None.

### Attack Path

1. Protocol rebalances but main ticks are not symmetric and will miss out on fees when the price moves to the side that has less liquidity allocated.

### Impact

Loss of fees.

### PoC

```solidity
    // in setup
    IUniswapV3Pool pool = IUniswapV3Pool(0x20E068D76f9E90b90604500B84c7e19dCB923e7e);
    IERC20 wbtc = IERC20(0x4200000000000000000000000000000000000006); // token0
    IERC20 usdc = IERC20(0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452); // token1
    address uniRouter = address(0x2626664c2603336E57B271c5C0b26F421741e481);
    vm.createSelectFork(vm.envString("RPC_URL_BASE"), 26874136);

function test_POC_WrongTicks_DueToIsLowerSide() public {
    skip(10 minutes);
    vm.startPrank(rebalancer);
    IStrategy(strategy).rebalance();

    IStrategy.Position memory mainPos = IStrategy(strategy).getMainPosition();
    (, int24 tick,,,,,) = pool.slot0();
    //@audit position is not symmetric, harming long term fees
    assertEq(tick, -1769);
    assertEq(mainPos.tickLower, -1770);
    assertEq(mainPos.tickUpper, -1766);
}
```

### Mitigation

`bool isLowerSided = modulo <= (tickSpacing / 2);`

---

Related findings:

- [Cds amounts to reduce from each chain are incorrect and will lead to the inability to withdraw cds in one of the chains](https://0xsimao.com/findings/autonomint-reduce-each-inability-withdraw): Autonomint
